Add shallow_copy_file_from_path,deep_copy_file_from_path and raw_copy_file_to_path, and use PathBuf in fuzz_write

This commit is contained in:
Chris Hennick 2024-04-20 14:38:54 -07:00
parent 0087dab984
commit cdc2e7aa23
No known key found for this signature in database
GPG key ID: DA47AABA4961C509
3 changed files with 46 additions and 28 deletions

View file

@ -275,7 +275,8 @@
- `index_for_name`, `index_for_path`, `name_for_index`: get the index of a file given its path or vice-versa, without - `index_for_name`, `index_for_path`, `name_for_index`: get the index of a file given its path or vice-versa, without
initializing metadata from the local-file header or needing to mutably borrow the `ZipArchive`. initializing metadata from the local-file header or needing to mutably borrow the `ZipArchive`.
- `add_symlink_from_path`: create a symlink using `AsRef<Path>` arguments - `add_symlink_from_path`, `shallow_copy_file_from_path`, `deep_copy_file_from_path`, `raw_copy_file_to_path`: copy a
file or create a symlink using `AsRef<Path>` arguments
### Changed ### Changed

View file

@ -14,7 +14,7 @@ pub enum BasicFileOperation {
}, },
WriteDirectory(zip::write::FullFileOptions), WriteDirectory(zip::write::FullFileOptions),
WriteSymlinkWithTarget { WriteSymlinkWithTarget {
target: Box<PathBuf>, target: PathBuf,
options: zip::write::FullFileOptions, options: zip::write::FullFileOptions,
}, },
ShallowCopy(Box<FileOperation>), ShallowCopy(Box<FileOperation>),
@ -24,7 +24,7 @@ pub enum BasicFileOperation {
#[derive(Arbitrary, Clone, Debug)] #[derive(Arbitrary, Clone, Debug)]
pub struct FileOperation { pub struct FileOperation {
basic: BasicFileOperation, basic: BasicFileOperation,
name: String, path: PathBuf,
reopen: bool, reopen: bool,
// 'abort' flag is separate, to prevent trying to copy an aborted file // 'abort' flag is separate, to prevent trying to copy an aborted file
} }
@ -36,20 +36,9 @@ pub struct FuzzTestCase {
flush_on_finish_file: bool, flush_on_finish_file: bool,
} }
impl FileOperation {
fn referenceable_name(&self) -> String {
if let BasicFileOperation::WriteDirectory(_) = self.basic {
if !self.name.ends_with('\\') && !self.name.ends_with('/') {
return self.name.to_owned() + "/";
}
}
self.name.to_owned()
}
}
fn do_operation<T>( fn do_operation<T>(
writer: &mut RefCell<zip::ZipWriter<T>>, writer: &mut RefCell<zip::ZipWriter<T>>,
operation: FileOperation, operation: &FileOperation,
abort: bool, abort: bool,
flush_on_finish_file: bool, flush_on_finish_file: bool,
) -> Result<(), Box<dyn std::error::Error>> ) -> Result<(), Box<dyn std::error::Error>>
@ -59,39 +48,38 @@ where
writer writer
.borrow_mut() .borrow_mut()
.set_flush_on_finish_file(flush_on_finish_file); .set_flush_on_finish_file(flush_on_finish_file);
let name = operation.name; let path = &operation.path;
match operation.basic { match &operation.basic {
BasicFileOperation::WriteNormalFile { BasicFileOperation::WriteNormalFile {
contents, contents,
mut options, options,
.. ..
} => { } => {
let uncompressed_size = contents.iter().map(Vec::len).sum::<usize>(); let uncompressed_size = contents.iter().map(Vec::len).sum::<usize>();
let mut options = (*options).to_owned();
if uncompressed_size >= u32::MAX as usize { if uncompressed_size >= u32::MAX as usize {
options = options.large_file(true); options = options.large_file(true);
} }
writer.borrow_mut().start_file(name, options)?; writer.borrow_mut().start_file_from_path(path, options)?;
for chunk in contents { for chunk in contents {
writer.borrow_mut().write_all(chunk.as_slice())?; writer.borrow_mut().write_all(chunk.as_slice())?;
} }
} }
BasicFileOperation::WriteDirectory(options) => { BasicFileOperation::WriteDirectory(options) => {
writer.borrow_mut().add_directory(name, options)?; writer.borrow_mut().add_directory_from_path(path, options.to_owned())?;
} }
BasicFileOperation::WriteSymlinkWithTarget { target, options } => { BasicFileOperation::WriteSymlinkWithTarget { target, options } => {
writer writer
.borrow_mut() .borrow_mut()
.add_symlink(name, target.to_string_lossy(), options)?; .add_symlink_from_path(&path, target, options.to_owned())?;
} }
BasicFileOperation::ShallowCopy(base) => { BasicFileOperation::ShallowCopy(base) => {
let base_name = base.referenceable_name(); do_operation(writer, &base, false, flush_on_finish_file)?;
do_operation(writer, *base, false, flush_on_finish_file)?; writer.borrow_mut().shallow_copy_file_from_path(&base.path, &path)?;
writer.borrow_mut().shallow_copy_file(&base_name, &name)?;
} }
BasicFileOperation::DeepCopy(base) => { BasicFileOperation::DeepCopy(base) => {
let base_name = base.referenceable_name(); do_operation(writer, &base, false, flush_on_finish_file)?;
do_operation(writer, *base, false, flush_on_finish_file)?; writer.borrow_mut().deep_copy_file_from_path(&base.path, &path)?;
writer.borrow_mut().deep_copy_file(&base_name, &name)?;
} }
} }
if abort { if abort {
@ -113,7 +101,7 @@ fuzz_target!(|test_case: FuzzTestCase| {
for (operation, abort) in test_case.operations { for (operation, abort) in test_case.operations {
let _ = do_operation( let _ = do_operation(
&mut writer, &mut writer,
operation, &operation,
abort, abort,
test_case.flush_on_finish_file, test_case.flush_on_finish_file,
); );

View file

@ -594,6 +594,16 @@ impl<A: Read + Write + Seek> ZipWriter<A> {
} }
self.finish_file() self.finish_file()
} }
/// Like `deep_copy_file`, but uses Path arguments.
///
/// This function ensures that the '/' path separator is used and normalizes `.` and `..`. It
/// ignores any `..` or Windows drive letter that would produce a path outside the ZIP file's
/// root.
pub fn deep_copy_file_from_path<T: AsRef<Path>, U: AsRef<Path>>(&mut self, src_path: T, dest_path: U) -> ZipResult<()>
{
self.deep_copy_file(&*path_to_string(src_path), &*path_to_string(dest_path))
}
} }
impl<W: Write + Seek> ZipWriter<W> { impl<W: Write + Seek> ZipWriter<W> {
@ -997,6 +1007,15 @@ impl<W: Write + Seek> ZipWriter<W> {
Ok(()) Ok(())
} }
/// Like `raw_copy_file_to_path`, but uses Path arguments.
///
/// This function ensures that the '/' path separator is used and normalizes `.` and `..`. It
/// ignores any `..` or Windows drive letter that would produce a path outside the ZIP file's
/// root.
pub fn raw_copy_file_to_path<P: AsRef<Path>>(&mut self, file: ZipFile, path: P) -> ZipResult<()> {
self.raw_copy_file_rename(file, path_to_string(path))
}
/// Add a new file using the already compressed data from a ZIP file being read, this allows faster /// Add a new file using the already compressed data from a ZIP file being read, this allows faster
/// copies of the `ZipFile` since there is no need to decompress and compress it again. Any `ZipFile` /// copies of the `ZipFile` since there is no need to decompress and compress it again. Any `ZipFile`
/// metadata is copied and not checked, for example the file CRC. /// metadata is copied and not checked, for example the file CRC.
@ -1219,6 +1238,16 @@ impl<W: Write + Seek> ZipWriter<W> {
self.insert_file_data(dest_data)?; self.insert_file_data(dest_data)?;
Ok(()) Ok(())
} }
/// Like `shallow_copy_file`, but uses Path arguments.
///
/// This function ensures that the '/' path separator is used and normalizes `.` and `..`. It
/// ignores any `..` or Windows drive letter that would produce a path outside the ZIP file's
/// root.
pub fn shallow_copy_file_from_path<T: AsRef<Path>, U: AsRef<Path>>(&mut self, src_path: T, dest_path: U) -> ZipResult<()>
{
self.shallow_copy_file(&*path_to_string(src_path), &*path_to_string(dest_path))
}
} }
impl<W: Write + Seek> Drop for ZipWriter<W> { impl<W: Write + Seek> Drop for ZipWriter<W> {