diff --git a/src/read.rs b/src/read.rs index 12791238..7bdb9fc6 100644 --- a/src/read.rs +++ b/src/read.rs @@ -727,17 +727,17 @@ fn parse_extra_field(file: &mut ZipFileData) -> ZipResult<()> { match kind { // Zip64 extended information extra field 0x0001 => { - if file.uncompressed_size == 0xFFFFFFFF { + if file.uncompressed_size == spec::ZIP64_BYTES_THR { file.large_file = true; file.uncompressed_size = reader.read_u64::()?; len_left -= 8; } - if file.compressed_size == 0xFFFFFFFF { + if file.compressed_size == spec::ZIP64_BYTES_THR { file.large_file = true; file.compressed_size = reader.read_u64::()?; len_left -= 8; } - if file.header_start == 0xFFFFFFFF { + if file.header_start == spec::ZIP64_BYTES_THR { file.header_start = reader.read_u64::()?; len_left -= 8; } diff --git a/src/spec.rs b/src/spec.rs index 4ab3656d..3ffcf732 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -9,6 +9,9 @@ const CENTRAL_DIRECTORY_END_SIGNATURE: u32 = 0x06054b50; pub const ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE: u32 = 0x06064b50; const ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE: u32 = 0x07064b50; +pub const ZIP64_BYTES_THR: u64 = u32::MAX as u64; +pub const ZIP64_ENTRY_THR: usize = u16::MAX as usize; + pub struct CentralDirectoryEnd { pub disk_number: u16, pub disk_with_central_directory: u16, diff --git a/src/write.rs b/src/write.rs index 457839bc..3d0e0170 100644 --- a/src/write.rs +++ b/src/write.rs @@ -216,7 +216,7 @@ impl Write for ZipWriter { let write_result = w.write(buf); if let Ok(count) = write_result { self.stats.update(&buf[0..count]); - if self.stats.bytes_written > 0xFFFFFFFF + if self.stats.bytes_written > spec::ZIP64_BYTES_THR && !self.files.last_mut().unwrap().large_file { let _inner = mem::replace(&mut self.inner, GenericZipWriter::Closed); @@ -647,6 +647,7 @@ impl ZipWriter { S: Into, { let mut options = FileOptions::default() + .large_file(file.compressed_size().max(file.size()) > spec::ZIP64_BYTES_THR) .last_modified_time(file.last_modified()) .compression_method(file.compression()); if let Some(perms) = file.unix_mode() { @@ -759,7 +760,8 @@ impl ZipWriter { } let central_size = writer.seek(io::SeekFrom::Current(0))? - central_start; - if self.files.len() > 0xFFFF || central_size > 0xFFFFFFFF || central_start > 0xFFFFFFFF + if self.files.len() > spec::ZIP64_ENTRY_THR + || central_size.max(central_start) > spec::ZIP64_BYTES_THR { let zip64_footer = spec::Zip64CentralDirectoryEnd { version_made_by: DEFAULT_VERSION as u16, @@ -783,27 +785,15 @@ impl ZipWriter { zip64_footer.write(writer)?; } - let number_of_files = if self.files.len() > 0xFFFF { - 0xFFFF - } else { - self.files.len() as u16 - }; + let number_of_files = self.files.len().min(spec::ZIP64_ENTRY_THR) as u16; let footer = spec::CentralDirectoryEnd { disk_number: 0, disk_with_central_directory: 0, zip_file_comment: self.comment.clone(), number_of_files_on_this_disk: number_of_files, number_of_files, - central_directory_size: if central_size > 0xFFFFFFFF { - 0xFFFFFFFF - } else { - central_size as u32 - }, - central_directory_offset: if central_start > 0xFFFFFFFF { - 0xFFFFFFFF - } else { - central_start as u32 - }, + central_directory_size: central_size.min(spec::ZIP64_BYTES_THR) as u32, + central_directory_offset: central_start.min(spec::ZIP64_BYTES_THR) as u32, }; footer.write(writer)?; @@ -866,7 +856,7 @@ impl GenericZipWriter { #[allow(deprecated)] match compression { CompressionMethod::Stored => { - if let Some(_) = compression_level { + if compression_level.is_some() { return Err(ZipError::UnsupportedArchive( "Unsupported compression level", )); @@ -917,7 +907,7 @@ impl GenericZipWriter { bare, clamp_opt( compression_level.unwrap_or(zstd::DEFAULT_COMPRESSION_LEVEL), - zstd::compression_level_range().clone(), + zstd::compression_level_range(), ) .ok_or(ZipError::UnsupportedArchive( "Unsupported compression level", @@ -1027,18 +1017,14 @@ fn write_local_file_header(writer: &mut T, file: &ZipFileData) -> ZipR writer.write_u16::(file.last_modified_time.datepart())?; // crc-32 writer.write_u32::(file.crc32)?; - // compressed size - writer.write_u32::(if file.compressed_size > 0xFFFFFFFF { - 0xFFFFFFFF + // compressed size and uncompressed size + if file.large_file { + writer.write_u32::(spec::ZIP64_BYTES_THR as u32)?; + writer.write_u32::(spec::ZIP64_BYTES_THR as u32)?; } else { - file.compressed_size as u32 - })?; - // uncompressed size - writer.write_u32::(if file.uncompressed_size > 0xFFFFFFFF { - 0xFFFFFFFF - } else { - file.uncompressed_size as u32 - })?; + writer.write_u32::(file.compressed_size as u32)?; + writer.write_u32::(file.uncompressed_size as u32)?; + } // file name length writer.write_u16::(file.file_name.as_bytes().len() as u16)?; // extra field length @@ -1061,27 +1047,19 @@ fn update_local_file_header( const CRC32_OFFSET: u64 = 14; writer.seek(io::SeekFrom::Start(file.header_start + CRC32_OFFSET))?; writer.write_u32::(file.crc32)?; - writer.write_u32::(if file.compressed_size > 0xFFFFFFFF { - if file.large_file { - 0xFFFFFFFF - } else { - // compressed size can be slightly larger than uncompressed size + 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 { return Err(ZipError::Io(io::Error::new( io::ErrorKind::Other, "Large file option has not been set", ))); } - } else { - file.compressed_size as u32 - })?; - writer.write_u32::(if file.uncompressed_size > 0xFFFFFFFF { - // uncompressed size is checked on write to catch it as soon as possible - 0xFFFFFFFF - } else { - file.uncompressed_size as u32 - })?; - if file.large_file { - update_local_zip64_extra_field(writer, file)?; + writer.write_u32::(file.compressed_size as u32)?; + // uncompressed size is already checked on write to catch it as soon as possible + writer.write_u32::(file.uncompressed_size as u32)?; } Ok(()) } @@ -1115,17 +1093,9 @@ fn write_central_directory_header(writer: &mut T, file: &ZipFileData) // crc-32 writer.write_u32::(file.crc32)?; // compressed size - writer.write_u32::(if file.compressed_size > 0xFFFFFFFF { - 0xFFFFFFFF - } else { - file.compressed_size as u32 - })?; + writer.write_u32::(file.compressed_size.min(spec::ZIP64_BYTES_THR) as u32)?; // uncompressed size - writer.write_u32::(if file.uncompressed_size > 0xFFFFFFFF { - 0xFFFFFFFF - } else { - file.uncompressed_size as u32 - })?; + writer.write_u32::(file.uncompressed_size.min(spec::ZIP64_BYTES_THR) as u32)?; // file name length writer.write_u16::(file.file_name.as_bytes().len() as u16)?; // extra field length @@ -1139,11 +1109,7 @@ fn write_central_directory_header(writer: &mut T, file: &ZipFileData) // external file attributes writer.write_u32::(file.external_attributes)?; // relative offset of local header - writer.write_u32::(if file.header_start > 0xFFFFFFFF { - 0xFFFFFFFF - } else { - file.header_start as u32 - })?; + writer.write_u32::(file.header_start.min(spec::ZIP64_BYTES_THR) as u32)?; // file name writer.write_all(file.file_name.as_bytes())?; // zip64 extra field @@ -1159,7 +1125,7 @@ fn write_central_directory_header(writer: &mut T, file: &ZipFileData) fn validate_extra_data(file: &ZipFileData) -> ZipResult<()> { let mut data = file.extra_field.as_slice(); - if data.len() > 0xFFFF { + if data.len() > spec::ZIP64_ENTRY_THR { return Err(ZipError::Io(io::Error::new( io::ErrorKind::InvalidData, "Extra data exceeds extra field", @@ -1242,9 +1208,9 @@ fn write_central_zip64_extra_field(writer: &mut T, file: &ZipFileData) // only appear if the corresponding Local or Central // directory record field is set to 0xFFFF or 0xFFFFFFFF. let mut size = 0; - let uncompressed_size = file.uncompressed_size > 0xFFFFFFFF; - let compressed_size = file.compressed_size > 0xFFFFFFFF; - let header_start = file.header_start > 0xFFFFFFFF; + let uncompressed_size = file.uncompressed_size > spec::ZIP64_BYTES_THR; + let compressed_size = file.compressed_size > spec::ZIP64_BYTES_THR; + let header_start = file.header_start > spec::ZIP64_BYTES_THR; if uncompressed_size { size += 8; }