From a4e92ed9bec1f5879eb1c20dfe281c4d25ed5f89 Mon Sep 17 00:00:00 2001 From: Igor Tolmachev Date: Sun, 23 Jun 2024 15:19:40 +0900 Subject: Improve ZipFile --- src/zip/driver.rs | 113 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 79 insertions(+), 34 deletions(-) (limited to 'src/zip/driver.rs') diff --git a/src/zip/driver.rs b/src/zip/driver.rs index e5aa58d..8e8c27c 100644 --- a/src/zip/driver.rs +++ b/src/zip/driver.rs @@ -1,7 +1,8 @@ use crate::driver::{ArchiveRead, ArchiveWrite, Driver}; -use crate::zip::error::{ZipError, ZipResult}; -use crate::zip::structs::{deserialize, EOCDR64Locator, CDR, EOCDR, EOCDR64}; -use crate::zip::ZipFile; +use crate::zip::file::CompressionMethod; +use crate::zip::structs::{deserialize, EOCDR64Locator, ExtraHeader, CDR, EOCDR, EOCDR64}; +use crate::zip::{ZipError, ZipFile, ZipResult}; +use chrono::{Local, NaiveDate, NaiveDateTime, NaiveTime}; use std::collections::HashMap as Map; use std::fs::File; use std::io::{Read, Seek, SeekFrom, Write}; @@ -41,7 +42,7 @@ impl ArchiveRead for Zip { io.read(&mut buf)?; buf }; - let eocdr: EOCDR = deserialize(&buf).map_err(|_| ZipError::InvalidEOCDR)?; + let eocdr: EOCDR = deserialize(&buf).unwrap(); let comment = { let mut buf: Vec = vec![0; eocdr.comment_len as usize]; io.read(&mut buf)?; @@ -55,31 +56,29 @@ impl ArchiveRead for Zip { io.read(&mut buf)?; buf }; - let (cd_pointer, cd_size, cd_records) = if u32::from_le_bytes(buf[0..4].try_into().unwrap()) - == 0x07064b50 - { - let eocdr64locator: EOCDR64Locator = - deserialize(&buf[4..]).map_err(|_| ZipError::InvalidEOCDR64Locator)?; - - io.seek(SeekFrom::Start(eocdr64locator.eocdr64_pointer))?; - let buf = { - let mut buf = [0; 56]; - io.read(&mut buf)?; - buf + let (cd_pointer, cd_size, cd_records) = + if u32::from_le_bytes(buf[0..4].try_into().unwrap()) == 0x07064b50 { + let eocdr64locator: EOCDR64Locator = deserialize(&buf[4..]).unwrap(); + + io.seek(SeekFrom::Start(eocdr64locator.eocdr64_pointer))?; + let buf = { + let mut buf = [0; 56]; + io.read(&mut buf)?; + buf + }; + if u32::from_le_bytes(buf[0..4].try_into().unwrap()) != 0x06064b50 { + return Err(ZipError::InvalidEOCDR64Signature.into()); + } + let eocdr64: EOCDR64 = deserialize(&buf[4..]).unwrap(); + + (eocdr64.cd_pointer, eocdr64.cd_size, eocdr64.cd_records) + } else { + ( + eocdr.cd_pointer as u64, + eocdr.cd_size as u64, + eocdr.cd_records as u64, + ) }; - if u32::from_le_bytes(buf[0..4].try_into().unwrap()) != 0x06064b50 { - return Err(ZipError::InvalidEOCDR64Signature.into()); - } - let eocdr64: EOCDR64 = deserialize(&buf[4..]).map_err(|_| ZipError::InvalidEOCDR64)?; - - (eocdr64.cd_pointer, eocdr64.cd_size, eocdr64.cd_records) - } else { - ( - eocdr.cd_pointer as u64, - eocdr.cd_size as u64, - eocdr.cd_records as u64, - ) - }; // Read cd records let mut files = Map::with_capacity(cd_records as usize); @@ -96,7 +95,7 @@ impl ArchiveRead for Zip { return Err(ZipError::InvalidCDRSignature.into()); } p += 4; - let cdr: CDR = deserialize(&buf[p..p + 42]).map_err(|_| ZipError::InvalidCDR)?; + let cdr: CDR = deserialize(&buf[p..p + 42]).unwrap(); p += 42; let name = String::from_utf8(buf[p..p + cdr.name_len as usize].into()).unwrap(); p += cdr.name_len as usize; @@ -105,15 +104,61 @@ impl ArchiveRead for Zip { let comment = String::from_utf8(buf[p..p + cdr.comment_len as usize].into()).unwrap(); p += cdr.comment_len as usize; + let mut compressed_size = cdr.compressed_size as u64; + let mut size = cdr.size as u64; + let mut header_pointer = cdr.header_pointer as u64; + + let mut ep: usize = 0; + while ep < cdr.extra_field_len as usize { + let header: ExtraHeader = deserialize(&extra_fields[ep..ep + 4]).unwrap(); + ep += 4; + match header.id { + 0x0001 => { + if size == 0xFFFFFFFF { + compressed_size = deserialize(&extra_fields[ep..ep + 8]).unwrap(); + ep += 8; + } + if compressed_size == 0xFFFFFFFF { + size = deserialize(&extra_fields[ep..ep + 8]).unwrap(); + ep += 8; + } + if header_pointer == 0xFFFFFFFF { + header_pointer = deserialize(&extra_fields[ep..ep + 8]).unwrap(); + ep += 8; + } + if cdr.disk == 0xFFFF { + ep += 4 + } + } + _ => ep += header.size as usize, + } + } + files.insert( name.clone(), ZipFile::new( + CompressionMethod::from_struct_id(cdr.compression_method)?, + NaiveDateTime::new( + NaiveDate::from_ymd_opt( + (cdr.dos_date as i32 >> 9 & 0x7F) + 1980, + cdr.dos_date as u32 >> 5 & 0xF, + cdr.dos_date as u32 & 0x1F, + ) + .ok_or(ZipError::InvalidDate)?, + NaiveTime::from_hms_opt( + (cdr.dos_time as u32 >> 11) & 0x1F, + (cdr.dos_time as u32 >> 5) & 0x3F, + (cdr.dos_time as u32 & 0x1F) * 2, + ) + .ok_or(ZipError::InvalidTime)?, + ) + .and_local_timezone(Local) + .unwrap(), + cdr.crc, + compressed_size, + size, + header_pointer, name, - cdr.dos_date, - cdr.dos_time, - cdr.compression_method, - cdr.compressed_size as u64, - cdr.size as u64, comment, ), ); -- cgit v1.2.3