Merge branch 'master' into oldpr384

This commit is contained in:
Chris Hennick 2024-05-03 11:57:18 -07:00 committed by GitHub
commit 9442a639fb
Signed by: DevComp
GPG key ID: B5690EEEBB952194
12 changed files with 285 additions and 179 deletions

View file

@ -6,7 +6,6 @@ on:
- 'master' - 'master'
push: push:
branches-ignore: branches-ignore:
- 'release-plz-**'
- 'gh-readonly-queue/**' - 'gh-readonly-queue/**'
workflow_dispatch: workflow_dispatch:
merge_group: merge_group:
@ -22,6 +21,7 @@ jobs:
matrix: matrix:
os: [ubuntu-latest, macOS-latest, windows-latest] os: [ubuntu-latest, macOS-latest, windows-latest]
rustalias: [stable, nightly, msrv] rustalias: [stable, nightly, msrv]
feature_flag: ["--all-features", "--no-default-features", ""]
include: include:
- rustalias: stable - rustalias: stable
rust: stable rust: stable
@ -29,7 +29,7 @@ jobs:
rust: '1.70' rust: '1.70'
- rustalias: nightly - rustalias: nightly
rust: nightly rust: nightly
name: 'Build and test: ${{ matrix.os }}, ${{ matrix.rustalias }}' name: 'Build and test ${{ matrix.feature_flag }}: ${{ matrix.os }}, ${{ matrix.rustalias }}'
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@master
@ -44,19 +44,13 @@ jobs:
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: check command: check
args: --all --bins --examples args: --all ${{ matrix.feature_flag }} --bins --examples
- name: Tests - name: Tests
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --all args: --all ${{ matrix.feature_flag }}
- name: Tests (no features)
uses: actions-rs/cargo@v1
with:
command: test
args: --all --no-default-features
style_and_docs: style_and_docs:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name

View file

@ -23,7 +23,6 @@ time = { version = "0.3.36", default-features = false }
[dependencies] [dependencies]
aes = { version = "0.8.4", optional = true } aes = { version = "0.8.4", optional = true }
byteorder = "1.5.0"
bzip2 = { version = "0.4.4", optional = true } bzip2 = { version = "0.4.4", optional = true }
chrono = { version = "0.4.38", optional = true } chrono = { version = "0.4.38", optional = true }
constant_time_eq = { version = "0.3.0", optional = true } constant_time_eq = { version = "0.3.0", optional = true }

View file

@ -70,6 +70,7 @@ See the [examples directory](examples) for:
* How to extract a zip file. * How to extract a zip file.
* How to extract a single file from a zip. * How to extract a single file from a zip.
* How to read a zip from the standard input. * How to read a zip from the standard input.
* How to append a directory to an existing archive
Fuzzing Fuzzing
------- -------

63
examples/append.rs Normal file
View file

@ -0,0 +1,63 @@
use std::{
fs::{File, OpenOptions},
path::{Path, PathBuf},
str::FromStr,
};
use zip::write::SimpleFileOptions;
fn gather_files<'a, T: Into<&'a Path>>(path: T, files: &mut Vec<PathBuf>) {
let path: &Path = path.into();
for entry in path.read_dir().unwrap() {
match entry {
Ok(e) => {
if e.path().is_dir() {
gather_files(e.path().as_ref(), files);
} else if e.path().is_file() {
files.push(e.path());
}
}
Err(_) => todo!(),
}
}
}
fn real_main() -> i32 {
let args: Vec<_> = std::env::args().collect();
if args.len() < 3 {
println!("Usage: {} <existing archive> <folder_to_append>", args[0]);
return 1;
}
let existing_archive_path = &*args[1];
let append_dir_path = &*args[2];
let archive = PathBuf::from_str(existing_archive_path).unwrap();
let to_append = PathBuf::from_str(append_dir_path).unwrap();
let existing_zip = OpenOptions::new()
.read(true)
.write(true)
.open(archive)
.unwrap();
let mut append_zip = zip::ZipWriter::new_append(existing_zip).unwrap();
let mut files: Vec<PathBuf> = vec![];
gather_files(to_append.as_ref(), &mut files);
for file in files {
append_zip
.start_file(file.to_string_lossy(), SimpleFileOptions::default())
.unwrap();
let mut f = File::open(file).unwrap();
let _ = std::io::copy(&mut f, &mut append_zip);
}
append_zip.finish().unwrap();
0
}
fn main() {
std::process::exit(real_main());
}

View file

@ -4,9 +4,9 @@
//! different byte order (little endian) than NIST (big endian). //! different byte order (little endian) than NIST (big endian).
//! See [AesCtrZipKeyStream] for more information. //! See [AesCtrZipKeyStream] for more information.
use crate::unstable::LittleEndianWriteExt;
use aes::cipher::generic_array::GenericArray; use aes::cipher::generic_array::GenericArray;
use aes::cipher::{BlockEncrypt, KeyInit}; use aes::cipher::{BlockEncrypt, KeyInit};
use byteorder::WriteBytesExt;
use std::{any, fmt}; use std::{any, fmt};
/// Internal block size of an AES cipher. /// Internal block size of an AES cipher.
@ -112,7 +112,7 @@ where
// Note: AES block size is always 16 bytes, same as u128. // Note: AES block size is always 16 bytes, same as u128.
self.buffer self.buffer
.as_mut() .as_mut()
.write_u128::<byteorder::LittleEndian>(self.counter) .write_u128_le(self.counter)
.expect("did not expect u128 le conversion to fail"); .expect("did not expect u128 le conversion to fail");
self.cipher self.cipher
.encrypt_block(GenericArray::from_mut_slice(&mut self.buffer)); .encrypt_block(GenericArray::from_mut_slice(&mut self.buffer));
@ -154,7 +154,7 @@ mod tests {
/// Checks whether `crypt_in_place` produces the correct plaintext after one use and yields the /// Checks whether `crypt_in_place` produces the correct plaintext after one use and yields the
/// cipertext again after applying it again. /// cipertext again after applying it again.
fn roundtrip<Aes>(key: &[u8], ciphertext: &mut [u8], expected_plaintext: &[u8]) fn roundtrip<Aes>(key: &[u8], ciphertext: &[u8], expected_plaintext: &[u8])
where where
Aes: AesKind, Aes: AesKind,
Aes::Cipher: KeyInit + BlockEncrypt, Aes::Cipher: KeyInit + BlockEncrypt,
@ -182,7 +182,7 @@ mod tests {
// `7z a -phelloworld -mem=AES256 -mx=0 aes256_40byte.zip 40byte_data.txt` // `7z a -phelloworld -mem=AES256 -mx=0 aes256_40byte.zip 40byte_data.txt`
#[test] #[test]
fn crypt_aes_256_0_byte() { fn crypt_aes_256_0_byte() {
let mut ciphertext = []; let ciphertext = [];
let expected_plaintext = &[]; let expected_plaintext = &[];
let key = [ let key = [
0x0b, 0xec, 0x2e, 0xf2, 0x46, 0xf0, 0x7e, 0x35, 0x16, 0x54, 0xe0, 0x98, 0x10, 0xb3, 0x0b, 0xec, 0x2e, 0xf2, 0x46, 0xf0, 0x7e, 0x35, 0x16, 0x54, 0xe0, 0x98, 0x10, 0xb3,
@ -190,36 +190,36 @@ mod tests {
0x5c, 0xd0, 0xc0, 0x54, 0x5c, 0xd0, 0xc0, 0x54,
]; ];
roundtrip::<Aes256>(&key, &mut ciphertext, expected_plaintext); roundtrip::<Aes256>(&key, &ciphertext, expected_plaintext);
} }
#[test] #[test]
fn crypt_aes_128_5_byte() { fn crypt_aes_128_5_byte() {
let mut ciphertext = [0x98, 0xa9, 0x8c, 0x26, 0x0e]; let ciphertext = [0x98, 0xa9, 0x8c, 0x26, 0x0e];
let expected_plaintext = b"asdf\n"; let expected_plaintext = b"asdf\n";
let key = [ let key = [
0xe0, 0x25, 0x7b, 0x57, 0x97, 0x6a, 0xa4, 0x23, 0xab, 0x94, 0xaa, 0x44, 0xfd, 0x47, 0xe0, 0x25, 0x7b, 0x57, 0x97, 0x6a, 0xa4, 0x23, 0xab, 0x94, 0xaa, 0x44, 0xfd, 0x47,
0x4f, 0xa5, 0x4f, 0xa5,
]; ];
roundtrip::<Aes128>(&key, &mut ciphertext, expected_plaintext); roundtrip::<Aes128>(&key, &ciphertext, expected_plaintext);
} }
#[test] #[test]
fn crypt_aes_192_5_byte() { fn crypt_aes_192_5_byte() {
let mut ciphertext = [0x36, 0x55, 0x5c, 0x61, 0x3c]; let ciphertext = [0x36, 0x55, 0x5c, 0x61, 0x3c];
let expected_plaintext = b"asdf\n"; let expected_plaintext = b"asdf\n";
let key = [ let key = [
0xe4, 0x4a, 0x88, 0x52, 0x8f, 0xf7, 0x0b, 0x81, 0x7b, 0x75, 0xf1, 0x74, 0x21, 0x37, 0xe4, 0x4a, 0x88, 0x52, 0x8f, 0xf7, 0x0b, 0x81, 0x7b, 0x75, 0xf1, 0x74, 0x21, 0x37,
0x8c, 0x90, 0xad, 0xbe, 0x4a, 0x65, 0xa8, 0x96, 0x0e, 0xcc, 0x8c, 0x90, 0xad, 0xbe, 0x4a, 0x65, 0xa8, 0x96, 0x0e, 0xcc,
]; ];
roundtrip::<Aes192>(&key, &mut ciphertext, expected_plaintext); roundtrip::<Aes192>(&key, &ciphertext, expected_plaintext);
} }
#[test] #[test]
fn crypt_aes_256_5_byte() { fn crypt_aes_256_5_byte() {
let mut ciphertext = [0xc2, 0x47, 0xc0, 0xdc, 0x56]; let ciphertext = [0xc2, 0x47, 0xc0, 0xdc, 0x56];
let expected_plaintext = b"asdf\n"; let expected_plaintext = b"asdf\n";
let key = [ let key = [
0x79, 0x5e, 0x17, 0xf2, 0xc6, 0x3d, 0x28, 0x9b, 0x4b, 0x4b, 0xbb, 0xa9, 0xba, 0xc9, 0x79, 0x5e, 0x17, 0xf2, 0xc6, 0x3d, 0x28, 0x9b, 0x4b, 0x4b, 0xbb, 0xa9, 0xba, 0xc9,
@ -227,12 +227,12 @@ mod tests {
0x15, 0xb2, 0x86, 0xab, 0x15, 0xb2, 0x86, 0xab,
]; ];
roundtrip::<Aes256>(&key, &mut ciphertext, expected_plaintext); roundtrip::<Aes256>(&key, &ciphertext, expected_plaintext);
} }
#[test] #[test]
fn crypt_aes_128_40_byte() { fn crypt_aes_128_40_byte() {
let mut ciphertext = [ let ciphertext = [
0xcf, 0x72, 0x6b, 0xa1, 0xb2, 0x0f, 0xdf, 0xaa, 0x10, 0xad, 0x9c, 0x7f, 0x6d, 0x1c, 0xcf, 0x72, 0x6b, 0xa1, 0xb2, 0x0f, 0xdf, 0xaa, 0x10, 0xad, 0x9c, 0x7f, 0x6d, 0x1c,
0x8d, 0xb5, 0x16, 0x7e, 0xbb, 0x11, 0x69, 0x52, 0x8c, 0x89, 0x80, 0x32, 0xaa, 0x76, 0x8d, 0xb5, 0x16, 0x7e, 0xbb, 0x11, 0x69, 0x52, 0x8c, 0x89, 0x80, 0x32, 0xaa, 0x76,
0xa6, 0x18, 0x31, 0x98, 0xee, 0xdd, 0x22, 0x68, 0xb7, 0xe6, 0x77, 0xd2, 0xa6, 0x18, 0x31, 0x98, 0xee, 0xdd, 0x22, 0x68, 0xb7, 0xe6, 0x77, 0xd2,
@ -243,12 +243,12 @@ mod tests {
0x81, 0xb6, 0x81, 0xb6,
]; ];
roundtrip::<Aes128>(&key, &mut ciphertext, expected_plaintext); roundtrip::<Aes128>(&key, &ciphertext, expected_plaintext);
} }
#[test] #[test]
fn crypt_aes_192_40_byte() { fn crypt_aes_192_40_byte() {
let mut ciphertext = [ let ciphertext = [
0xa6, 0xfc, 0x52, 0x79, 0x2c, 0x6c, 0xfe, 0x68, 0xb1, 0xa8, 0xb3, 0x07, 0x52, 0x8b, 0xa6, 0xfc, 0x52, 0x79, 0x2c, 0x6c, 0xfe, 0x68, 0xb1, 0xa8, 0xb3, 0x07, 0x52, 0x8b,
0x82, 0xa6, 0x87, 0x9c, 0x72, 0x42, 0x3a, 0xf8, 0xc6, 0xa9, 0xc9, 0xfb, 0x61, 0x19, 0x82, 0xa6, 0x87, 0x9c, 0x72, 0x42, 0x3a, 0xf8, 0xc6, 0xa9, 0xc9, 0xfb, 0x61, 0x19,
0x37, 0xb9, 0x56, 0x62, 0xf4, 0xfc, 0x5e, 0x7a, 0xdd, 0x55, 0x0a, 0x48, 0x37, 0xb9, 0x56, 0x62, 0xf4, 0xfc, 0x5e, 0x7a, 0xdd, 0x55, 0x0a, 0x48,
@ -259,12 +259,12 @@ mod tests {
0xfe, 0xae, 0x1b, 0xba, 0x01, 0x97, 0x97, 0x79, 0xbb, 0xa6, 0xfe, 0xae, 0x1b, 0xba, 0x01, 0x97, 0x97, 0x79, 0xbb, 0xa6,
]; ];
roundtrip::<Aes192>(&key, &mut ciphertext, expected_plaintext); roundtrip::<Aes192>(&key, &ciphertext, expected_plaintext);
} }
#[test] #[test]
fn crypt_aes_256_40_byte() { fn crypt_aes_256_40_byte() {
let mut ciphertext = [ let ciphertext = [
0xa9, 0x99, 0xbd, 0xea, 0x82, 0x9b, 0x8f, 0x2f, 0xb7, 0x52, 0x2f, 0x6b, 0xd8, 0xf6, 0xa9, 0x99, 0xbd, 0xea, 0x82, 0x9b, 0x8f, 0x2f, 0xb7, 0x52, 0x2f, 0x6b, 0xd8, 0xf6,
0xab, 0x0e, 0x24, 0x51, 0x9e, 0x18, 0x0f, 0xc0, 0x8f, 0x54, 0x15, 0x80, 0xae, 0xbc, 0xab, 0x0e, 0x24, 0x51, 0x9e, 0x18, 0x0f, 0xc0, 0x8f, 0x54, 0x15, 0x80, 0xae, 0xbc,
0xa0, 0x5c, 0x8a, 0x11, 0x8d, 0x14, 0x7e, 0xc5, 0xb4, 0xae, 0xd3, 0x37, 0xa0, 0x5c, 0x8a, 0x11, 0x8d, 0x14, 0x7e, 0xc5, 0xb4, 0xae, 0xd3, 0x37,
@ -276,6 +276,6 @@ mod tests {
0xc2, 0x07, 0x36, 0xb6, 0xc2, 0x07, 0x36, 0xb6,
]; ];
roundtrip::<Aes256>(&key, &mut ciphertext, expected_plaintext); roundtrip::<Aes256>(&key, &ciphertext, expected_plaintext);
} }
} }

View file

@ -1,9 +1,6 @@
use std::io::Read;
use byteorder::LittleEndian;
use byteorder::ReadBytesExt;
use crate::result::{ZipError, ZipResult}; use crate::result::{ZipError, ZipResult};
use crate::unstable::LittleEndianReadExt;
use std::io::Read;
/// extended timestamp, as described in <https://libzip.org/specifications/extrafld.txt> /// extended timestamp, as described in <https://libzip.org/specifications/extrafld.txt>
@ -23,7 +20,9 @@ impl ExtendedTimestamp {
where where
R: Read, R: Read,
{ {
let flags = reader.read_u8()?; let mut flags = [0u8];
reader.read_exact(&mut flags)?;
let flags = flags[0];
// the `flags` field refers to the local headers and might not correspond // the `flags` field refers to the local headers and might not correspond
// to the len field. If the length field is 1+4, we assume that only // to the len field. If the length field is 1+4, we assume that only
@ -48,19 +47,19 @@ impl ExtendedTimestamp {
} }
let mod_time = if (flags & 0b00000001u8 == 0b00000001u8) || len == 5 { let mod_time = if (flags & 0b00000001u8 == 0b00000001u8) || len == 5 {
Some(reader.read_u32::<LittleEndian>()?) Some(reader.read_u32_le()?)
} else { } else {
None None
}; };
let ac_time = if flags & 0b00000010u8 == 0b00000010u8 && len > 5 { let ac_time = if flags & 0b00000010u8 == 0b00000010u8 && len > 5 {
Some(reader.read_u32::<LittleEndian>()?) Some(reader.read_u32_le()?)
} else { } else {
None None
}; };
let cr_time = if flags & 0b00000100u8 == 0b00000100u8 && len > 5 { let cr_time = if flags & 0b00000100u8 == 0b00000100u8 && len > 5 {
Some(reader.read_u32::<LittleEndian>()?) Some(reader.read_u32_le()?)
} else { } else {
None None
}; };

View file

@ -11,7 +11,6 @@ use crate::result::{ZipError, ZipResult};
use crate::spec; use crate::spec;
use crate::types::{AesMode, AesVendorVersion, DateTime, System, ZipFileData}; use crate::types::{AesMode, AesVendorVersion, DateTime, System, ZipFileData};
use crate::zipcrypto::{ZipCryptoReader, ZipCryptoReaderValid, ZipCryptoValidator}; use crate::zipcrypto::{ZipCryptoReader, ZipCryptoReaderValid, ZipCryptoValidator};
use byteorder::{LittleEndian, ReadBytesExt};
use std::borrow::{Borrow, Cow}; use std::borrow::{Borrow, Cow};
use std::collections::HashMap; use std::collections::HashMap;
use std::io::{self, prelude::*}; use std::io::{self, prelude::*};
@ -86,6 +85,7 @@ pub(crate) mod zip_archive {
use crate::read::lzma::LzmaDecoder; use crate::read::lzma::LzmaDecoder;
use crate::result::ZipError::InvalidPassword; use crate::result::ZipError::InvalidPassword;
use crate::spec::path_to_string; use crate::spec::path_to_string;
use crate::unstable::LittleEndianReadExt;
pub use zip_archive::ZipArchive; pub use zip_archive::ZipArchive;
#[allow(clippy::large_enum_variant)] #[allow(clippy::large_enum_variant)]
@ -209,15 +209,15 @@ pub(crate) fn find_content<'a>(
) -> ZipResult<io::Take<&'a mut dyn Read>> { ) -> ZipResult<io::Take<&'a mut dyn Read>> {
// Parse local header // Parse local header
reader.seek(io::SeekFrom::Start(data.header_start))?; reader.seek(io::SeekFrom::Start(data.header_start))?;
let signature = reader.read_u32::<LittleEndian>()?; let signature = reader.read_u32_le()?;
if signature != spec::LOCAL_FILE_HEADER_SIGNATURE { if signature != spec::LOCAL_FILE_HEADER_SIGNATURE {
return Err(ZipError::InvalidArchive("Invalid local file header")); return Err(ZipError::InvalidArchive("Invalid local file header"));
} }
let data_start = match data.data_start.get() { let data_start = match data.data_start.get() {
None => { None => {
reader.seek(io::SeekFrom::Current(22))?; reader.seek(io::SeekFrom::Current(22))?;
let file_name_length = reader.read_u16::<LittleEndian>()? as u64; let file_name_length = reader.read_u16_le()? as u64;
let extra_field_length = reader.read_u16::<LittleEndian>()? as u64; let extra_field_length = reader.read_u16_le()? as u64;
let magic_and_header = 4 + 22 + 2 + 2; let magic_and_header = 4 + 22 + 2 + 2;
let data_start = let data_start =
data.header_start + magic_and_header + file_name_length + extra_field_length; data.header_start + magic_and_header + file_name_length + extra_field_length;
@ -854,7 +854,7 @@ pub(crate) fn central_header_to_zip_file<R: Read + Seek>(
let central_header_start = reader.stream_position()?; let central_header_start = reader.stream_position()?;
// Parse central header // Parse central header
let signature = reader.read_u32::<LittleEndian>()?; let signature = reader.read_u32_le()?;
if signature != spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE { if signature != spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE {
Err(ZipError::InvalidArchive("Invalid Central Directory header")) Err(ZipError::InvalidArchive("Invalid Central Directory header"))
} else { } else {
@ -868,25 +868,25 @@ fn central_header_to_zip_file_inner<R: Read>(
archive_offset: u64, archive_offset: u64,
central_header_start: u64, central_header_start: u64,
) -> ZipResult<ZipFileData> { ) -> ZipResult<ZipFileData> {
let version_made_by = reader.read_u16::<LittleEndian>()?; let version_made_by = reader.read_u16_le()?;
let _version_to_extract = reader.read_u16::<LittleEndian>()?; let _version_to_extract = reader.read_u16_le()?;
let flags = reader.read_u16::<LittleEndian>()?; let flags = reader.read_u16_le()?;
let encrypted = flags & 1 == 1; let encrypted = flags & 1 == 1;
let is_utf8 = flags & (1 << 11) != 0; let is_utf8 = flags & (1 << 11) != 0;
let using_data_descriptor = flags & (1 << 3) != 0; let using_data_descriptor = flags & (1 << 3) != 0;
let compression_method = reader.read_u16::<LittleEndian>()?; let compression_method = reader.read_u16_le()?;
let last_mod_time = reader.read_u16::<LittleEndian>()?; let last_mod_time = reader.read_u16_le()?;
let last_mod_date = reader.read_u16::<LittleEndian>()?; let last_mod_date = reader.read_u16_le()?;
let crc32 = reader.read_u32::<LittleEndian>()?; let crc32 = reader.read_u32_le()?;
let compressed_size = reader.read_u32::<LittleEndian>()?; let compressed_size = reader.read_u32_le()?;
let uncompressed_size = reader.read_u32::<LittleEndian>()?; let uncompressed_size = reader.read_u32_le()?;
let file_name_length = reader.read_u16::<LittleEndian>()? as usize; let file_name_length = reader.read_u16_le()? as usize;
let extra_field_length = reader.read_u16::<LittleEndian>()? as usize; let extra_field_length = reader.read_u16_le()? as usize;
let file_comment_length = reader.read_u16::<LittleEndian>()? as usize; let file_comment_length = reader.read_u16_le()? as usize;
let _disk_number = reader.read_u16::<LittleEndian>()?; let _disk_number = reader.read_u16_le()?;
let _internal_file_attributes = reader.read_u16::<LittleEndian>()?; let _internal_file_attributes = reader.read_u16_le()?;
let external_file_attributes = reader.read_u32::<LittleEndian>()?; let external_file_attributes = reader.read_u32_le()?;
let offset = reader.read_u32::<LittleEndian>()? as u64; let offset = reader.read_u32_le()? as u64;
let mut file_name_raw = vec![0; file_name_length]; let mut file_name_raw = vec![0; file_name_length];
reader.read_exact(&mut file_name_raw)?; reader.read_exact(&mut file_name_raw)?;
let mut extra_field = vec![0; extra_field_length]; let mut extra_field = vec![0; extra_field_length];
@ -960,24 +960,24 @@ fn parse_extra_field(file: &mut ZipFileData) -> ZipResult<()> {
let mut reader = io::Cursor::new(extra_field.as_ref()); let mut reader = io::Cursor::new(extra_field.as_ref());
while (reader.position() as usize) < extra_field.len() { while (reader.position() as usize) < extra_field.len() {
let kind = reader.read_u16::<LittleEndian>()?; let kind = reader.read_u16_le()?;
let len = reader.read_u16::<LittleEndian>()?; let len = reader.read_u16_le()?;
let mut len_left = len as i64; let mut len_left = len as i64;
match kind { match kind {
// Zip64 extended information extra field // Zip64 extended information extra field
0x0001 => { 0x0001 => {
if file.uncompressed_size == spec::ZIP64_BYTES_THR { if file.uncompressed_size == spec::ZIP64_BYTES_THR {
file.large_file = true; file.large_file = true;
file.uncompressed_size = reader.read_u64::<LittleEndian>()?; file.uncompressed_size = reader.read_u64_le()?;
len_left -= 8; len_left -= 8;
} }
if file.compressed_size == spec::ZIP64_BYTES_THR { if file.compressed_size == spec::ZIP64_BYTES_THR {
file.large_file = true; file.large_file = true;
file.compressed_size = reader.read_u64::<LittleEndian>()?; file.compressed_size = reader.read_u64_le()?;
len_left -= 8; len_left -= 8;
} }
if file.header_start == spec::ZIP64_BYTES_THR { if file.header_start == spec::ZIP64_BYTES_THR {
file.header_start = reader.read_u64::<LittleEndian>()?; file.header_start = reader.read_u64_le()?;
len_left -= 8; len_left -= 8;
} }
} }
@ -988,10 +988,12 @@ fn parse_extra_field(file: &mut ZipFileData) -> ZipResult<()> {
"AES extra data field has an unsupported length", "AES extra data field has an unsupported length",
)); ));
} }
let vendor_version = reader.read_u16::<LittleEndian>()?; let vendor_version = reader.read_u16_le()?;
let vendor_id = reader.read_u16::<LittleEndian>()?; let vendor_id = reader.read_u16_le()?;
let aes_mode = reader.read_u8()?; let mut out = [0u8];
let compression_method = reader.read_u16::<LittleEndian>()?; reader.read_exact(&mut out)?;
let aes_mode = out[0];
let compression_method = reader.read_u16_le()?;
if vendor_id != 0x4541 { if vendor_id != 0x4541 {
return Err(ZipError::InvalidArchive("Invalid AES vendor")); return Err(ZipError::InvalidArchive("Invalid AES vendor"));
@ -1255,7 +1257,7 @@ impl<'a> Drop for ZipFile<'a> {
/// * `data_start`: set to 0 /// * `data_start`: set to 0
/// * `external_attributes`: `unix_mode()`: will return None /// * `external_attributes`: `unix_mode()`: will return None
pub fn read_zipfile_from_stream<'a, R: Read>(reader: &'a mut R) -> ZipResult<Option<ZipFile<'_>>> { pub fn read_zipfile_from_stream<'a, R: Read>(reader: &'a mut R) -> ZipResult<Option<ZipFile<'_>>> {
let signature = reader.read_u32::<LittleEndian>()?; let signature = reader.read_u32_le()?;
match signature { match signature {
spec::LOCAL_FILE_HEADER_SIGNATURE => (), spec::LOCAL_FILE_HEADER_SIGNATURE => (),
@ -1263,20 +1265,20 @@ pub fn read_zipfile_from_stream<'a, R: Read>(reader: &'a mut R) -> ZipResult<Opt
_ => return Err(ZipError::InvalidArchive("Invalid local file header")), _ => return Err(ZipError::InvalidArchive("Invalid local file header")),
} }
let version_made_by = reader.read_u16::<LittleEndian>()?; let version_made_by = reader.read_u16_le()?;
let flags = reader.read_u16::<LittleEndian>()?; let flags = reader.read_u16_le()?;
let encrypted = flags & 1 == 1; let encrypted = flags & 1 == 1;
let is_utf8 = flags & (1 << 11) != 0; let is_utf8 = flags & (1 << 11) != 0;
let using_data_descriptor = flags & (1 << 3) != 0; let using_data_descriptor = flags & (1 << 3) != 0;
#[allow(deprecated)] #[allow(deprecated)]
let compression_method = CompressionMethod::from_u16(reader.read_u16::<LittleEndian>()?); let compression_method = CompressionMethod::from_u16(reader.read_u16_le()?);
let last_mod_time = reader.read_u16::<LittleEndian>()?; let last_mod_time = reader.read_u16_le()?;
let last_mod_date = reader.read_u16::<LittleEndian>()?; let last_mod_date = reader.read_u16_le()?;
let crc32 = reader.read_u32::<LittleEndian>()?; let crc32 = reader.read_u32_le()?;
let compressed_size = reader.read_u32::<LittleEndian>()?; let compressed_size = reader.read_u32_le()?;
let uncompressed_size = reader.read_u32::<LittleEndian>()?; let uncompressed_size = reader.read_u32_le()?;
let file_name_length = reader.read_u16::<LittleEndian>()? as usize; let file_name_length = reader.read_u16_le()? as usize;
let extra_field_length = reader.read_u16::<LittleEndian>()? as usize; let extra_field_length = reader.read_u16_le()? as usize;
let mut file_name_raw = vec![0; file_name_length]; let mut file_name_raw = vec![0; file_name_length];
reader.read_exact(&mut file_name_raw)?; reader.read_exact(&mut file_name_raw)?;

View file

@ -1,3 +1,4 @@
use crate::unstable::LittleEndianReadExt;
use std::fs; use std::fs;
use std::io::{self, Read}; use std::io::{self, Read};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -7,8 +8,6 @@ use super::{
ZipFileData, ZipResult, ZipFileData, ZipResult,
}; };
use byteorder::{LittleEndian, ReadBytesExt};
/// Stream decoder for zip. /// Stream decoder for zip.
#[derive(Debug)] #[derive(Debug)]
pub struct ZipStreamReader<R>(R); pub struct ZipStreamReader<R>(R);
@ -28,7 +27,7 @@ impl<R: Read> ZipStreamReader<R> {
let central_header_start = 0; let central_header_start = 0;
// Parse central header // Parse central header
let signature = self.0.read_u32::<LittleEndian>()?; let signature = self.0.read_u32_le()?;
if signature != spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE { if signature != spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE {
Ok(None) Ok(None)
} else { } else {

View file

@ -1,5 +1,5 @@
use crate::result::{ZipError, ZipResult}; use crate::result::{ZipError, ZipResult};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use crate::unstable::{LittleEndianReadExt, LittleEndianWriteExt};
use std::borrow::Cow; use std::borrow::Cow;
use std::io; use std::io;
use std::io::prelude::*; use std::io::prelude::*;
@ -26,17 +26,17 @@ pub struct CentralDirectoryEnd {
impl CentralDirectoryEnd { impl CentralDirectoryEnd {
pub fn parse<T: Read>(reader: &mut T) -> ZipResult<CentralDirectoryEnd> { pub fn parse<T: Read>(reader: &mut T) -> ZipResult<CentralDirectoryEnd> {
let magic = reader.read_u32::<LittleEndian>()?; let magic = reader.read_u32_le()?;
if magic != CENTRAL_DIRECTORY_END_SIGNATURE { if magic != CENTRAL_DIRECTORY_END_SIGNATURE {
return Err(ZipError::InvalidArchive("Invalid digital signature header")); return Err(ZipError::InvalidArchive("Invalid digital signature header"));
} }
let disk_number = reader.read_u16::<LittleEndian>()?; let disk_number = reader.read_u16_le()?;
let disk_with_central_directory = reader.read_u16::<LittleEndian>()?; let disk_with_central_directory = reader.read_u16_le()?;
let number_of_files_on_this_disk = reader.read_u16::<LittleEndian>()?; let number_of_files_on_this_disk = reader.read_u16_le()?;
let number_of_files = reader.read_u16::<LittleEndian>()?; let number_of_files = reader.read_u16_le()?;
let central_directory_size = reader.read_u32::<LittleEndian>()?; let central_directory_size = reader.read_u32_le()?;
let central_directory_offset = reader.read_u32::<LittleEndian>()?; let central_directory_offset = reader.read_u32_le()?;
let zip_file_comment_length = reader.read_u16::<LittleEndian>()? as usize; let zip_file_comment_length = reader.read_u16_le()? as usize;
let mut zip_file_comment = vec![0; zip_file_comment_length]; let mut zip_file_comment = vec![0; zip_file_comment_length];
reader.read_exact(&mut zip_file_comment)?; reader.read_exact(&mut zip_file_comment)?;
@ -65,7 +65,7 @@ impl CentralDirectoryEnd {
let mut pos = file_length - HEADER_SIZE; let mut pos = file_length - HEADER_SIZE;
while pos >= search_upper_bound { while pos >= search_upper_bound {
reader.seek(io::SeekFrom::Start(pos))?; reader.seek(io::SeekFrom::Start(pos))?;
if reader.read_u32::<LittleEndian>()? == CENTRAL_DIRECTORY_END_SIGNATURE { if reader.read_u32_le()? == CENTRAL_DIRECTORY_END_SIGNATURE {
reader.seek(io::SeekFrom::Current( reader.seek(io::SeekFrom::Current(
BYTES_BETWEEN_MAGIC_AND_COMMENT_SIZE as i64, BYTES_BETWEEN_MAGIC_AND_COMMENT_SIZE as i64,
))?; ))?;
@ -85,14 +85,14 @@ impl CentralDirectoryEnd {
} }
pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()> { pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()> {
writer.write_u32::<LittleEndian>(CENTRAL_DIRECTORY_END_SIGNATURE)?; writer.write_u32_le(CENTRAL_DIRECTORY_END_SIGNATURE)?;
writer.write_u16::<LittleEndian>(self.disk_number)?; writer.write_u16_le(self.disk_number)?;
writer.write_u16::<LittleEndian>(self.disk_with_central_directory)?; writer.write_u16_le(self.disk_with_central_directory)?;
writer.write_u16::<LittleEndian>(self.number_of_files_on_this_disk)?; writer.write_u16_le(self.number_of_files_on_this_disk)?;
writer.write_u16::<LittleEndian>(self.number_of_files)?; writer.write_u16_le(self.number_of_files)?;
writer.write_u32::<LittleEndian>(self.central_directory_size)?; writer.write_u32_le(self.central_directory_size)?;
writer.write_u32::<LittleEndian>(self.central_directory_offset)?; writer.write_u32_le(self.central_directory_offset)?;
writer.write_u16::<LittleEndian>(self.zip_file_comment.len() as u16)?; writer.write_u16_le(self.zip_file_comment.len() as u16)?;
writer.write_all(&self.zip_file_comment)?; writer.write_all(&self.zip_file_comment)?;
Ok(()) Ok(())
} }
@ -106,15 +106,15 @@ pub struct Zip64CentralDirectoryEndLocator {
impl Zip64CentralDirectoryEndLocator { impl Zip64CentralDirectoryEndLocator {
pub fn parse<T: Read>(reader: &mut T) -> ZipResult<Zip64CentralDirectoryEndLocator> { pub fn parse<T: Read>(reader: &mut T) -> ZipResult<Zip64CentralDirectoryEndLocator> {
let magic = reader.read_u32::<LittleEndian>()?; let magic = reader.read_u32_le()?;
if magic != ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE { if magic != ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE {
return Err(ZipError::InvalidArchive( return Err(ZipError::InvalidArchive(
"Invalid zip64 locator digital signature header", "Invalid zip64 locator digital signature header",
)); ));
} }
let disk_with_central_directory = reader.read_u32::<LittleEndian>()?; let disk_with_central_directory = reader.read_u32_le()?;
let end_of_central_directory_offset = reader.read_u64::<LittleEndian>()?; let end_of_central_directory_offset = reader.read_u64_le()?;
let number_of_disks = reader.read_u32::<LittleEndian>()?; let number_of_disks = reader.read_u32_le()?;
Ok(Zip64CentralDirectoryEndLocator { Ok(Zip64CentralDirectoryEndLocator {
disk_with_central_directory, disk_with_central_directory,
@ -124,10 +124,10 @@ impl Zip64CentralDirectoryEndLocator {
} }
pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()> { pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()> {
writer.write_u32::<LittleEndian>(ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE)?; writer.write_u32_le(ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE)?;
writer.write_u32::<LittleEndian>(self.disk_with_central_directory)?; writer.write_u32_le(self.disk_with_central_directory)?;
writer.write_u64::<LittleEndian>(self.end_of_central_directory_offset)?; writer.write_u64_le(self.end_of_central_directory_offset)?;
writer.write_u32::<LittleEndian>(self.number_of_disks)?; writer.write_u32_le(self.number_of_disks)?;
Ok(()) Ok(())
} }
} }
@ -156,20 +156,20 @@ impl Zip64CentralDirectoryEnd {
while pos >= nominal_offset { while pos >= nominal_offset {
reader.seek(io::SeekFrom::Start(pos))?; reader.seek(io::SeekFrom::Start(pos))?;
if reader.read_u32::<LittleEndian>()? == ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE { if reader.read_u32_le()? == ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE {
let archive_offset = pos - nominal_offset; let archive_offset = pos - nominal_offset;
let _record_size = reader.read_u64::<LittleEndian>()?; let _record_size = reader.read_u64_le()?;
// We would use this value if we did anything with the "zip64 extensible data sector". // We would use this value if we did anything with the "zip64 extensible data sector".
let version_made_by = reader.read_u16::<LittleEndian>()?; let version_made_by = reader.read_u16_le()?;
let version_needed_to_extract = reader.read_u16::<LittleEndian>()?; let version_needed_to_extract = reader.read_u16_le()?;
let disk_number = reader.read_u32::<LittleEndian>()?; let disk_number = reader.read_u32_le()?;
let disk_with_central_directory = reader.read_u32::<LittleEndian>()?; let disk_with_central_directory = reader.read_u32_le()?;
let number_of_files_on_this_disk = reader.read_u64::<LittleEndian>()?; let number_of_files_on_this_disk = reader.read_u64_le()?;
let number_of_files = reader.read_u64::<LittleEndian>()?; let number_of_files = reader.read_u64_le()?;
let central_directory_size = reader.read_u64::<LittleEndian>()?; let central_directory_size = reader.read_u64_le()?;
let central_directory_offset = reader.read_u64::<LittleEndian>()?; let central_directory_offset = reader.read_u64_le()?;
results.push(( results.push((
Zip64CentralDirectoryEnd { Zip64CentralDirectoryEnd {
@ -201,16 +201,16 @@ impl Zip64CentralDirectoryEnd {
} }
pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()> { pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()> {
writer.write_u32::<LittleEndian>(ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE)?; writer.write_u32_le(ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE)?;
writer.write_u64::<LittleEndian>(44)?; // record size writer.write_u64_le(44)?; // record size
writer.write_u16::<LittleEndian>(self.version_made_by)?; writer.write_u16_le(self.version_made_by)?;
writer.write_u16::<LittleEndian>(self.version_needed_to_extract)?; writer.write_u16_le(self.version_needed_to_extract)?;
writer.write_u32::<LittleEndian>(self.disk_number)?; writer.write_u32_le(self.disk_number)?;
writer.write_u32::<LittleEndian>(self.disk_with_central_directory)?; writer.write_u32_le(self.disk_with_central_directory)?;
writer.write_u64::<LittleEndian>(self.number_of_files_on_this_disk)?; writer.write_u64_le(self.number_of_files_on_this_disk)?;
writer.write_u64::<LittleEndian>(self.number_of_files)?; writer.write_u64_le(self.number_of_files)?;
writer.write_u64::<LittleEndian>(self.central_directory_size)?; writer.write_u64_le(self.central_directory_size)?;
writer.write_u64::<LittleEndian>(self.central_directory_offset)?; writer.write_u64_le(self.central_directory_offset)?;
Ok(()) Ok(())
} }
} }

View file

@ -1,3 +1,8 @@
#![allow(missing_docs)]
use std::io;
use std::io::{Read, Write};
/// Provides high level API for reading from a stream. /// Provides high level API for reading from a stream.
pub mod stream { pub mod stream {
pub use crate::read::stream::*; pub use crate::read::stream::*;
@ -18,3 +23,47 @@ pub mod write {
} }
} }
} }
/// Helper methods for writing unsigned integers in little-endian form.
pub trait LittleEndianWriteExt: Write {
fn write_u16_le(&mut self, input: u16) -> io::Result<()> {
self.write_all(&input.to_le_bytes())
}
fn write_u32_le(&mut self, input: u32) -> io::Result<()> {
self.write_all(&input.to_le_bytes())
}
fn write_u64_le(&mut self, input: u64) -> io::Result<()> {
self.write_all(&input.to_le_bytes())
}
fn write_u128_le(&mut self, input: u128) -> io::Result<()> {
self.write_all(&input.to_le_bytes())
}
}
impl<W: Write> LittleEndianWriteExt for W {}
/// Helper methods for reading unsigned integers in little-endian form.
pub trait LittleEndianReadExt: Read {
fn read_u16_le(&mut self) -> io::Result<u16> {
let mut out = [0u8; 2];
self.read_exact(&mut out)?;
Ok(u16::from_le_bytes(out))
}
fn read_u32_le(&mut self) -> io::Result<u32> {
let mut out = [0u8; 4];
self.read_exact(&mut out)?;
Ok(u32::from_le_bytes(out))
}
fn read_u64_le(&mut self) -> io::Result<u64> {
let mut out = [0u8; 8];
self.read_exact(&mut out)?;
Ok(u64::from_le_bytes(out))
}
}
impl<R: Read> LittleEndianReadExt for R {}

View file

@ -5,7 +5,6 @@ use crate::read::{find_content, ZipArchive, ZipFile, ZipFileReader};
use crate::result::{ZipError, ZipResult}; use crate::result::{ZipError, ZipResult};
use crate::spec; use crate::spec;
use crate::types::{ffi, DateTime, System, ZipFileData, DEFAULT_VERSION}; use crate::types::{ffi, DateTime, System, ZipFileData, DEFAULT_VERSION};
use byteorder::{LittleEndian, WriteBytesExt};
#[cfg(any(feature = "_deflate-any", feature = "bzip2", feature = "zstd",))] #[cfg(any(feature = "_deflate-any", feature = "bzip2", feature = "zstd",))]
use core::num::NonZeroU64; use core::num::NonZeroU64;
use crc32fast::Hasher; use crc32fast::Hasher;
@ -126,6 +125,7 @@ use crate::result::ZipError::InvalidArchive;
#[cfg(feature = "lzma")] #[cfg(feature = "lzma")]
use crate::result::ZipError::UnsupportedArchive; use crate::result::ZipError::UnsupportedArchive;
use crate::spec::path_to_string; use crate::spec::path_to_string;
use crate::unstable::LittleEndianWriteExt;
use crate::write::GenericZipWriter::{Closed, Storer}; use crate::write::GenericZipWriter::{Closed, Storer};
use crate::zipcrypto::ZipCryptoKeys; use crate::zipcrypto::ZipCryptoKeys;
use crate::CompressionMethod::Stored; use crate::CompressionMethod::Stored;
@ -378,8 +378,8 @@ impl FileOptions<ExtendedFileOptions> {
} }
}; };
vec.reserve_exact(data.len() + 4); vec.reserve_exact(data.len() + 4);
vec.write_u16::<LittleEndian>(header_id)?; vec.write_u16_le(header_id)?;
vec.write_u16::<LittleEndian>(data.len() as u16)?; vec.write_u16_le(data.len() as u16)?;
vec.write_all(data)?; vec.write_all(data)?;
Ok(()) Ok(())
} }
@ -734,34 +734,34 @@ impl<W: Write + Seek> ZipWriter<W> {
let index = self.insert_file_data(file)?; let index = self.insert_file_data(file)?;
let file = &mut self.files[index]; let file = &mut self.files[index];
let writer = self.inner.get_plain(); let writer = self.inner.get_plain();
writer.write_u32::<LittleEndian>(spec::LOCAL_FILE_HEADER_SIGNATURE)?; writer.write_u32_le(spec::LOCAL_FILE_HEADER_SIGNATURE)?;
// version needed to extract // version needed to extract
writer.write_u16::<LittleEndian>(file.version_needed())?; writer.write_u16_le(file.version_needed())?;
// general purpose bit flag // general purpose bit flag
let flag = if !file.file_name.is_ascii() { let flag = if !file.file_name.is_ascii() {
1u16 << 11 1u16 << 11
} else { } else {
0 0
} | if file.encrypted { 1u16 << 0 } else { 0 }; } | if file.encrypted { 1u16 << 0 } else { 0 };
writer.write_u16::<LittleEndian>(flag)?; writer.write_u16_le(flag)?;
// Compression method // Compression method
#[allow(deprecated)] #[allow(deprecated)]
writer.write_u16::<LittleEndian>(file.compression_method.to_u16())?; writer.write_u16_le(file.compression_method.to_u16())?;
// last mod file time and last mod file date // last mod file time and last mod file date
writer.write_u16::<LittleEndian>(file.last_modified_time.timepart())?; writer.write_u16_le(file.last_modified_time.timepart())?;
writer.write_u16::<LittleEndian>(file.last_modified_time.datepart())?; writer.write_u16_le(file.last_modified_time.datepart())?;
// crc-32 // crc-32
writer.write_u32::<LittleEndian>(file.crc32)?; writer.write_u32_le(file.crc32)?;
// compressed size and uncompressed size // compressed size and uncompressed size
if file.large_file { if file.large_file {
writer.write_u32::<LittleEndian>(spec::ZIP64_BYTES_THR as u32)?; writer.write_u32_le(spec::ZIP64_BYTES_THR as u32)?;
writer.write_u32::<LittleEndian>(spec::ZIP64_BYTES_THR as u32)?; writer.write_u32_le(spec::ZIP64_BYTES_THR as u32)?;
} else { } else {
writer.write_u32::<LittleEndian>(file.compressed_size as u32)?; writer.write_u32_le(file.compressed_size as u32)?;
writer.write_u32::<LittleEndian>(file.uncompressed_size as u32)?; writer.write_u32_le(file.uncompressed_size as u32)?;
} }
// file name length // file name length
writer.write_u16::<LittleEndian>(file.file_name.as_bytes().len() as u16)?; writer.write_u16_le(file.file_name.as_bytes().len() as u16)?;
// extra field length // extra field length
let mut extra_field_length = file.extra_field_len(); let mut extra_field_length = file.extra_field_len();
if file.large_file { if file.large_file {
@ -772,7 +772,7 @@ impl<W: Write + Seek> ZipWriter<W> {
return Err(InvalidArchive("Extra data field is too large")); return Err(InvalidArchive("Extra data field is too large"));
} }
let extra_field_length = extra_field_length as u16; let extra_field_length = extra_field_length as u16;
writer.write_u16::<LittleEndian>(extra_field_length)?; writer.write_u16_le(extra_field_length)?;
// file name // file name
writer.write_all(file.file_name.as_bytes())?; writer.write_all(file.file_name.as_bytes())?;
// zip64 extra field // zip64 extra field
@ -801,7 +801,7 @@ impl<W: Write + Seek> ZipWriter<W> {
let pad_body = vec![0; pad_length - 4]; let pad_body = vec![0; pad_length - 4];
writer.write_all(b"za").map_err(ZipError::from)?; // 0x617a writer.write_all(b"za").map_err(ZipError::from)?; // 0x617a
writer writer
.write_u16::<LittleEndian>(pad_body.len() as u16) .write_u16_le(pad_body.len() as u16)
.map_err(ZipError::from)?; .map_err(ZipError::from)?;
writer.write_all(&pad_body).map_err(ZipError::from)?; writer.write_all(&pad_body).map_err(ZipError::from)?;
} else { } else {
@ -814,7 +814,7 @@ impl<W: Write + Seek> ZipWriter<W> {
// Update extra field length in local file header. // Update extra field length in local file header.
writer.seek(SeekFrom::Start(file.header_start + 28))?; writer.seek(SeekFrom::Start(file.header_start + 28))?;
writer.write_u16::<LittleEndian>(new_extra_field_length)?; writer.write_u16_le(new_extra_field_length)?;
writer.seek(SeekFrom::Start(header_end))?; writer.seek(SeekFrom::Start(header_end))?;
debug_assert_eq!(header_end % align, 0); debug_assert_eq!(header_end % align, 0);
} }
@ -1618,7 +1618,7 @@ fn clamp_opt<T: Ord + Copy, U: Ord + Copy + TryFrom<T>>(
fn update_local_file_header<T: Write + Seek>(writer: &mut T, file: &ZipFileData) -> ZipResult<()> { fn update_local_file_header<T: Write + Seek>(writer: &mut T, file: &ZipFileData) -> ZipResult<()> {
const CRC32_OFFSET: u64 = 14; const CRC32_OFFSET: u64 = 14;
writer.seek(SeekFrom::Start(file.header_start + CRC32_OFFSET))?; writer.seek(SeekFrom::Start(file.header_start + CRC32_OFFSET))?;
writer.write_u32::<LittleEndian>(file.crc32)?; writer.write_u32_le(file.crc32)?;
if file.large_file { if file.large_file {
update_local_zip64_extra_field(writer, file)?; update_local_zip64_extra_field(writer, file)?;
} else { } else {
@ -1629,9 +1629,9 @@ fn update_local_file_header<T: Write + Seek>(writer: &mut T, file: &ZipFileData)
"Large file option has not been set", "Large file option has not been set",
))); )));
} }
writer.write_u32::<LittleEndian>(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 // uncompressed size is already checked on write to catch it as soon as possible
writer.write_u32::<LittleEndian>(file.uncompressed_size as u32)?; writer.write_u32_le(file.uncompressed_size as u32)?;
} }
Ok(()) Ok(())
} }
@ -1643,49 +1643,49 @@ fn write_central_directory_header<T: Write>(writer: &mut T, file: &ZipFileData)
write_central_zip64_extra_field(&mut zip64_extra_field.as_mut(), file)?; write_central_zip64_extra_field(&mut zip64_extra_field.as_mut(), file)?;
// central file header signature // central file header signature
writer.write_u32::<LittleEndian>(spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE)?; writer.write_u32_le(spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE)?;
// version made by // version made by
let version_made_by = (file.system as u16) << 8 | (file.version_made_by as u16); let version_made_by = (file.system as u16) << 8 | (file.version_made_by as u16);
writer.write_u16::<LittleEndian>(version_made_by)?; writer.write_u16_le(version_made_by)?;
// version needed to extract // version needed to extract
writer.write_u16::<LittleEndian>(file.version_needed())?; writer.write_u16_le(file.version_needed())?;
// general purpose bit flag // general purpose bit flag
let flag = if !file.file_name.is_ascii() { let flag = if !file.file_name.is_ascii() {
1u16 << 11 1u16 << 11
} else { } else {
0 0
} | if file.encrypted { 1u16 << 0 } else { 0 }; } | if file.encrypted { 1u16 << 0 } else { 0 };
writer.write_u16::<LittleEndian>(flag)?; writer.write_u16_le(flag)?;
// compression method // compression method
#[allow(deprecated)] #[allow(deprecated)]
writer.write_u16::<LittleEndian>(file.compression_method.to_u16())?; writer.write_u16_le(file.compression_method.to_u16())?;
// last mod file time + date // last mod file time + date
writer.write_u16::<LittleEndian>(file.last_modified_time.timepart())?; writer.write_u16_le(file.last_modified_time.timepart())?;
writer.write_u16::<LittleEndian>(file.last_modified_time.datepart())?; writer.write_u16_le(file.last_modified_time.datepart())?;
// crc-32 // crc-32
writer.write_u32::<LittleEndian>(file.crc32)?; writer.write_u32_le(file.crc32)?;
// compressed size // compressed size
writer.write_u32::<LittleEndian>(file.compressed_size.min(spec::ZIP64_BYTES_THR) as u32)?; writer.write_u32_le(file.compressed_size.min(spec::ZIP64_BYTES_THR) as u32)?;
// uncompressed size // uncompressed size
writer.write_u32::<LittleEndian>(file.uncompressed_size.min(spec::ZIP64_BYTES_THR) as u32)?; writer.write_u32_le(file.uncompressed_size.min(spec::ZIP64_BYTES_THR) as u32)?;
// file name length // file name length
writer.write_u16::<LittleEndian>(file.file_name.as_bytes().len() as u16)?; writer.write_u16_le(file.file_name.as_bytes().len() as u16)?;
// extra field length // extra field length
writer.write_u16::<LittleEndian>( writer.write_u16_le(
zip64_extra_field_length zip64_extra_field_length
+ file.extra_field_len() as u16 + file.extra_field_len() as u16
+ file.central_extra_field_len() as u16, + file.central_extra_field_len() as u16,
)?; )?;
// file comment length // file comment length
writer.write_u16::<LittleEndian>(0)?; writer.write_u16_le(0)?;
// disk number start // disk number start
writer.write_u16::<LittleEndian>(0)?; writer.write_u16_le(0)?;
// internal file attributes // internal file attributes
writer.write_u16::<LittleEndian>(0)?; writer.write_u16_le(0)?;
// external file attributes // external file attributes
writer.write_u32::<LittleEndian>(file.external_attributes)?; writer.write_u32_le(file.external_attributes)?;
// relative offset of local header // relative offset of local header
writer.write_u32::<LittleEndian>(file.header_start.min(spec::ZIP64_BYTES_THR) as u32)?; writer.write_u32_le(file.header_start.min(spec::ZIP64_BYTES_THR) as u32)?;
// file name // file name
writer.write_all(file.file_name.as_bytes())?; writer.write_all(file.file_name.as_bytes())?;
// zip64 extra field // zip64 extra field
@ -1739,10 +1739,10 @@ fn validate_extra_data(header_id: u16, data: &[u8]) -> ZipResult<()> {
fn write_local_zip64_extra_field<T: Write>(writer: &mut T, file: &ZipFileData) -> ZipResult<()> { fn write_local_zip64_extra_field<T: Write>(writer: &mut T, file: &ZipFileData) -> ZipResult<()> {
// This entry in the Local header MUST include BOTH original // This entry in the Local header MUST include BOTH original
// and compressed file size fields. // and compressed file size fields.
writer.write_u16::<LittleEndian>(0x0001)?; writer.write_u16_le(0x0001)?;
writer.write_u16::<LittleEndian>(16)?; writer.write_u16_le(16)?;
writer.write_u64::<LittleEndian>(file.uncompressed_size)?; writer.write_u64_le(file.uncompressed_size)?;
writer.write_u64::<LittleEndian>(file.compressed_size)?; writer.write_u64_le(file.compressed_size)?;
// Excluded fields: // Excluded fields:
// u32: disk start number // u32: disk start number
Ok(()) Ok(())
@ -1754,8 +1754,8 @@ fn update_local_zip64_extra_field<T: Write + Seek>(
) -> ZipResult<()> { ) -> ZipResult<()> {
let zip64_extra_field = file.header_start + 30 + file.file_name.as_bytes().len() as u64; let zip64_extra_field = file.header_start + 30 + file.file_name.as_bytes().len() as u64;
writer.seek(SeekFrom::Start(zip64_extra_field + 4))?; writer.seek(SeekFrom::Start(zip64_extra_field + 4))?;
writer.write_u64::<LittleEndian>(file.uncompressed_size)?; writer.write_u64_le(file.uncompressed_size)?;
writer.write_u64::<LittleEndian>(file.compressed_size)?; writer.write_u64_le(file.compressed_size)?;
// Excluded fields: // Excluded fields:
// u32: disk start number // u32: disk start number
Ok(()) Ok(())
@ -1780,18 +1780,18 @@ fn write_central_zip64_extra_field<T: Write>(writer: &mut T, file: &ZipFileData)
size += 8; size += 8;
} }
if size > 0 { if size > 0 {
writer.write_u16::<LittleEndian>(0x0001)?; writer.write_u16_le(0x0001)?;
writer.write_u16::<LittleEndian>(size)?; writer.write_u16_le(size)?;
size += 4; size += 4;
if uncompressed_size { if uncompressed_size {
writer.write_u64::<LittleEndian>(file.uncompressed_size)?; writer.write_u64_le(file.uncompressed_size)?;
} }
if compressed_size { if compressed_size {
writer.write_u64::<LittleEndian>(file.compressed_size)?; writer.write_u64_le(file.compressed_size)?;
} }
if header_start { if header_start {
writer.write_u64::<LittleEndian>(file.header_start)?; writer.write_u64_le(file.header_start)?;
} }
// Excluded fields: // Excluded fields:
// u32: disk start number // u32: disk start number

View file

@ -1,8 +1,8 @@
use byteorder::{LittleEndian, WriteBytesExt};
use std::collections::HashSet; use std::collections::HashSet;
use std::io::prelude::*; use std::io::prelude::*;
use std::io::Cursor; use std::io::Cursor;
use zip::result::ZipResult; use zip::result::ZipResult;
use zip::unstable::LittleEndianWriteExt;
use zip::write::ExtendedFileOptions; use zip::write::ExtendedFileOptions;
use zip::write::FileOptions; use zip::write::FileOptions;
use zip::write::SimpleFileOptions; use zip::write::SimpleFileOptions;
@ -159,8 +159,8 @@ fn check_test_archive<R: Read + Seek>(zip_file: R) -> ZipResult<zip::ZipArchive<
{ {
let file_with_extra_data = archive.by_name("test_with_extra_data/🐢.txt")?; let file_with_extra_data = archive.by_name("test_with_extra_data/🐢.txt")?;
let mut extra_data = Vec::new(); let mut extra_data = Vec::new();
extra_data.write_u16::<LittleEndian>(0xbeef)?; extra_data.write_u16_le(0xbeef)?;
extra_data.write_u16::<LittleEndian>(EXTRA_DATA.len() as u16)?; extra_data.write_u16_le(EXTRA_DATA.len() as u16)?;
extra_data.write_all(EXTRA_DATA)?; extra_data.write_all(EXTRA_DATA)?;
assert_eq!( assert_eq!(
file_with_extra_data.extra_data(), file_with_extra_data.extra_data(),