1use std::borrow::Cow;
2
3use ownable::{IntoOwned, ToOwned};
4use winnow::{
5 binary::{le_u16, le_u32, le_u64, le_u8, length_take},
6 combinator::{opt, preceded, repeat_till},
7 error::{ErrMode, ErrorKind, ParserError, StrContext},
8 seq,
9 token::{literal, take},
10 PResult, Parser, Partial,
11};
12
13use crate::parse::NtfsTimestamp;
14
15pub(crate) struct ExtraFieldRecord<'a> {
17 pub(crate) tag: u16,
18 pub(crate) payload: &'a [u8],
19}
20
21impl<'a> ExtraFieldRecord<'a> {
22 pub(crate) fn parser(i: &mut Partial<&'a [u8]>) -> PResult<Self> {
23 seq! {Self {
24 tag: le_u16,
25 payload: length_take(le_u16),
26 }}
27 .parse_next(i)
28 }
29}
30
31#[derive(Debug, Clone, Copy)]
40pub struct ExtraFieldSettings {
41 pub uncompressed_size_u32: u32,
45
46 pub compressed_size_u32: u32,
50
51 pub header_offset_u32: u32,
55}
56
57#[derive(Clone)]
63pub enum ExtraField<'a> {
64 Zip64(ExtraZip64Field),
66 Timestamp(ExtraTimestampField),
68 Unix(ExtraUnixField<'a>),
70 NewUnix(ExtraNewUnixField),
72 Ntfs(ExtraNtfsField),
74 Unknown {
76 tag: u16,
78 },
79}
80
81impl<'a> ExtraField<'a> {
82 pub fn mk_parser(
85 settings: ExtraFieldSettings,
86 ) -> impl FnMut(&mut Partial<&'a [u8]>) -> PResult<Self> {
87 move |i| {
88 use ExtraField as EF;
89 let rec = ExtraFieldRecord::parser.parse_next(i)?;
90 let payload = &mut Partial::new(rec.payload);
91
92 let variant = match rec.tag {
93 ExtraZip64Field::TAG => opt(ExtraZip64Field::mk_parser(settings).map(EF::Zip64))
94 .context(StrContext::Label("zip64"))
95 .parse_next(payload)?,
96 ExtraTimestampField::TAG => opt(ExtraTimestampField::parser.map(EF::Timestamp))
97 .context(StrContext::Label("timestamp"))
98 .parse_next(payload)?,
99 ExtraNtfsField::TAG => {
100 opt(ExtraNtfsField::parser.map(EF::Ntfs)).parse_next(payload)?
101 }
102 ExtraUnixField::TAG | ExtraUnixField::TAG_INFOZIP => {
103 opt(ExtraUnixField::parser.map(EF::Unix)).parse_next(payload)?
104 }
105 ExtraNewUnixField::TAG => {
106 opt(ExtraNewUnixField::parser.map(EF::NewUnix)).parse_next(payload)?
107 }
108 _ => None,
109 }
110 .unwrap_or(EF::Unknown { tag: rec.tag });
111
112 Ok(variant)
113 }
114 }
115}
116
117#[derive(Clone, Default)]
119pub struct ExtraZip64Field {
120 pub uncompressed_size: u64,
122
123 pub compressed_size: u64,
125
126 pub header_offset: u64,
128
129 pub disk_start: Option<u32>,
131}
132
133impl ExtraZip64Field {
134 const TAG: u16 = 0x0001;
135
136 pub(crate) fn mk_parser(
137 settings: ExtraFieldSettings,
138 ) -> impl FnMut(&mut Partial<&'_ [u8]>) -> PResult<Self> {
139 move |i| {
140 let uncompressed_size = if settings.uncompressed_size_u32 == 0xFFFF_FFFF {
141 le_u64.parse_next(i)?
142 } else {
143 settings.uncompressed_size_u32 as u64
144 };
145 let compressed_size = if settings.compressed_size_u32 == 0xFFFF_FFFF {
146 le_u64.parse_next(i)?
147 } else {
148 settings.compressed_size_u32 as u64
149 };
150 let header_offset = if settings.header_offset_u32 == 0xFFFF_FFFF {
151 le_u64.parse_next(i)?
152 } else {
153 settings.header_offset_u32 as u64
154 };
155 let disk_start = opt(le_u32.complete_err()).parse_next(i)?;
156
157 Ok(Self {
158 uncompressed_size,
159 compressed_size,
160 header_offset,
161 disk_start,
162 })
163 }
164 }
165}
166
167#[derive(Clone)]
169pub struct ExtraTimestampField {
170 pub mtime: u32,
172}
173
174impl ExtraTimestampField {
175 const TAG: u16 = 0x5455;
176
177 fn parser(i: &mut Partial<&'_ [u8]>) -> PResult<Self> {
178 preceded(
179 le_u8.verify(|x| x & 0b1 != 0),
181 seq! {Self { mtime: le_u32 }},
182 )
183 .parse_next(i)
184 }
185}
186
187#[derive(Clone, ToOwned, IntoOwned)]
189pub struct ExtraUnixField<'a> {
190 pub atime: u32,
192 pub mtime: u32,
194 pub uid: u16,
196 pub gid: u16,
198 pub data: Cow<'a, [u8]>,
200}
201
202impl<'a> ExtraUnixField<'a> {
203 const TAG: u16 = 0x000d;
204 const TAG_INFOZIP: u16 = 0x5855;
205
206 fn parser(i: &mut Partial<&'a [u8]>) -> PResult<Self> {
207 let t_size = le_u16.parse_next(i)?;
208
209 let t_size = t_size
211 .checked_sub(12)
212 .ok_or(ErrMode::from_error_kind(i, ErrorKind::Verify))?;
213
214 seq! {Self {
215 atime: le_u32,
216 mtime: le_u32,
217 uid: le_u16,
218 gid: le_u16,
219 data: take(t_size).map(Cow::Borrowed),
220 }}
221 .parse_next(i)
222 }
223}
224
225#[derive(Clone)]
243pub struct ExtraNewUnixField {
244 pub uid: u64,
246
247 pub gid: u64,
249}
250
251impl ExtraNewUnixField {
252 const TAG: u16 = 0x7875;
253
254 fn parser(i: &mut Partial<&'_ [u8]>) -> PResult<Self> {
255 let _ = literal("\x01").parse_next(i)?;
256 seq! {Self {
257 uid: Self::parse_variable_length_integer,
258 gid: Self::parse_variable_length_integer,
259 }}
260 .parse_next(i)
261 }
262
263 fn parse_variable_length_integer(i: &mut Partial<&'_ [u8]>) -> PResult<u64> {
264 let slice = length_take(le_u8).parse_next(i)?;
265 if let Some(u) = match slice.len() {
266 1 => Some(le_u8.parse_peek(slice)?.1 as u64),
267 2 => Some(le_u16.parse_peek(slice)?.1 as u64),
268 4 => Some(le_u32.parse_peek(slice)?.1 as u64),
269 8 => Some(le_u64.parse_peek(slice)?.1),
270 _ => None,
271 } {
272 Ok(u)
273 } else {
274 Err(ErrMode::from_error_kind(i, ErrorKind::Alt))
275 }
276 }
277}
278
279#[derive(Clone)]
281pub struct ExtraNtfsField {
282 pub attrs: Vec<NtfsAttr>,
284}
285
286impl ExtraNtfsField {
287 const TAG: u16 = 0x000a;
288
289 fn parser(i: &mut Partial<&'_ [u8]>) -> PResult<Self> {
290 let _ = take(4_usize).parse_next(i)?; seq! {Self {
292 attrs: repeat_till(0.., NtfsAttr::parser, winnow::combinator::eof).map(|x| x.0),
298 }}
299 .parse_next(i)
300 }
301}
302
303#[derive(Clone)]
305pub enum NtfsAttr {
306 Attr1(NtfsAttr1),
308
309 Unknown {
311 tag: u16,
313 },
314}
315
316impl NtfsAttr {
317 fn parser(i: &mut Partial<&'_ [u8]>) -> PResult<Self> {
318 let tag = le_u16.parse_next(i)?;
319 let payload = length_take(le_u16).parse_next(i)?;
320
321 match tag {
322 0x0001 => NtfsAttr1::parser
323 .parse_peek(Partial::new(payload))
324 .map(|(_, attr)| NtfsAttr::Attr1(attr)),
325 _ => Ok(NtfsAttr::Unknown { tag }),
326 }
327 }
328}
329
330#[derive(Clone)]
332pub struct NtfsAttr1 {
333 pub mtime: NtfsTimestamp,
335
336 pub atime: NtfsTimestamp,
338
339 pub ctime: NtfsTimestamp,
341}
342
343impl NtfsAttr1 {
344 fn parser(i: &mut Partial<&'_ [u8]>) -> PResult<Self> {
345 seq! {Self {
346 mtime: NtfsTimestamp::parser,
347 atime: NtfsTimestamp::parser,
348 ctime: NtfsTimestamp::parser,
349 }}
350 .parse_next(i)
351 }
352}