From 0482a1329a735c05056ef4e1077bee2fd1e8ba0a Mon Sep 17 00:00:00 2001 From: Chris Hennick <4961925+Pr0methean@users.noreply.github.com> Date: Thu, 9 May 2024 17:48:24 -0700 Subject: [PATCH] Fix bad mergee: write_local_file_header is now part of start_entry --- src/write.rs | 140 +++++---------------------------------------------- 1 file changed, 13 insertions(+), 127 deletions(-) diff --git a/src/write.rs b/src/write.rs index 8f85b4e4..ea95ac2f 100644 --- a/src/write.rs +++ b/src/write.rs @@ -815,15 +815,15 @@ impl ZipWriter { let index = self.insert_file_data(file)?; let file = &mut self.files[index]; let writer = self.inner.get_plain(); + // local file header signature writer.write_u32_le(spec::LOCAL_FILE_HEADER_SIGNATURE)?; // version needed to extract writer.write_u16_le(file.version_needed())?; // general purpose bit flag - let flag = if !file.file_name.is_ascii() { - 1u16 << 11 - } else { - 0 - } | if file.encrypted { 1u16 << 0 } else { 0 }; + let is_utf8 = std::str::from_utf8(&file.file_name_raw).is_ok(); + let is_ascii = file.file_name_raw.is_ascii(); + let flag = if is_utf8 && !is_ascii { 1u16 << 11 } else { 0 } + | if file.encrypted { 1u16 << 0 } else { 0 }; writer.write_u16_le(flag)?; // Compression method #[allow(deprecated)] @@ -842,89 +842,22 @@ impl ZipWriter { writer.write_u32_le(file.uncompressed_size as u32)?; } // file name length - writer.write_u16_le(file.file_name.as_bytes().len() as u16)?; + writer.write_u16_le(file.file_name_raw.len() as u16)?; // extra field length - let mut extra_field_length = file.extra_field_len(); - if file.large_file { - extra_field_length += 20; + let mut extra_field_length = if file.large_file { 20 } else { 0 }; + if let Some(field) = &file.extra_field { + extra_field_length += field.len(); } - if extra_field_length + file.central_extra_field_len() > u16::MAX as usize { - let _ = self.abort_file(); - return Err(InvalidArchive("Extra data field is too large")); + match extra_field_length.try_into() { + Ok(length_u16) => writer.write_u16_le(length_u16)?, + Err(_) => return Err(ZipError::InvalidArchive("Extra field is too long")), } - let extra_field_length = extra_field_length as u16; - writer.write_u16_le(extra_field_length)?; // file name - writer.write_all(file.file_name.as_bytes())?; + writer.write_all(&file.file_name_raw)?; // zip64 extra field if file.large_file { write_local_zip64_extra_field(writer, file)?; } - if let Some(extra_field) = &file.extra_field { - file.extra_data_start = Some(writer.stream_position()?); - writer.write_all(extra_field)?; - } - let mut header_end = writer.stream_position()?; - if options.alignment > 1 { - let align = options.alignment as u64; - let unaligned_header_bytes = header_end % align; - if unaligned_header_bytes != 0 { - let pad_length = (align - unaligned_header_bytes) as usize; - let Some(new_extra_field_length) = - (pad_length as u16).checked_add(extra_field_length) - else { - let _ = self.abort_file(); - return Err(InvalidArchive( - "Extra data field would be larger than allowed after aligning", - )); - }; - if pad_length >= 4 { - // Add an extra field to the extra_data - let pad_body = vec![0; pad_length - 4]; - writer.write_all(b"za").map_err(ZipError::from)?; // 0x617a - writer - .write_u16_le(pad_body.len() as u16) - .map_err(ZipError::from)?; - writer.write_all(&pad_body).map_err(ZipError::from)?; - } else { - // extra_data padding is too small for an extra field header, so pad with - // zeroes - let pad = vec![0; pad_length]; - writer.write_all(&pad).map_err(ZipError::from)?; - } - header_end = writer.stream_position()?; - - // Update extra field length in local file header. - writer.seek(SeekFrom::Start(file.header_start + 28))?; - writer.write_u16_le(new_extra_field_length)?; - writer.seek(SeekFrom::Start(header_end))?; - debug_assert_eq!(header_end % align, 0); - } - } - match options.encrypt_with { - #[cfg(feature = "aes-crypto")] - Some(EncryptWith::Aes { mode, password }) => { - let aeswriter = AesWriter::new( - mem::replace(&mut self.inner, GenericZipWriter::Closed).unwrap(), - mode, - password.as_bytes(), - )?; - self.inner = GenericZipWriter::Storer(MaybeEncrypted::Aes(aeswriter)); - } - Some(EncryptWith::ZipCrypto(keys, ..)) => { - let mut zipwriter = crate::zipcrypto::ZipCryptoWriter { - writer: mem::replace(&mut self.inner, Closed).unwrap(), - buffer: vec![], - keys, - }; - let crypto_header = [0u8; 12]; - - zipwriter.write_all(&crypto_header)?; - header_end = zipwriter.writer.stream_position()?; - self.inner = Storer(MaybeEncrypted::ZipCrypto(zipwriter)); - } - None => {} - } self.stats.start = header_end; debug_assert!(file.data_start.get().is_none()); file.data_start.get_or_init(|| header_end); @@ -1725,53 +1658,6 @@ fn clamp_opt>( } } -fn write_local_file_header(writer: &mut T, file: &ZipFileData) -> ZipResult<()> { - // local file header signature - writer.write_u32_le(spec::LOCAL_FILE_HEADER_SIGNATURE)?; - // version needed to extract - writer.write_u16_le(file.version_needed())?; - // general purpose bit flag - let is_utf8 = std::str::from_utf8(&file.file_name_raw).is_ok(); - let is_ascii = file.file_name_raw.is_ascii(); - let flag = if is_utf8 && !is_ascii { 1u16 << 11 } else { 0 } - | if file.encrypted { 1u16 << 0 } else { 0 }; - writer.write_u16_le(flag)?; - // Compression method - #[allow(deprecated)] - writer.write_u16_le(file.compression_method.to_u16())?; - // last mod file time and last mod file date - writer.write_u16_le(file.last_modified_time.timepart())?; - writer.write_u16_le(file.last_modified_time.datepart())?; - // crc-32 - writer.write_u32_le(file.crc32)?; - // compressed size and uncompressed size - if file.large_file { - writer.write_u32_le(spec::ZIP64_BYTES_THR as u32)?; - writer.write_u32_le(spec::ZIP64_BYTES_THR as u32)?; - } else { - writer.write_u32_le(file.compressed_size as u32)?; - writer.write_u32_le(file.uncompressed_size as u32)?; - } - // file name length - writer.write_u16_le(file.file_name_raw.len() as u16)?; - // extra field length - let mut extra_field_length = if file.large_file { 20 } else { 0 }; - if let Some(field) = &file.extra_field { - extra_field_length += field.len(); - } - match extra_field_length.try_into() { - Ok(length_u16) => writer.write_u16_le(length_u16)?, - Err(_) => return Err(ZipError::InvalidArchive("Extra field is too long")), - } - // file name - writer.write_all(&file.file_name_raw)?; - // zip64 extra field - if file.large_file { - write_local_zip64_extra_field(writer, file)?; - } - Ok(()) -} - fn update_aes_extra_data( writer: &mut W, file: &mut ZipFileData,