cpio_archive/
newc.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//! New ASCII format support.
6
7use {
8    crate::{CpioHeader, CpioReader, CpioResult, Error},
9    std::{
10        ffi::CStr,
11        io::{Read, Take},
12    },
13};
14
15pub const MAGIC: &[u8] = b"070701";
16
17fn u32_from_hex(data: &[u8]) -> CpioResult<u32> {
18    let s = std::str::from_utf8(data).map_err(|_| Error::BadHeaderString)?;
19    u32::from_str_radix(s, 16).map_err(|_| Error::BadHeaderHex(s.to_string()))
20}
21
22fn u64_from_hex(data: &[u8]) -> CpioResult<u64> {
23    let s = std::str::from_utf8(data).map_err(|_| Error::BadHeaderString)?;
24    u64::from_str_radix(s, 16).map_err(|_| Error::BadHeaderHex(s.to_string()))
25}
26
27fn read_hex_u32(reader: &mut impl Read, count: usize) -> CpioResult<u32> {
28    let mut buffer = vec![0u8; count];
29    reader.read_exact(&mut buffer)?;
30    u32_from_hex(&buffer)
31}
32
33fn read_hex_u64(reader: &mut impl Read, count: usize) -> CpioResult<u64> {
34    let mut buffer = vec![0u8; count];
35    reader.read_exact(&mut buffer)?;
36    u64_from_hex(&buffer)
37}
38
39#[derive(Clone, Debug)]
40pub struct NewcHeader {
41    pub inode: u32,
42    pub mode: u32,
43    pub uid: u32,
44    pub gid: u32,
45    pub nlink: u32,
46    pub mtime: u32,
47    pub file_size: u64,
48    pub dev_major: u32,
49    pub dev_minor: u32,
50    pub rdev_major: u32,
51    pub rdev_minor: u32,
52    pub checksum: u32,
53    pub name: String,
54}
55
56impl NewcHeader {
57    pub fn from_reader(reader: &mut impl Read) -> CpioResult<Self> {
58        let inode = read_hex_u32(reader, 8)?;
59        let mode = read_hex_u32(reader, 8)?;
60        let uid = read_hex_u32(reader, 8)?;
61        let gid = read_hex_u32(reader, 8)?;
62        let nlink = read_hex_u32(reader, 8)?;
63        let mtime = read_hex_u32(reader, 8)?;
64        let file_size = read_hex_u64(reader, 8)?;
65        let dev_major = read_hex_u32(reader, 8)?;
66        let dev_minor = read_hex_u32(reader, 8)?;
67        let rdev_major = read_hex_u32(reader, 8)?;
68        let rdev_minor = read_hex_u32(reader, 8)?;
69        let name_length = read_hex_u32(reader, 8)?;
70        let checksum = read_hex_u32(reader, 8)?;
71
72        let mut name_data = vec![0u8; name_length as usize];
73        reader.read_exact(&mut name_data)?;
74
75        let name = CStr::from_bytes_with_nul(&name_data)
76            .map_err(|_| Error::FilenameDecode)?
77            .to_string_lossy()
78            .to_string();
79
80        // Pad to 4 byte boundary.
81        // MAGIC: 6 bytes = 4 + 2 bytes
82        // name: maybe out of align
83        // all other fields are aligned
84        let mut pad = [0u8; 4];
85        let pad_len = name_data.len().wrapping_add(2).wrapping_neg() % 4;
86        reader.read_exact(&mut pad[..pad_len])?;
87
88        Ok(Self {
89            inode,
90            mode,
91            uid,
92            gid,
93            nlink,
94            mtime,
95            file_size,
96            dev_major,
97            dev_minor,
98            rdev_major,
99            rdev_minor,
100            checksum,
101            name,
102        })
103    }
104}
105
106impl CpioHeader for NewcHeader {
107    fn device(&self) -> u32 {
108        todo!()
109    }
110
111    fn inode(&self) -> u32 {
112        self.inode
113    }
114
115    fn mode(&self) -> u32 {
116        self.mode
117    }
118
119    fn uid(&self) -> u32 {
120        self.uid
121    }
122
123    fn gid(&self) -> u32 {
124        self.gid
125    }
126
127    fn nlink(&self) -> u32 {
128        self.nlink
129    }
130
131    fn rdev(&self) -> u32 {
132        todo!()
133    }
134
135    fn mtime(&self) -> u32 {
136        self.mtime
137    }
138
139    fn file_size(&self) -> u64 {
140        self.file_size
141    }
142
143    fn name(&self) -> &str {
144        &self.name
145    }
146}
147
148/// A cpio archive reader for *New ASCII format* archives.
149pub struct NewcReader<T: Read + Sized> {
150    archive_reader: Option<T>,
151    entry_reader: Option<Take<T>>,
152    entry_data_pad: usize,
153    seen_trailer: bool,
154}
155
156impl<T: Read + Sized> CpioReader<T> for NewcReader<T> {
157    fn new(reader: T) -> Self {
158        Self {
159            archive_reader: Some(reader),
160            entry_reader: None,
161            entry_data_pad: 0,
162            seen_trailer: false,
163        }
164    }
165
166    fn read_next(&mut self) -> CpioResult<Option<Box<dyn CpioHeader>>> {
167        self.finish()?;
168
169        if let Some(mut reader) = self.archive_reader.take() {
170            let mut magic = [0u8; 6];
171
172            match reader.read_exact(&mut magic) {
173                Ok(_) => {}
174                Err(ref e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
175                    return Ok(None);
176                }
177                Err(e) => {
178                    return Err(e.into());
179                }
180            }
181
182            if magic != MAGIC {
183                return Err(Error::BadMagic);
184            }
185
186            let header = NewcHeader::from_reader(&mut reader)?;
187
188            if header.name == "TRAILER!!!" {
189                self.seen_trailer = true;
190                Ok(None)
191            } else {
192                self.entry_reader = Some(reader.take(header.file_size as _));
193                self.entry_data_pad = (header.file_size.wrapping_neg() % 4) as usize;
194                Ok(Some(Box::new(header)))
195            }
196        } else {
197            Ok(None)
198        }
199    }
200
201    fn finish(&mut self) -> CpioResult<()> {
202        if let Some(mut reader) = self.entry_reader.take() {
203            let mut buffer = vec![0u8; 32768];
204            loop {
205                if reader.read(&mut buffer)? == 0 {
206                    break;
207                }
208            }
209
210            let mut reader = reader.into_inner();
211
212            let mut pad = [0u8; 4];
213            reader.read_exact(&mut pad[..self.entry_data_pad])?;
214            self.entry_data_pad = 0;
215
216            // Only restore the archive reader if we haven't seen the trailer,
217            // as the trailer indicates end of archive.
218            if !self.seen_trailer {
219                self.archive_reader = Some(reader);
220            }
221        }
222
223        Ok(())
224    }
225}
226
227impl<T: Read + Sized> Iterator for NewcReader<T> {
228    type Item = CpioResult<Box<dyn CpioHeader>>;
229
230    fn next(&mut self) -> Option<Self::Item> {
231        match self.read_next() {
232            Ok(Some(r)) => Some(Ok(r)),
233            Ok(None) => None,
234            Err(e) => Some(Err(e)),
235        }
236    }
237}
238
239impl<T: Read + Sized> Read for NewcReader<T> {
240    fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
241        if let Some(reader) = &mut self.entry_reader {
242            reader.read(buf)
243        } else {
244            Err(std::io::Error::new(
245                std::io::ErrorKind::Other,
246                "no current archive entry to read from",
247            ))
248        }
249    }
250}