From 4a784b563637ae104474aafa4b43214af9aa6ffb Mon Sep 17 00:00:00 2001 From: Danny McClanahan <1305167+cosmicexplorer@users.noreply.github.com> Date: Wed, 22 May 2024 13:00:49 -0400 Subject: [PATCH] interpose ZipRawValues into ZipFileData --- src/read.rs | 42 ++++++++++++++++--------------- src/spec.rs | 0 src/types.rs | 70 +++++++++++++++++++++++++++++++++------------------- src/write.rs | 22 ++++++++--------- 4 files changed, 77 insertions(+), 57 deletions(-) mode change 100755 => 100644 src/spec.rs diff --git a/src/read.rs b/src/read.rs index c7e1cf7c2..7879c4331 100644 --- a/src/read.rs +++ b/src/read.rs @@ -11,7 +11,7 @@ use crate::result::{ZipError, ZipResult}; use crate::spec::{self, Block}; use crate::types::{ AesMode, AesVendorVersion, DateTime, System, ZipCentralEntryBlock, ZipFileData, - ZipLocalEntryBlock, + ZipLocalEntryBlock, ZipRawValues, }; use crate::zipcrypto::{ZipCryptoReader, ZipCryptoReaderValid, ZipCryptoValidator}; use indexmap::IndexMap; @@ -252,7 +252,7 @@ pub(crate) fn find_content<'a>( }; reader.seek(io::SeekFrom::Start(data_start))?; - Ok((reader as &mut dyn Read).take(data.compressed_size)) + Ok((reader as &mut dyn Read).take(data.compressed_size())) } #[allow(clippy::too_many_arguments)] @@ -404,7 +404,7 @@ impl ZipArchive { if file.using_data_descriptor { return None; } - total = total.checked_add(file.uncompressed_size as u128)?; + total = total.checked_add(file.uncompressed_size() as u128)?; } Some(total) } @@ -700,7 +700,7 @@ impl ZipArchive { None => Ok(None), Some((aes_mode, _, _)) => { let (verification_value, salt) = - AesReader::new(limit_reader, aes_mode, data.compressed_size) + AesReader::new(limit_reader, aes_mode, data.compressed_size()) .get_verification_value_and_salt()?; let aes_info = AesInfo { aes_mode, @@ -980,14 +980,14 @@ impl ZipArchive { let crypto_reader = make_crypto_reader( data.compression_method, - data.crc32, + data.crc32(), data.last_modified_time, data.using_data_descriptor, limit_reader, password, data.aes_mode, #[cfg(feature = "aes-crypto")] - data.compressed_size, + data.compressed_size(), )?; Ok(ZipFile { crypto_reader: Some(crypto_reader), @@ -1094,9 +1094,11 @@ fn central_header_to_zip_file_inner( compression_method: CompressionMethod::parse_from_u16(compression_method), compression_level: None, last_modified_time: DateTime::try_from_msdos(last_mod_date, last_mod_time).ok(), - crc32, - compressed_size: compressed_size as u64, - uncompressed_size: uncompressed_size as u64, + raw_values: ZipRawValues { + crc32, + compressed_size: compressed_size.into(), + uncompressed_size: uncompressed_size.into(), + }, file_name, file_name_raw, extra_field: Some(Arc::new(extra_field.to_vec())), @@ -1147,14 +1149,14 @@ fn parse_extra_field(file: &mut ZipFileData) -> ZipResult<()> { match kind { // Zip64 extended information extra field 0x0001 => { - if file.uncompressed_size == spec::ZIP64_BYTES_THR { + if file.uncompressed_size() == spec::ZIP64_BYTES_THR { file.large_file = true; - file.uncompressed_size = reader.read_u64_le()?; + file.raw_values.uncompressed_size = reader.read_u64_le()?; len_left -= 8; } - if file.compressed_size == spec::ZIP64_BYTES_THR { + if file.compressed_size() == spec::ZIP64_BYTES_THR { file.large_file = true; - file.compressed_size = reader.read_u64_le()?; + file.raw_values.compressed_size = reader.read_u64_le()?; len_left -= 8; } if file.header_start == spec::ZIP64_BYTES_THR { @@ -1228,7 +1230,7 @@ impl<'a> ZipFile<'a> { if let ZipFileReader::NoReader = self.reader { let data = &self.data; let crypto_reader = self.crypto_reader.take().expect("Invalid reader state"); - self.reader = make_reader(data.compression_method, data.crc32, crypto_reader)?; + self.reader = make_reader(data.compression_method, data.crc32(), crypto_reader)?; } Ok(&mut self.reader) } @@ -1325,12 +1327,12 @@ impl<'a> ZipFile<'a> { /// Get the size of the file, in bytes, in the archive pub fn compressed_size(&self) -> u64 { - self.data.compressed_size + self.data.compressed_size() } /// Get the size of the file, in bytes, when uncompressed pub fn size(&self) -> u64 { - self.data.uncompressed_size + self.data.uncompressed_size() } /// Get the time the file was last modified @@ -1360,7 +1362,7 @@ impl<'a> ZipFile<'a> { /// Get the CRC32 hash of the original file pub fn crc32(&self) -> u32 { - self.data.crc32 + self.data.crc32() } /// Get the extra data of the zip header for this file @@ -1459,9 +1461,9 @@ pub fn read_zipfile_from_stream<'a, R: Read>(reader: &'a mut R) -> ZipResult return Err(e), } - let limit_reader = (reader as &'a mut dyn Read).take(result.compressed_size); + let limit_reader = (reader as &'a mut dyn Read).take(result.compressed_size()); - let result_crc32 = result.crc32; + let result_crc32 = result.crc32(); let result_compression_method = result.compression_method; let crypto_reader = make_crypto_reader( result_compression_method, @@ -1472,7 +1474,7 @@ pub fn read_zipfile_from_stream<'a, R: Read>(reader: &'a mut R) -> ZipResult, /// Last modified time. This will only have a 2 second precision. pub last_modified_time: Option, - /// CRC32 checksum - pub crc32: u32, - /// Size of the file in the ZIP - pub compressed_size: u64, - /// Size of the file when extracted - pub uncompressed_size: u64, + /// Checksum and data extents + pub(crate) raw_values: ZipRawValues, /// Name of the file pub file_name: Box, /// Raw file name. To be used when file_name was incorrectly decoded. @@ -431,6 +432,21 @@ pub struct ZipFileData { } impl ZipFileData { + #[inline(always)] + pub fn crc32(&self) -> u32 { + self.raw_values.crc32 + } + + #[inline(always)] + pub fn compressed_size(&self) -> u64 { + self.raw_values.compressed_size + } + + #[inline(always)] + pub fn uncompressed_size(&self) -> u64 { + self.raw_values.uncompressed_size + } + #[allow(dead_code)] pub fn is_dir(&self) -> bool { is_dir(&self.file_name) @@ -583,9 +599,7 @@ impl ZipFileData { compression_method, compression_level: options.compression_level, last_modified_time: Some(options.last_modified_time), - crc32: raw_values.crc32, - compressed_size: raw_values.compressed_size, - uncompressed_size: raw_values.uncompressed_size, + raw_values, file_name, // Never used for saving, but used as map key in insert_file_data() file_name_raw, extra_field, @@ -664,9 +678,11 @@ impl ZipFileData { compression_method, compression_level: None, last_modified_time: DateTime::try_from_msdos(last_mod_date, last_mod_time).ok(), - crc32, - compressed_size: compressed_size.into(), - uncompressed_size: uncompressed_size.into(), + raw_values: ZipRawValues { + crc32, + compressed_size: compressed_size.into(), + uncompressed_size: uncompressed_size.into(), + }, file_name, file_name_raw: file_name_raw.into(), extra_field: Some(Arc::new(extra_field)), @@ -717,8 +733,8 @@ impl ZipFileData { } pub(crate) fn local_block(&self) -> ZipResult { - let compressed_size: u32 = self.clamp_size_field(self.compressed_size); - let uncompressed_size: u32 = self.clamp_size_field(self.uncompressed_size); + let compressed_size: u32 = self.clamp_size_field(self.compressed_size()); + let uncompressed_size: u32 = self.clamp_size_field(self.uncompressed_size()); let extra_block_len: usize = self .zip64_extra_field_block() @@ -738,7 +754,7 @@ impl ZipFileData { compression_method: self.compression_method.serialize_to_u16(), last_mod_time: last_modified_time.timepart(), last_mod_date: last_modified_time.datepart(), - crc32: self.crc32, + crc32: self.crc32(), compressed_size, uncompressed_size, file_name_length: self.file_name_raw.len().try_into().unwrap(), @@ -760,14 +776,14 @@ impl ZipFileData { compression_method: self.compression_method.serialize_to_u16(), last_mod_time: last_modified_time.timepart(), last_mod_date: last_modified_time.datepart(), - crc32: self.crc32, + crc32: self.crc32(), compressed_size: self - .compressed_size + .compressed_size() .min(spec::ZIP64_BYTES_THR) .try_into() .unwrap(), uncompressed_size: self - .uncompressed_size + .uncompressed_size() .min(spec::ZIP64_BYTES_THR) .try_into() .unwrap(), @@ -789,13 +805,13 @@ impl ZipFileData { pub(crate) fn zip64_extra_field_block(&self) -> Option { let uncompressed_size: Option = - if self.uncompressed_size > spec::ZIP64_BYTES_THR || self.large_file { + if self.uncompressed_size() > spec::ZIP64_BYTES_THR || self.large_file { Some(spec::ZIP64_BYTES_THR) } else { None }; let compressed_size: Option = - if self.compressed_size > spec::ZIP64_BYTES_THR || self.large_file { + if self.compressed_size() > spec::ZIP64_BYTES_THR || self.large_file { Some(spec::ZIP64_BYTES_THR) } else { None @@ -1039,9 +1055,11 @@ mod test { compression_method: crate::compression::CompressionMethod::Stored, compression_level: None, last_modified_time: None, - crc32: 0, - compressed_size: 0, - uncompressed_size: 0, + raw_values: ZipRawValues { + crc32: 0, + compressed_size: 0, + uncompressed_size: 0, + }, file_name: file_name.clone().into_boxed_str(), file_name_raw: file_name.into_bytes().into_boxed_slice(), extra_field: None, diff --git a/src/write.rs b/src/write.rs index 337b167c6..24379e8f6 100644 --- a/src/write.rs +++ b/src/write.rs @@ -548,12 +548,12 @@ impl ZipWriter { let src_index = self.index_by_name(src_name)?; let src_data = &self.files[src_index]; let data_start = *src_data.data_start.get().unwrap_or(&0); - let compressed_size = src_data.compressed_size; + let compressed_size = src_data.compressed_size(); debug_assert!(compressed_size <= write_position - data_start); - let uncompressed_size = src_data.uncompressed_size; + let uncompressed_size = src_data.uncompressed_size(); let raw_values = ZipRawValues { - crc32: src_data.crc32, + crc32: src_data.crc32(), compressed_size, uncompressed_size, }; @@ -925,13 +925,13 @@ impl ZipWriter { None => return Ok(()), Some((_, f)) => f, }; - file.uncompressed_size = self.stats.bytes_written; + file.raw_values.uncompressed_size = self.stats.bytes_written; let file_end = writer.stream_position()?; debug_assert!(file_end >= self.stats.start); - file.compressed_size = file_end - self.stats.start; + file.raw_values.compressed_size = file_end - self.stats.start; - file.crc32 = self.stats.hasher.clone().finalize(); + file.raw_values.crc32 = self.stats.hasher.clone().finalize(); if let Some(aes_mode) = &mut file.aes_mode { // We prefer using AE-1 which provides an extra CRC check, but for small files we // switch to AE-2 to prevent being able to use the CRC value to to reconstruct the @@ -939,7 +939,7 @@ impl ZipWriter { // // C.f. https://www.winzip.com/en/support/aes-encryption/#crc-faq aes_mode.1 = if self.stats.bytes_written < 20 { - file.crc32 = 0; + file.raw_values.crc32 = 0; AesVendorVersion::Ae2 } else { AesVendorVersion::Ae1 @@ -1740,20 +1740,20 @@ fn update_aes_extra_data( fn update_local_file_header(writer: &mut T, file: &ZipFileData) -> ZipResult<()> { const CRC32_OFFSET: u64 = 14; writer.seek(SeekFrom::Start(file.header_start + CRC32_OFFSET))?; - writer.write_u32_le(file.crc32)?; + writer.write_u32_le(file.crc32())?; if file.large_file { update_local_zip64_extra_field(writer, file)?; } else { // check compressed size as well as it can also be slightly larger than uncompressed size - if file.compressed_size > spec::ZIP64_BYTES_THR { + if file.compressed_size() > spec::ZIP64_BYTES_THR { return Err(ZipError::Io(io::Error::new( io::ErrorKind::Other, "Large file option has not been set", ))); } - writer.write_u32_le(file.compressed_size as u32)?; + writer.write_u32_le(file.compressed_size() as u32)?; // uncompressed size is already checked on write to catch it as soon as possible - writer.write_u32_le(file.uncompressed_size as u32)?; + writer.write_u32_le(file.uncompressed_size() as u32)?; } Ok(()) }