debian_packaging/deb/
reader.rs1use {
8 crate::{
9 binary_package_control::BinaryPackageControlFile,
10 control::ControlParagraphReader,
11 error::{DebianError, Result},
12 },
13 std::{
14 io::{Cursor, Read},
15 ops::{Deref, DerefMut},
16 },
17};
18
19fn reader_from_filename(extension: &str, data: std::io::Cursor<Vec<u8>>) -> Result<Box<dyn Read>> {
20 match extension {
21 "" => Ok(Box::new(data)),
22 ".gz" => Ok(Box::new(libflate::gzip::Decoder::new(data)?)),
23 ".xz" => Ok(Box::new(xz2::read::XzDecoder::new(data))),
24 ".zst" => Ok(Box::new(zstd::Decoder::new(data)?)),
25 _ => Err(DebianError::DebUnknownCompression(extension.to_string())),
26 }
27}
28
29fn reader_from_filename_async(
30 extension: &str,
31 data: futures::io::Cursor<Vec<u8>>,
32) -> Result<Box<dyn futures::AsyncRead + Unpin>> {
33 match extension {
34 "" => Ok(Box::new(data)),
35 ".gz" => Ok(Box::new(
36 async_compression::futures::bufread::GzipDecoder::new(data),
37 )),
38 ".xz" => Ok(Box::new(
39 async_compression::futures::bufread::XzDecoder::new(data),
40 )),
41 ".zst" => Ok(Box::new(
42 async_compression::futures::bufread::ZstdDecoder::new(data),
43 )),
44 _ => Err(DebianError::DebUnknownCompression(extension.to_string())),
45 }
46}
47
48pub struct BinaryPackageReader<R: Read> {
56 archive: ar::Archive<R>,
57}
58
59impl<R: Read> BinaryPackageReader<R> {
60 pub fn new(reader: R) -> Result<Self> {
62 Ok(Self {
63 archive: ar::Archive::new(reader),
64 })
65 }
66
67 pub fn next_entry(&mut self) -> Option<Result<BinaryPackageEntry>> {
71 if let Some(entry) = self.archive.next_entry() {
72 match entry {
73 Ok(mut entry) => {
74 let filename = String::from_utf8_lossy(entry.header().identifier()).to_string();
77
78 let mut data = vec![];
79 match entry.read_to_end(&mut data) {
80 Ok(_) => {}
81 Err(e) => {
82 return Some(Err(e.into()));
83 }
84 }
85
86 if filename == "debian-binary" {
87 Some(Ok(BinaryPackageEntry::DebianBinary(std::io::Cursor::new(
88 data,
89 ))))
90 } else if let Some(tail) = filename.strip_prefix("control.tar") {
91 match reader_from_filename(tail, std::io::Cursor::new(data)) {
92 Ok(res) => Some(Ok(BinaryPackageEntry::Control(ControlTarReader {
93 archive: tar::Archive::new(res),
94 }))),
95 Err(e) => Some(Err(e)),
96 }
97 } else if let Some(tail) = filename.strip_prefix("data.tar") {
98 match reader_from_filename_async(tail, futures::io::Cursor::new(data)) {
99 Ok(res) => Some(Ok(BinaryPackageEntry::Data(DataTarReader {
100 archive: async_tar::Archive::new(res),
101 }))),
102 Err(e) => Some(Err(e)),
103 }
104 } else {
105 Some(Err(DebianError::DebUnknownBinaryPackageEntry(
106 filename.to_string(),
107 )))
108 }
109 }
110 Err(e) => Some(Err(e.into())),
111 }
112 } else {
113 None
114 }
115 }
116}
117
118pub enum BinaryPackageEntry {
120 DebianBinary(std::io::Cursor<Vec<u8>>),
122 Control(ControlTarReader),
124 Data(DataTarReader),
126}
127
128pub struct ControlTarReader {
130 archive: tar::Archive<Box<dyn Read>>,
131}
132
133impl Deref for ControlTarReader {
134 type Target = tar::Archive<Box<dyn Read>>;
135
136 fn deref(&self) -> &Self::Target {
137 &self.archive
138 }
139}
140
141impl DerefMut for ControlTarReader {
142 fn deref_mut(&mut self) -> &mut Self::Target {
143 &mut self.archive
144 }
145}
146
147impl ControlTarReader {
148 pub fn entries(&mut self) -> Result<ControlTarEntries<'_>> {
154 let entries = self.archive.entries()?;
155
156 Ok(ControlTarEntries { entries })
157 }
158}
159
160pub struct ControlTarEntries<'a> {
165 entries: tar::Entries<'a, Box<dyn Read>>,
166}
167
168impl<'a> Iterator for ControlTarEntries<'a> {
169 type Item = Result<ControlTarEntry<'a>>;
170
171 fn next(&mut self) -> Option<Self::Item> {
172 match self.entries.next() {
173 Some(Ok(entry)) => Some(Ok(ControlTarEntry { inner: entry })),
174 Some(Err(e)) => Some(Err(e.into())),
175 None => None,
176 }
177 }
178}
179
180pub struct ControlTarEntry<'a> {
185 inner: tar::Entry<'a, Box<dyn Read>>,
186}
187
188impl<'a> Deref for ControlTarEntry<'a> {
189 type Target = tar::Entry<'a, Box<dyn Read>>;
190
191 fn deref(&self) -> &Self::Target {
192 &self.inner
193 }
194}
195
196impl<'a> DerefMut for ControlTarEntry<'a> {
197 fn deref_mut(&mut self) -> &mut Self::Target {
198 &mut self.inner
199 }
200}
201
202impl<'a> ControlTarEntry<'a> {
203 pub fn to_control_file(&mut self) -> Result<(&'_ tar::Header, ControlTarFile)> {
207 let path_bytes = self.inner.path_bytes().to_vec();
208 let path = String::from_utf8_lossy(&path_bytes);
209
210 let mut data = vec![];
211 self.inner.read_to_end(&mut data)?;
212
213 match path.trim_start_matches("./") {
214 "control" => {
215 let mut reader = ControlParagraphReader::new(Cursor::new(data));
216 let paragraph = reader.next().ok_or(DebianError::ControlFileNoParagraph)??;
217 let control = BinaryPackageControlFile::from(paragraph);
218
219 Ok((self.inner.header(), ControlTarFile::Control(control)))
220 }
221 "conffiles" => Ok((self.inner.header(), ControlTarFile::Conffiles(data))),
222 "triggers" => Ok((self.inner.header(), ControlTarFile::Triggers(data))),
223 "shlibs" => Ok((self.inner.header(), ControlTarFile::Shlibs(data))),
224 "symbols" => Ok((self.inner.header(), ControlTarFile::Symbols(data))),
225 "preinst" => Ok((self.inner.header(), ControlTarFile::Preinst(data))),
226 "postinst" => Ok((self.inner.header(), ControlTarFile::Postinst(data))),
227 "prerm" => Ok((self.inner.header(), ControlTarFile::Prerm(data))),
228 "postrm" => Ok((self.inner.header(), ControlTarFile::Postrm(data))),
229 _ => Ok((self.inner.header(), ControlTarFile::Other(path_bytes, data))),
230 }
231 }
232}
233
234pub enum ControlTarFile {
238 Control(BinaryPackageControlFile<'static>),
240
241 Conffiles(Vec<u8>),
243
244 Triggers(Vec<u8>),
246
247 Shlibs(Vec<u8>),
249
250 Symbols(Vec<u8>),
252
253 Preinst(Vec<u8>),
255
256 Postinst(Vec<u8>),
258
259 Prerm(Vec<u8>),
261
262 Postrm(Vec<u8>),
264
265 Other(Vec<u8>, Vec<u8>),
269}
270
271pub struct DataTarReader {
273 archive: async_tar::Archive<Box<dyn futures::io::AsyncRead + Unpin>>,
274}
275
276impl Deref for DataTarReader {
277 type Target = async_tar::Archive<Box<dyn futures::io::AsyncRead + Unpin>>;
278
279 fn deref(&self) -> &Self::Target {
280 &self.archive
281 }
282}
283
284impl DerefMut for DataTarReader {
285 fn deref_mut(&mut self) -> &mut Self::Target {
286 &mut self.archive
287 }
288}
289
290impl DataTarReader {
291 pub fn into_inner(self) -> async_tar::Archive<Box<dyn futures::io::AsyncRead + Unpin>> {
293 self.archive
294 }
295}
296
297pub fn resolve_control_file(reader: impl Read) -> Result<BinaryPackageControlFile<'static>> {
299 let mut reader = BinaryPackageReader::new(reader)?;
300
301 while let Some(entry) = reader.next_entry() {
302 if let BinaryPackageEntry::Control(mut control) = entry? {
303 for entry in control.entries()? {
304 if let ControlTarFile::Control(control) = entry?.to_control_file()?.1 {
305 return Ok(control);
306 }
307 }
308 }
309 }
310
311 Err(DebianError::ControlFileNotFound)
312}