use std::borrow::Cow;
use ownable::{IntoOwned, ToOwned};
use winnow::{
binary::{le_u16, le_u32, le_u64, le_u8, length_take},
combinator::{opt, preceded, repeat_till},
error::{ErrMode, ErrorKind, ParserError, StrContext},
seq,
token::{literal, take},
PResult, Parser, Partial,
};
use crate::parse::NtfsTimestamp;
pub(crate) struct ExtraFieldRecord<'a> {
pub(crate) tag: u16,
pub(crate) payload: &'a [u8],
}
impl<'a> ExtraFieldRecord<'a> {
pub(crate) fn parser(i: &mut Partial<&'a [u8]>) -> PResult<Self> {
seq! {Self {
tag: le_u16,
payload: length_take(le_u16),
}}
.parse_next(i)
}
}
#[derive(Debug, Clone, Copy)]
pub struct ExtraFieldSettings {
pub uncompressed_size_u32: u32,
pub compressed_size_u32: u32,
pub header_offset_u32: u32,
}
#[derive(Clone)]
pub enum ExtraField<'a> {
Zip64(ExtraZip64Field),
Timestamp(ExtraTimestampField),
Unix(ExtraUnixField<'a>),
NewUnix(ExtraNewUnixField),
Ntfs(ExtraNtfsField),
Unknown {
tag: u16,
},
}
impl<'a> ExtraField<'a> {
pub fn mk_parser(
settings: ExtraFieldSettings,
) -> impl FnMut(&mut Partial<&'a [u8]>) -> PResult<Self> {
move |i| {
use ExtraField as EF;
let rec = ExtraFieldRecord::parser.parse_next(i)?;
let payload = &mut Partial::new(rec.payload);
let variant = match rec.tag {
ExtraZip64Field::TAG => opt(ExtraZip64Field::mk_parser(settings).map(EF::Zip64))
.context(StrContext::Label("zip64"))
.parse_next(payload)?,
ExtraTimestampField::TAG => opt(ExtraTimestampField::parser.map(EF::Timestamp))
.context(StrContext::Label("timestamp"))
.parse_next(payload)?,
ExtraNtfsField::TAG => {
opt(ExtraNtfsField::parser.map(EF::Ntfs)).parse_next(payload)?
}
ExtraUnixField::TAG => {
opt(ExtraUnixField::parser.map(EF::Unix)).parse_next(payload)?
}
ExtraNewUnixField::TAG => {
opt(ExtraNewUnixField::parser.map(EF::NewUnix)).parse_next(payload)?
}
_ => None,
}
.unwrap_or(EF::Unknown { tag: rec.tag });
Ok(variant)
}
}
}
#[derive(Clone, Default)]
pub struct ExtraZip64Field {
pub uncompressed_size: u64,
pub compressed_size: u64,
pub header_offset: u64,
pub disk_start: Option<u32>,
}
impl ExtraZip64Field {
const TAG: u16 = 0x0001;
pub(crate) fn mk_parser(
settings: ExtraFieldSettings,
) -> impl FnMut(&mut Partial<&'_ [u8]>) -> PResult<Self> {
move |i| {
let uncompressed_size = if settings.uncompressed_size_u32 == 0xFFFF_FFFF {
le_u64.parse_next(i)?
} else {
settings.uncompressed_size_u32 as u64
};
let compressed_size = if settings.compressed_size_u32 == 0xFFFF_FFFF {
le_u64.parse_next(i)?
} else {
settings.compressed_size_u32 as u64
};
let header_offset = if settings.header_offset_u32 == 0xFFFF_FFFF {
le_u64.parse_next(i)?
} else {
settings.header_offset_u32 as u64
};
let disk_start = opt(le_u32.complete_err()).parse_next(i)?;
Ok(Self {
uncompressed_size,
compressed_size,
header_offset,
disk_start,
})
}
}
}
#[derive(Clone)]
pub struct ExtraTimestampField {
pub mtime: u32,
}
impl ExtraTimestampField {
const TAG: u16 = 0x5455;
fn parser(i: &mut Partial<&'_ [u8]>) -> PResult<Self> {
preceded(
le_u8.verify(|x| x & 0b1 != 0),
seq! {Self { mtime: le_u32 }},
)
.parse_next(i)
}
}
#[derive(Clone, ToOwned, IntoOwned)]
pub struct ExtraUnixField<'a> {
pub atime: u32,
pub mtime: u32,
pub uid: u16,
pub gid: u16,
pub data: Cow<'a, [u8]>,
}
impl<'a> ExtraUnixField<'a> {
const TAG: u16 = 0x000d;
const _TAG_INFOZIP: u16 = 0x5855;
fn parser(i: &mut Partial<&'a [u8]>) -> PResult<Self> {
let t_size = le_u16.parse_next(i)?;
let t_size = t_size
.checked_sub(12)
.ok_or(ErrMode::from_error_kind(i, ErrorKind::Verify))?;
seq! {Self {
atime: le_u32,
mtime: le_u32,
uid: le_u16,
gid: le_u16,
data: take(t_size).map(Cow::Borrowed),
}}
.parse_next(i)
}
}
#[derive(Clone)]
pub struct ExtraNewUnixField {
pub uid: u64,
pub gid: u64,
}
impl ExtraNewUnixField {
const TAG: u16 = 0x7875;
fn parser(i: &mut Partial<&'_ [u8]>) -> PResult<Self> {
let _ = literal("\x01").parse_next(i)?;
seq! {Self {
uid: Self::parse_variable_length_integer,
gid: Self::parse_variable_length_integer,
}}
.parse_next(i)
}
fn parse_variable_length_integer(i: &mut Partial<&'_ [u8]>) -> PResult<u64> {
let slice = length_take(le_u8).parse_next(i)?;
if let Some(u) = match slice.len() {
1 => Some(le_u8.parse_peek(slice)?.1 as u64),
2 => Some(le_u16.parse_peek(slice)?.1 as u64),
4 => Some(le_u32.parse_peek(slice)?.1 as u64),
8 => Some(le_u64.parse_peek(slice)?.1),
_ => None,
} {
Ok(u)
} else {
Err(ErrMode::from_error_kind(i, ErrorKind::Alt))
}
}
}
#[derive(Clone)]
pub struct ExtraNtfsField {
pub attrs: Vec<NtfsAttr>,
}
impl ExtraNtfsField {
const TAG: u16 = 0x000a;
fn parser(i: &mut Partial<&'_ [u8]>) -> PResult<Self> {
let _ = take(4_usize).parse_next(i)?; seq! {Self {
attrs: repeat_till(0.., NtfsAttr::parser, winnow::combinator::eof).map(|x| x.0),
}}
.parse_next(i)
}
}
#[derive(Clone)]
pub enum NtfsAttr {
Attr1(NtfsAttr1),
Unknown {
tag: u16,
},
}
impl NtfsAttr {
fn parser(i: &mut Partial<&'_ [u8]>) -> PResult<Self> {
let tag = le_u16.parse_next(i)?;
let payload = length_take(le_u16).parse_next(i)?;
match tag {
0x0001 => NtfsAttr1::parser
.parse_peek(Partial::new(payload))
.map(|(_, attr)| NtfsAttr::Attr1(attr)),
_ => Ok(NtfsAttr::Unknown { tag }),
}
}
}
#[derive(Clone)]
pub struct NtfsAttr1 {
pub mtime: NtfsTimestamp,
pub atime: NtfsTimestamp,
pub ctime: NtfsTimestamp,
}
impl NtfsAttr1 {
fn parser(i: &mut Partial<&'_ [u8]>) -> PResult<Self> {
seq! {Self {
mtime: NtfsTimestamp::parser,
atime: NtfsTimestamp::parser,
ctime: NtfsTimestamp::parser,
}}
.parse_next(i)
}
}