dyz1990 / sevenz-rust

A 7z decompressor/compressor lib written in pure rust
Apache License 2.0
153 stars 24 forks source link

Incorrect handling of 7z time #8

Closed KarbalOxalis closed 1 year ago

KarbalOxalis commented 1 year ago

In the implementation of this crate the mtime, ctime, and atime of a 7z entry is handled as an i64, as if it were a Unix timestamp, but if you look at the (public domain) C source for 7zip, it's always treated as the following struct, with no special handling for Linux:

typedef struct
{
  UInt32 Low;
  UInt32 High;
} CNtfsFileTime;

Effectively an equivalent to a Windows FILETIME, documentation for which can be found on MSDN or likely many other places.

In my own project I used (more or less) the following translation of the C conversion function:

#[repr(C)]
struct SzTime {
    low: u32,
    high: u32,
}

fn sz_to_unix_time_64(time: SzTime) -> Option<(i64, u32)> {
    const NANOS_IN_SECOND: u64 = 1_000_000_000;
    const WIN_TICKS_IN_SECOND: u64 = NANOS_IN_SECOND / 100;

    const WIN_TIME_START_YEAR: u64 = 1601;
    const UNIX_TIME_START_YEAR: u64 = 1970;

    const UNIX_WIN_YEAR_OFFSET: u64 = UNIX_TIME_START_YEAR - WIN_TIME_START_YEAR;
    const UNIX_WIN_DAY_OFFSET: u64 = 89 + (365 * UNIX_WIN_YEAR_OFFSET);
    const UNIX_WIN_SEC_OFFSET: u64 = 60 * 60 * 24 * UNIX_WIN_DAY_OFFSET;

    let time: u64 = time.low as u64 | (time.high as u64) << 32;

    let secs = (time / WIN_TICKS_IN_SECOND) - UNIX_WIN_SEC_OFFSET;
    let nanos = (time % WIN_TICKS_IN_SECOND) * 100;

    let secs: i64 = secs.try_into().ok()?;
    Some((secs, nanos as u32))
}
dyz1990 commented 1 year ago

@KarbalOxalis Thanks for your report. This issue was fixed on version 0.2.3