diff --git a/crates/rc-zip/src/format/date_time.rs b/crates/rc-zip/src/format/date_time.rs index 4af476c..baeee9a 100644 --- a/crates/rc-zip/src/format/date_time.rs +++ b/crates/rc-zip/src/format/date_time.rs @@ -87,9 +87,9 @@ impl NtfsTimestamp { // windows timestamp resolution let ticks_per_second = 10_000_000; let secs = (self.timestamp / ticks_per_second) as i64; - let nsecs = (1_000_000_000 / ticks_per_second) * (self.timestamp * ticks_per_second); + let nsecs = ((self.timestamp % ticks_per_second) * 100) as u32; let epoch = Utc.with_ymd_and_hms(1601, 1, 1, 0, 0, 0).single()?; - match Utc.timestamp_opt(epoch.timestamp() + secs, nsecs as u32) { + match Utc.timestamp_opt(epoch.timestamp() + secs, nsecs) { LocalResult::Single(date) => Some(date), _ => None, } diff --git a/crates/rc-zip/src/format/extra_field.rs b/crates/rc-zip/src/format/extra_field.rs index 0f6fb7a..c8d8627 100644 --- a/crates/rc-zip/src/format/extra_field.rs +++ b/crates/rc-zip/src/format/extra_field.rs @@ -1,8 +1,9 @@ use crate::format::*; +use tracing::trace; use winnow::{ binary::{le_u16, le_u32, le_u64, le_u8, length_take}, - combinator::{cond, opt, preceded, repeat}, - error::{ErrMode, ErrorKind, ParserError}, + combinator::{cond, opt, preceded, repeat_till}, + error::{ErrMode, ErrorKind, ParserError, StrContext}, seq, token::{tag, take}, PResult, Parser, Partial, @@ -66,15 +67,16 @@ impl ExtraField { move |i| { use ExtraField as EF; let rec = ExtraFieldRecord::parser.parse_next(i)?; + trace!("parsing extra field record, tag {:04x}", rec.tag); let payload = &mut Partial::new(rec.payload); let variant = match rec.tag { - ExtraZip64Field::TAG => { - opt(ExtraZip64Field::mk_parser(settings).map(EF::Zip64)).parse_next(payload)? - } - ExtraTimestampField::TAG => { - opt(ExtraTimestampField::parser.map(EF::Timestamp)).parse_next(payload)? - } + 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::parse.map(EF::Ntfs)).parse_next(payload)? } @@ -234,7 +236,12 @@ impl ExtraNtfsField { fn parse(i: &mut Partial<&'_ [u8]>) -> PResult { let _ = take(4_usize).parse_next(i)?; // reserved (unused) seq! {Self { - attrs: repeat(0.., NtfsAttr::parse), + // from the winnow docs: + // Parsers like repeat do not know when an eof is from insufficient + // data or the end of the stream, causing them to always report + // Incomplete. + // using repeat_till with eof combinator to work around this: + attrs: repeat_till(0.., NtfsAttr::parse, winnow::combinator::eof).map(|x| x.0), }} .parse_next(i) } @@ -250,6 +257,7 @@ pub enum NtfsAttr { impl NtfsAttr { fn parse(i: &mut Partial<&'_ [u8]>) -> PResult { let tag = le_u16.parse_next(i)?; + trace!("parsing NTFS attribute, tag {:04x}", tag); let payload = length_take(le_u16).parse_next(i)?; match tag { @@ -270,6 +278,7 @@ pub struct NtfsAttr1 { impl NtfsAttr1 { fn parser(i: &mut Partial<&'_ [u8]>) -> PResult { + trace!("parsing NTFS attr 1, input len is {}", i.len()); seq! {Self { mtime: NtfsTimestamp::parser, atime: NtfsTimestamp::parser, diff --git a/crates/rc-zip/src/reader/archive_reader.rs b/crates/rc-zip/src/reader/archive_reader.rs index f576892..1ffd53e 100644 --- a/crates/rc-zip/src/reader/archive_reader.rs +++ b/crates/rc-zip/src/reader/archive_reader.rs @@ -289,13 +289,25 @@ impl ArchiveReader { buffer.available_data() ); let mut input = Partial::new(buffer.data()); + trace!( + initial_offset = input.as_bytes().offset_from(&buffer.data()), + initial_len = input.len(), + "initial offset & len" + ); 'read_headers: while !input.is_empty() { match DirectoryHeader::parser.parse_next(&mut input) { Ok(dh) => { + trace!( + input_empty_now = input.is_empty(), + offset = input.as_bytes().offset_from(&buffer.data()), + len = input.len(), + "ReadCentralDirectory | parsed directory header" + ); directory_headers.push(dh); } Err(ErrMode::Incomplete(_needed)) => { // need more data to read the full header + trace!("ReadCentralDirectory | incomplete!"); break 'read_headers; } Err(ErrMode::Backtrack(_err)) | Err(ErrMode::Cut(_err)) => { @@ -394,7 +406,7 @@ impl ArchiveReader { } } let consumed = input.as_bytes().offset_from(&buffer.data()); - tracing::trace!(%consumed, "ReadCentralDirectory done"); + tracing::trace!(%consumed, "ReadCentralDirectory total consumed"); buffer.consume(consumed); // need more data diff --git a/crates/rc-zip/src/tests.rs b/crates/rc-zip/src/tests.rs index 50331b4..9763261 100644 --- a/crates/rc-zip/src/tests.rs +++ b/crates/rc-zip/src/tests.rs @@ -267,7 +267,7 @@ fn test_cases() -> Vec { files: vec![ZipTestFile { name: "世界", content: FileContent::Bytes(vec![]), - modified: Some(date((2017, 11, 6), (13, 9, 26), 0, time_zone(0)).unwrap()), + modified: Some(date((2017, 11, 6), (21, 9, 27), 867862500, time_zone(0)).unwrap()), ..Default::default() }], ..Default::default() @@ -279,7 +279,7 @@ fn test_cases() -> Vec { files: vec![ZipTestFile { name: "found-me.txt", content: FileContent::Bytes("Oh no, you found me\n".repeat(5000).into()), - modified: Some(date((2024, 1, 26), (17, 14, 36), 0, time_zone(0)).unwrap()), + modified: Some(date((2024, 1, 26), (16, 14, 35), 46003100, time_zone(0)).unwrap()), ..Default::default() }], ..Default::default() @@ -291,7 +291,7 @@ fn test_cases() -> Vec { files: vec![ZipTestFile { name: "found-me.txt", content: FileContent::Bytes("Oh no, you found me\n".repeat(5000).into()), - modified: Some(date((2024, 1, 26), (17, 14, 36), 0, time_zone(0)).unwrap()), + modified: Some(date((2024, 1, 26), (16, 14, 35), 46003100, time_zone(0)).unwrap()), ..Default::default() }], ..Default::default() @@ -304,7 +304,7 @@ fn test_cases() -> Vec { files: vec![ZipTestFile { name: "found-me.txt", content: FileContent::Bytes("Oh no, you found me\n".repeat(5000).into()), - modified: Some(date((2024, 1, 26), (17, 14, 36), 0, time_zone(0)).unwrap()), + modified: Some(date((2024, 1, 26), (16, 14, 35), 46003100, time_zone(0)).unwrap()), ..Default::default() }], ..Default::default() @@ -333,6 +333,7 @@ fn read_from_file() { #[traced_test] fn real_world_files() { for case in test_cases() { + eprintln!("============ testing {}", case.name()); case.check(case.bytes().read_zip()); } }