fix: ZIP64 header was being written twice when copying a file
This commit is contained in:
parent
a770913f7b
commit
052f3a133e
3 changed files with 50 additions and 17 deletions
|
@ -51,12 +51,10 @@ impl <'k> Debug for FileOperation<'k> {
|
||||||
for content_slice in contents {
|
for content_slice in contents {
|
||||||
f.write_fmt(format_args!("writer.write_all(&({:?}[..] as [u8]))?;\n", content_slice))?;
|
f.write_fmt(format_args!("writer.write_all(&({:?}[..] as [u8]))?;\n", content_slice))?;
|
||||||
}
|
}
|
||||||
f.write_str("drop(options);\n")
|
|
||||||
},
|
},
|
||||||
BasicFileOperation::WriteDirectory(options) => {
|
BasicFileOperation::WriteDirectory(options) => {
|
||||||
f.write_fmt(format_args!("let options = {:?};\n\
|
f.write_fmt(format_args!("let options = {:?};\n\
|
||||||
writer.add_directory_from_path({:?}, options)?;\n\
|
writer.add_directory_from_path({:?}, options)?;\n",
|
||||||
drop(options);\n",
|
|
||||||
options, self.path))
|
options, self.path))
|
||||||
},
|
},
|
||||||
BasicFileOperation::WriteSymlinkWithTarget {target, options} => {
|
BasicFileOperation::WriteSymlinkWithTarget {target, options} => {
|
||||||
|
|
41
src/read.rs
41
src/read.rs
|
@ -1212,9 +1212,11 @@ fn central_header_to_zip_file_inner<R: Read>(
|
||||||
aes_extra_data_start: 0,
|
aes_extra_data_start: 0,
|
||||||
extra_fields: Vec::new(),
|
extra_fields: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
match parse_extra_field(&mut result) {
|
match parse_extra_field(&mut result) {
|
||||||
Ok(..) | Err(ZipError::Io(..)) => {}
|
Ok(stripped_extra_field) => {
|
||||||
|
result.extra_field = stripped_extra_field;
|
||||||
|
}
|
||||||
|
Err(ZipError::Io(..)) => {}
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1234,21 +1236,33 @@ fn central_header_to_zip_file_inner<R: Read>(
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_extra_field(file: &mut ZipFileData) -> ZipResult<()> {
|
pub(crate) fn parse_extra_field(file: &mut ZipFileData) -> ZipResult<Option<Arc<Vec<u8>>>> {
|
||||||
let Some(ref extra_field) = file.extra_field else {
|
let Some(ref extra_field) = file.extra_field else {
|
||||||
return Ok(());
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
let extra_field = extra_field.clone();
|
||||||
|
let mut processed_extra_field = extra_field.clone();
|
||||||
let len = extra_field.len();
|
let len = extra_field.len();
|
||||||
let extra_field = extra_field.to_vec();
|
let mut reader = io::Cursor::new(&**extra_field);
|
||||||
let mut reader = io::Cursor::new(extra_field);
|
|
||||||
|
|
||||||
/* TODO: codify this structure into Zip64ExtraFieldBlock fields! */
|
/* TODO: codify this structure into Zip64ExtraFieldBlock fields! */
|
||||||
let mut position = reader.position();
|
let mut position = reader.position() as usize;
|
||||||
while (position as usize) < len {
|
while (position) < len {
|
||||||
parse_single_extra_field(file, &mut reader, position, false)?;
|
let old_position = position;
|
||||||
position = reader.position();
|
let remove = parse_single_extra_field(file, &mut reader, position as u64, false)?;
|
||||||
|
position = reader.position() as usize;
|
||||||
|
if remove {
|
||||||
|
let remaining = len - (position - old_position);
|
||||||
|
if remaining == 0 {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
let mut new_extra_field = Vec::with_capacity(remaining);
|
||||||
|
new_extra_field.extend_from_slice(&extra_field[0..old_position]);
|
||||||
|
new_extra_field.extend_from_slice(&extra_field[position..]);
|
||||||
|
processed_extra_field = Arc::new(new_extra_field);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(Some(processed_extra_field))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_single_extra_field<R: Read>(
|
pub(crate) fn parse_single_extra_field<R: Read>(
|
||||||
|
@ -1256,7 +1270,7 @@ pub(crate) fn parse_single_extra_field<R: Read>(
|
||||||
reader: &mut R,
|
reader: &mut R,
|
||||||
bytes_already_read: u64,
|
bytes_already_read: u64,
|
||||||
disallow_zip64: bool,
|
disallow_zip64: bool,
|
||||||
) -> ZipResult<()> {
|
) -> ZipResult<bool> {
|
||||||
let kind = reader.read_u16_le()?;
|
let kind = reader.read_u16_le()?;
|
||||||
let len = reader.read_u16_le()?;
|
let len = reader.read_u16_le()?;
|
||||||
match kind {
|
match kind {
|
||||||
|
@ -1286,6 +1300,7 @@ pub(crate) fn parse_single_extra_field<R: Read>(
|
||||||
return Err(InvalidArchive("ZIP64 extra-data field is the wrong length"));
|
return Err(InvalidArchive("ZIP64 extra-data field is the wrong length"));
|
||||||
};
|
};
|
||||||
reader.read_exact(&mut vec![0u8; leftover_len])?;
|
reader.read_exact(&mut vec![0u8; leftover_len])?;
|
||||||
|
return Ok(true);
|
||||||
}
|
}
|
||||||
0x9901 => {
|
0x9901 => {
|
||||||
// AES
|
// AES
|
||||||
|
@ -1350,7 +1365,7 @@ pub(crate) fn parse_single_extra_field<R: Read>(
|
||||||
// Other fields are ignored
|
// Other fields are ignored
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Methods for retrieving information on zip files
|
/// Methods for retrieving information on zip files
|
||||||
|
|
22
src/write.rs
22
src/write.rs
|
@ -401,7 +401,7 @@ impl<'a> arbitrary::Arbitrary<'a> for FileOptions<'a, ExtendedFileOptions> {
|
||||||
u.arbitrary_loop(Some(0), Some(10), |u| {
|
u.arbitrary_loop(Some(0), Some(10), |u| {
|
||||||
options
|
options
|
||||||
.add_extra_data(
|
.add_extra_data(
|
||||||
u16::arbitrary(u)?,
|
u.int_in_range(2..=u16::MAX)?,
|
||||||
Box::<[u8]>::arbitrary(u)?,
|
Box::<[u8]>::arbitrary(u)?,
|
||||||
bool::arbitrary(u)?,
|
bool::arbitrary(u)?,
|
||||||
)
|
)
|
||||||
|
@ -2806,4 +2806,24 @@ mod test {
|
||||||
let _ = writer.finish_into_readable()?;
|
let _ = writer.finish_into_readable()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fuzz_crash_2024_06_14c() -> ZipResult<()> {
|
||||||
|
let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
|
||||||
|
writer.set_flush_on_finish_file(false);
|
||||||
|
let sub_writer = {
|
||||||
|
let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
|
||||||
|
writer.set_flush_on_finish_file(false);
|
||||||
|
let options = FileOptions { compression_method: Stored, compression_level: None, last_modified_time: DateTime::from_date_and_time(2060, 4, 6, 13, 13, 3)?, permissions: None, large_file: true, encrypt_with: None, extended_options: ExtendedFileOptions {extra_data: vec![].into(), central_extra_data: vec![].into()}, alignment: 0, ..Default::default()};
|
||||||
|
writer.start_file_from_path("\0", options)?;
|
||||||
|
writer.write_all(&([]))?;
|
||||||
|
writer = ZipWriter::new_append(writer.finish()?)?;
|
||||||
|
writer.deep_copy_file_from_path("\0", "")?;
|
||||||
|
writer
|
||||||
|
};
|
||||||
|
writer.merge_archive(sub_writer.finish_into_readable()?)?;
|
||||||
|
writer.deep_copy_file_from_path("", "_copy")?;
|
||||||
|
let _ = writer.finish_into_readable()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue