diff --git a/Cargo.toml b/Cargo.toml index 0bf103b2b..e2531ce00 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ rustdoc-args = ["--cfg", "docsrs"] members = [ ".", "cli", + "fuzz", ] default-members = [ ".", @@ -34,8 +35,9 @@ default-members = [ resolver = "2" [workspace.dependencies] +arbitrary = { version = "1.3.2", features = ["derive"] } time = { version = "0.3.36", default-features = false } -zip = { path = "." } +zip = { path = ".", default-features = false } [dependencies] aes = { version = "0.8.4", optional = true } @@ -65,7 +67,7 @@ lzma-rs = { version = "0.3.0", default-features = false, optional = true } crossbeam-utils = "0.8.20" [target.'cfg(fuzzing)'.dependencies] -arbitrary = { version = "1.3.2", features = ["derive"] } +arbitrary.workspace = true [dev-dependencies] bencher = "0.1.5" @@ -117,5 +119,12 @@ harness = false # Reduce the size of the zip-cli binary. [profile.release] strip = true -lto = true +# This is necessary for fuzzing, which can only use dev or release profiles, and breaks if LTO +# is specified. +lto = false opt-level = "z" + +[profile.release-lto] +inherits = "release" +# This slightly reduces the size of the output binary. +lto = true diff --git a/cli/Cargo.toml b/cli/Cargo.toml index ccba22458..8e70b38ec 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zip-cli" -version = "0.0.0" +version = "0.0.1" authors = [ "Danny McClanahan ", ] @@ -8,7 +8,8 @@ license = "MIT" repository = "https://github.com/zip-rs/zip2.git" keywords = ["zip", "archive", "compression", "cli"] categories = ["command-line-utilities", "compression", "filesystem", "development-tools::build-utils"] -rust-version = "1.73.0" +# Keep this up to date with clap! +rust-version = "1.74.0" description = """ Binary for creation and manipulation of zip files. """ @@ -18,7 +19,16 @@ edition = "2021" name = "zip-cli" [dependencies] -zip.workspace = true - clap = { version = "4.5.15", features = ["derive"] } eyre = "0.6" + +[dependencies.zip] +workspace = true +features = [ + "bzip2", + "deflate64", + "deflate", + "lzma", + "zstd", + "xz", +] diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index be1552cd0..fb7b99ae0 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -9,23 +9,17 @@ edition = "2018" cargo-fuzz = true [dependencies] +zip.workspace = true + libfuzzer-sys = "0.4" -arbitrary = { version = "1.3.2", features = ["derive"] } +arbitrary.workspace = true replace_with = "0.1.7" tikv-jemallocator = "0.6.0" -[dependencies.zip] -path = ".." -default-features = false - [features] zip_defaults = ["zip/default"] default = ["zip_defaults"] -# Prevent this from interfering with workspaces -[workspace] -members = ["."] - [[bin]] name = "fuzz_read" path = "fuzz_targets/fuzz_read.rs" diff --git a/fuzz/fuzz_targets/fuzz_write.rs b/fuzz/fuzz_targets/fuzz_write.rs index 53653a60b..c3cc9089a 100755 --- a/fuzz/fuzz_targets/fuzz_write.rs +++ b/fuzz/fuzz_targets/fuzz_write.rs @@ -1,12 +1,12 @@ #![no_main] use arbitrary::Arbitrary; -use core::fmt::{Debug}; +use core::fmt::Debug; use libfuzzer_sys::fuzz_target; use replace_with::replace_with_or_abort; use std::fmt::{Arguments, Formatter, Write}; -use std::io::{Cursor, Seek, SeekFrom}; use std::io::Write as IoWrite; +use std::io::{Cursor, Seek, SeekFrom}; use std::path::PathBuf; use tikv_jemallocator::Jemalloc; use zip::result::{ZipError, ZipResult}; @@ -93,22 +93,36 @@ fn do_operation<'k>( flush_on_finish_file: bool, files_added: &mut usize, stringifier: &mut impl Write, - panic_on_error: bool + panic_on_error: bool, ) -> Result<(), Box> { writer.set_flush_on_finish_file(flush_on_finish_file); - let FileOperation { basic, mut path, reopen} = operation; + let FileOperation { + basic, + mut path, + reopen, + } = operation; match basic { BasicFileOperation::WriteNormalFile { - contents, mut options, .. + contents, + mut options, + .. } => { let uncompressed_size = contents.iter().map(|chunk| chunk.len()).sum::(); if uncompressed_size >= u32::MAX as usize { options = options.large_file(true); } if options == FullFileOptions::default() { - writeln!(stringifier, "writer.start_file_from_path({:?}, Default::default())?;", path)?; + writeln!( + stringifier, + "writer.start_file_from_path({:?}, Default::default())?;", + path + )?; } else { - writeln!(stringifier, "writer.start_file_from_path({:?}, {:?})?;", path, options)?; + writeln!( + stringifier, + "writer.start_file_from_path({:?}, {:?})?;", + path, options + )?; } writer.start_file_from_path(&*path, options)?; for chunk in contents.iter() { @@ -118,12 +132,20 @@ fn do_operation<'k>( *files_added += 1; } BasicFileOperation::WriteDirectory(options) => { - writeln!(stringifier, "writer.add_directory_from_path(&{:?}, {:?})?;", path, options)?; + writeln!( + stringifier, + "writer.add_directory_from_path(&{:?}, {:?})?;", + path, options + )?; writer.add_directory_from_path(&*path, options.to_owned())?; *files_added += 1; } BasicFileOperation::WriteSymlinkWithTarget { target, options } => { - writeln!(stringifier, "writer.add_symlink_from_path(&{:?}, {:?}, {:?});", path, target, options)?; + writeln!( + stringifier, + "writer.add_symlink_from_path(&{:?}, {:?}, {:?});", + path, target, options + )?; writer.add_symlink_from_path(&*path, target, options.to_owned())?; *files_added += 1; } @@ -132,8 +154,20 @@ fn do_operation<'k>( return Ok(()); }; deduplicate_paths(&mut path, &base_path); - do_operation(writer, *base, false, flush_on_finish_file, files_added, stringifier, panic_on_error)?; - writeln!(stringifier, "writer.shallow_copy_file_from_path({:?}, {:?});", base_path, path)?; + do_operation( + writer, + *base, + false, + flush_on_finish_file, + files_added, + stringifier, + panic_on_error, + )?; + writeln!( + stringifier, + "writer.shallow_copy_file_from_path({:?}, {:?});", + base_path, path + )?; writer.shallow_copy_file_from_path(&*base_path, &*path)?; *files_added += 1; } @@ -142,38 +176,65 @@ fn do_operation<'k>( return Ok(()); }; deduplicate_paths(&mut path, &base_path); - do_operation(writer, *base, false, flush_on_finish_file, files_added, stringifier, panic_on_error)?; - writeln!(stringifier, "writer.deep_copy_file_from_path({:?}, {:?});", base_path, path)?; + do_operation( + writer, + *base, + false, + flush_on_finish_file, + files_added, + stringifier, + panic_on_error, + )?; + writeln!( + stringifier, + "writer.deep_copy_file_from_path({:?}, {:?});", + base_path, path + )?; writer.deep_copy_file_from_path(&*base_path, path)?; *files_added += 1; } - BasicFileOperation::MergeWithOtherFile { operations, initial_junk } => { + BasicFileOperation::MergeWithOtherFile { + operations, + initial_junk, + } => { if initial_junk.is_empty() { - writeln!(stringifier, "let sub_writer = {{\n\ - let mut writer = ZipWriter::new(Cursor::new(Vec::new()));")?; + writeln!( + stringifier, + "let sub_writer = {{\n\ + let mut writer = ZipWriter::new(Cursor::new(Vec::new()));" + )?; } else { - writeln!(stringifier, - "let sub_writer = {{\n\ + writeln!( + stringifier, + "let sub_writer = {{\n\ let mut initial_junk = Cursor::new(vec!{:?});\n\ initial_junk.seek(SeekFrom::End(0))?; - let mut writer = ZipWriter::new(initial_junk);", initial_junk)?; + let mut writer = ZipWriter::new(initial_junk);", + initial_junk + )?; } let mut initial_junk = Cursor::new(initial_junk.into_vec()); initial_junk.seek(SeekFrom::End(0))?; let mut other_writer = zip::ZipWriter::new(initial_junk); let mut inner_files_added = 0; - operations.into_vec().into_iter().for_each(|(operation, abort)| { - let _ = do_operation( - &mut other_writer, - operation, - abort, - false, - &mut inner_files_added, - stringifier, - panic_on_error - ); - }); - writeln!(stringifier, "writer\n}};\nwriter.merge_archive(sub_writer.finish_into_readable()?)?;")?; + operations + .into_vec() + .into_iter() + .for_each(|(operation, abort)| { + let _ = do_operation( + &mut other_writer, + operation, + abort, + false, + &mut inner_files_added, + stringifier, + panic_on_error, + ); + }); + writeln!( + stringifier, + "writer\n}};\nwriter.merge_archive(sub_writer.finish_into_readable()?)?;" + )?; writer.merge_archive(other_writer.finish_into_readable()?)?; *files_added += inner_files_added; } @@ -193,15 +254,19 @@ fn do_operation<'k>( match reopen { ReopenOption::DoNotReopen => { writeln!(stringifier, "writer")?; - return Ok(()) - }, + return Ok(()); + } ReopenOption::ViaFinish => { let old_comment = writer.get_raw_comment().to_owned(); - writeln!(stringifier, "let mut writer = ZipWriter::new_append(writer.finish()?)?;")?; + writeln!( + stringifier, + "let mut writer = ZipWriter::new_append(writer.finish()?)?;" + )?; replace_with_or_abort(writer, |old_writer: zip::ZipWriter>>| { (|| -> ZipResult>>> { zip::ZipWriter::new_append(old_writer.finish()?) - })().unwrap_or_else(|_| { + })() + .unwrap_or_else(|_| { if panic_on_error { panic!("Failed to create new ZipWriter") } @@ -214,11 +279,15 @@ fn do_operation<'k>( } ReopenOption::ViaFinishIntoReadable => { let old_comment = writer.get_raw_comment().to_owned(); - writeln!(stringifier, "let mut writer = ZipWriter::new_append(writer.finish()?)?;")?; + writeln!( + stringifier, + "let mut writer = ZipWriter::new_append(writer.finish()?)?;" + )?; replace_with_or_abort(writer, |old_writer| { (|| -> ZipResult>>> { zip::ZipWriter::new_append(old_writer.finish()?) - })().unwrap_or_else(|_| { + })() + .unwrap_or_else(|_| { if panic_on_error { panic!("Failed to create new ZipWriter") } @@ -231,7 +300,7 @@ fn do_operation<'k>( Ok(()) } -impl <'k> FuzzTestCase<'k> { +impl<'k> FuzzTestCase<'k> { fn execute(self, stringifier: &mut impl Write, panic_on_error: bool) -> ZipResult<()> { let mut initial_junk = Cursor::new(self.initial_junk.into_vec()); initial_junk.seek(SeekFrom::End(0))?; @@ -253,7 +322,7 @@ impl <'k> FuzzTestCase<'k> { self.flush_on_finish_file, &mut files_added, stringifier, - panic_on_error + panic_on_error, ); } if final_reopen { @@ -265,14 +334,21 @@ impl <'k> FuzzTestCase<'k> { } } -impl <'k> Debug for FuzzTestCase<'k> { +impl<'k> Debug for FuzzTestCase<'k> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { if self.initial_junk.is_empty() { - writeln!(f, "let mut writer = ZipWriter::new(Cursor::new(Vec::new()));")?; + writeln!( + f, + "let mut writer = ZipWriter::new(Cursor::new(Vec::new()));" + )?; } else { - writeln!(f, "let mut initial_junk = Cursor::new(vec!{:?});\n\ + writeln!( + f, + "let mut initial_junk = Cursor::new(vec!{:?});\n\ initial_junk.seek(SeekFrom::End(0))?;\n\ - let mut writer = ZipWriter::new(initial_junk);", &self.initial_junk)?; + let mut writer = ZipWriter::new(initial_junk);", + &self.initial_junk + )?; } let _ = self.clone().execute(f, false); Ok(())