diff --git a/CHANGELOG.md b/CHANGELOG.md index 53b5148ab..fc1570ce0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 initializing metadata from the local-file header or needing to mutably borrow the `ZipArchive`. - - `add_symlink_from_path`: create a symlink using `AsRef` 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` arguments ### Changed diff --git a/fuzz/fuzz_targets/fuzz_write.rs b/fuzz/fuzz_targets/fuzz_write.rs index f6a079e97..3f03edb56 100644 --- a/fuzz/fuzz_targets/fuzz_write.rs +++ b/fuzz/fuzz_targets/fuzz_write.rs @@ -14,7 +14,7 @@ pub enum BasicFileOperation { }, WriteDirectory(zip::write::FullFileOptions), WriteSymlinkWithTarget { - target: Box, + target: PathBuf, options: zip::write::FullFileOptions, }, ShallowCopy(Box), @@ -24,7 +24,7 @@ pub enum BasicFileOperation { #[derive(Arbitrary, Clone, Debug)] pub struct FileOperation { basic: BasicFileOperation, - name: String, + path: PathBuf, reopen: bool, // 'abort' flag is separate, to prevent trying to copy an aborted file } @@ -36,20 +36,9 @@ pub struct FuzzTestCase { 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( writer: &mut RefCell>, - operation: FileOperation, + operation: &FileOperation, abort: bool, flush_on_finish_file: bool, ) -> Result<(), Box> @@ -59,39 +48,38 @@ where writer .borrow_mut() .set_flush_on_finish_file(flush_on_finish_file); - let name = operation.name; - match operation.basic { + let path = &operation.path; + match &operation.basic { BasicFileOperation::WriteNormalFile { contents, - mut options, + options, .. } => { let uncompressed_size = contents.iter().map(Vec::len).sum::(); + let mut options = (*options).to_owned(); if uncompressed_size >= u32::MAX as usize { 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 { writer.borrow_mut().write_all(chunk.as_slice())?; } } 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 } => { writer .borrow_mut() - .add_symlink(name, target.to_string_lossy(), options)?; + .add_symlink_from_path(&path, target, options.to_owned())?; } BasicFileOperation::ShallowCopy(base) => { - let base_name = base.referenceable_name(); - do_operation(writer, *base, false, flush_on_finish_file)?; - writer.borrow_mut().shallow_copy_file(&base_name, &name)?; + do_operation(writer, &base, false, flush_on_finish_file)?; + writer.borrow_mut().shallow_copy_file_from_path(&base.path, &path)?; } BasicFileOperation::DeepCopy(base) => { - let base_name = base.referenceable_name(); - do_operation(writer, *base, false, flush_on_finish_file)?; - writer.borrow_mut().deep_copy_file(&base_name, &name)?; + do_operation(writer, &base, false, flush_on_finish_file)?; + writer.borrow_mut().deep_copy_file_from_path(&base.path, &path)?; } } if abort { @@ -113,7 +101,7 @@ fuzz_target!(|test_case: FuzzTestCase| { for (operation, abort) in test_case.operations { let _ = do_operation( &mut writer, - operation, + &operation, abort, test_case.flush_on_finish_file, ); diff --git a/src/write.rs b/src/write.rs index 7af5e6f68..7cbaea89f 100644 --- a/src/write.rs +++ b/src/write.rs @@ -594,6 +594,16 @@ impl ZipWriter { } 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, U: AsRef>(&mut self, src_path: T, dest_path: U) -> ZipResult<()> + { + self.deep_copy_file(&*path_to_string(src_path), &*path_to_string(dest_path)) + } } impl ZipWriter { @@ -997,6 +1007,15 @@ impl ZipWriter { 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>(&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 /// 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. @@ -1219,6 +1238,16 @@ impl ZipWriter { self.insert_file_data(dest_data)?; 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, U: AsRef>(&mut self, src_path: T, dest_path: U) -> ZipResult<()> + { + self.shallow_copy_file(&*path_to_string(src_path), &*path_to_string(dest_path)) + } } impl Drop for ZipWriter {