1use {
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 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
148pub 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 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}