debpkg/
lib.rs

1//! # debpkg
2//!
3//! A library to parse binary debian packages
4//!
5//! This library provides utilties to parse [binary debian
6//! packages](https://www.debian.org/doc/manuals/debian-faq/ch-pkg_basics.en.html#s-deb-format)
7//! abstracted over a reader. This API provides a streaming interface to avoid
8//! loading the entire debian package into RAM.
9//!
10//! This library only parses binary debian packages. It does not attempt to
11//! write binary debian packages.
12//!
13//! # Supported Debian Package Versions
14//!
15//! This package only supports version 2.0 of debian packages. Older versions
16//! are not currently supported.
17//!
18//! # Examples
19//!
20//! Parsing a debian package
21//!
22//! ```no_run
23//! let file = std::fs::File::open("test.deb").unwrap();
24//! let mut pkg = debpkg::DebPkg::parse(file).unwrap();
25//! let mut control_tar = pkg.control().unwrap();
26//! let control = debpkg::Control::extract(control_tar).unwrap();
27//! println!("Package Name: {}", control.name());
28//! println!("Package Version: {}", control.version());
29//! let arch = control.get("Architecture").unwrap();
30//! println!("Package Architecture: {}", arch);
31//!
32//! let mut data = pkg.data().unwrap();
33//! let dir = tempfile::TempDir::new().unwrap();
34//! data.unpack(dir).unwrap();
35//! ```
36
37use std::io::Read;
38
39mod error;
40pub use error::Error;
41
42mod control;
43pub use control::Control;
44
45mod debian_binary;
46use debian_binary::{parse_debian_binary_contents, DebianBinaryVersion};
47
48type Result<T> = std::result::Result<T, Error>;
49
50enum ReadState {
51    Opened,
52    ControlRead,
53    DataRead,
54}
55
56/// A debian package represented by the control data the archive holding all the
57/// information
58pub struct DebPkg<R: Read> {
59    /// How far we've read through the debian package. This is especially
60    /// important when R only implements Read and not Seek since we will not be
61    /// able to Read backwards.
62    state: ReadState,
63
64    /// The major and minor fomat version of the debian package
65    format_version: DebianBinaryVersion,
66
67    /// The ar archive in which the debian package is contained
68    archive: ar::Archive<R>,
69}
70
71fn validate_debian_binary<'a, R: 'a + Read>(
72    entry: &mut ar::Entry<'a, R>,
73) -> Result<DebianBinaryVersion> {
74    let identifier = "debian-binary";
75
76    if entry.header().identifier() == identifier.as_bytes() {
77        parse_debian_binary_contents(entry)
78    } else {
79        Err(Error::MissingDebianBinary)
80    }
81}
82
83impl<'a, R: 'a + Read> DebPkg<R> {
84    /// Parses a debian package out of reader
85    ///
86    /// # Arguments
87    ///
88    /// * `reader` - A type which implements `std::io::Read` and `std::io::Seek`
89    ///              and is formatted as an ar archive
90    ///
91    /// # Example
92    ///
93    /// ```no_run
94    /// use debpkg::DebPkg;
95    /// let file = std::fs::File::open("test.deb").unwrap();
96    /// let pkg = DebPkg::parse(file).unwrap();
97    /// ```
98    pub fn parse(reader: R) -> Result<DebPkg<R>> {
99        let mut archive = ar::Archive::new(reader);
100        let mut debian_binary_entry = match archive.next_entry() {
101            Some(Ok(entry)) => entry,
102            Some(Err(err)) => return Err(Error::Io(err)),
103            None => return Err(Error::MissingDebianBinary),
104        };
105        let format_version = validate_debian_binary(&mut debian_binary_entry)?;
106        drop(debian_binary_entry);
107
108        Ok(DebPkg {
109            state: ReadState::Opened,
110            format_version,
111            archive,
112        })
113    }
114
115    /// Returns the format version of the binary debian package
116    pub fn format_version(&self) -> (u32, u32) {
117        (self.format_version.major, self.format_version.minor)
118    }
119
120    /// Returns the control tar
121    ///
122    /// # Arguments
123    ///
124    /// * `self` - A `DebPkg` created by a call to `DebPkg::parse`
125    ///
126    /// # Example
127    ///
128    /// ```no_run
129    /// use debpkg::DebPkg;
130    /// let file = std::fs::File::open("test.deb").unwrap();
131    /// let mut pkg = DebPkg::parse(file).unwrap();
132    /// let mut control_tar = pkg.control().unwrap();
133    /// for file in control_tar.entries().unwrap() {
134    ///     println!("{}", file.unwrap().path().unwrap().display());
135    /// }
136    /// ```
137    ///
138    pub fn control(&'a mut self) -> Result<tar::Archive<Box<dyn Read + 'a>>> {
139        match self.state {
140            ReadState::Opened => {
141                let entry = match self.archive.next_entry() {
142                    Some(entry) => entry?,
143                    None => return Err(Error::MissingControlArchive),
144                };
145
146                self.state = ReadState::ControlRead;
147                get_tar_from_entry(entry)
148            }
149            ReadState::ControlRead | ReadState::DataRead => Err(Error::ControlAlreadyRead),
150        }
151    }
152
153    /// Returns the data tar
154    ///
155    /// Must only be called
156    ///
157    /// # Arguments
158    ///
159    /// * `self` - A `DebPkg` created by a call to `DebPkg::parse`
160    ///
161    /// # Example
162    ///
163    /// ```no_run
164    /// use debpkg::DebPkg;
165    /// let file = std::fs::File::open("test.deb").unwrap();
166    /// let mut pkg = DebPkg::parse(file).unwrap();
167    /// let mut data_tar = pkg.data().unwrap();
168    /// for file in data_tar.entries().unwrap() {
169    ///     println!("{}", file.unwrap().path().unwrap().display());
170    /// }
171    /// ```
172    ///
173    pub fn data(&'a mut self) -> Result<tar::Archive<Box<dyn Read + 'a>>> {
174        match self.control() {
175            Ok(_) => (),
176            Err(Error::ControlAlreadyRead) => (),
177            Err(e) => return Err(e),
178        };
179
180        match self.state {
181            ReadState::Opened => unreachable!(),
182            ReadState::ControlRead => {
183                let entry = match self.archive.next_entry() {
184                    Some(entry) => entry?,
185                    None => return Err(Error::MissingDataArchive),
186                };
187
188                self.state = ReadState::DataRead;
189                get_tar_from_entry(entry)
190            }
191            ReadState::DataRead => Err(Error::DataAlreadyRead),
192        }
193    }
194}
195
196fn get_tar_from_entry<'a, R: 'a + Read>(
197    entry: ar::Entry<'a, R>,
198) -> Result<tar::Archive<Box<dyn Read + 'a>>> {
199    let mut reader = entry.take(1024);
200    let mut first_1kb = vec![];
201    reader.read_to_end(&mut first_1kb)?;
202
203    let is_tar = infer::archive::is_tar(&first_1kb);
204    let is_gz = infer::archive::is_gz(&first_1kb);
205    let is_xz = infer::archive::is_xz(&first_1kb);
206    let is_bz2 = infer::archive::is_bz2(&first_1kb);
207    let is_zst = infer::archive::is_zst(&first_1kb);
208
209    let entry = std::io::Cursor::new(first_1kb).chain(reader.into_inner());
210
211    if is_tar {
212        let entry: Box<dyn Read> = Box::new(entry);
213        Ok(tar::Archive::new(entry))
214    } else if is_gz {
215        let gz: Box<dyn Read> = Box::new(flate2::read::GzDecoder::new(entry));
216        Ok(tar::Archive::new(gz))
217    } else if is_xz {
218        let xz: Box<dyn Read> = Box::new(xz2::read::XzDecoder::new_multi_decoder(entry));
219        Ok(tar::Archive::new(xz))
220    } else if is_bz2 {
221        let bz2: Box<dyn Read> = Box::new(bzip2::read::BzDecoder::new(entry));
222        Ok(tar::Archive::new(bz2))
223    } else if is_zst {
224        let zstd: Box<dyn Read> = Box::new(zstd::stream::read::Decoder::new(entry)?);
225        Ok(tar::Archive::new(zstd))
226    } else {
227        Err(Error::UnknownEntryFormat)
228    }
229}