Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion fuzz_write/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::io::Write as IoWrite;
use std::io::{Cursor, Seek, SeekFrom};
use std::path::PathBuf;
use tikv_jemallocator::Jemalloc;
use zip::read::read_zipfile_from_stream;
use zip::result::{ZipError, ZipResult};
use zip::unstable::path_to_string;
use zip::write::FullFileOptions;
Expand Down Expand Up @@ -61,6 +62,28 @@ impl FileOperation<'_> {
_ => Some(self.path.to_owned()),
}
}

fn is_streamable(&self) -> bool {
match &self.basic {
BasicFileOperation::WriteNormalFile { options, .. }
| BasicFileOperation::WriteDirectory(options)
| BasicFileOperation::WriteSymlinkWithTarget { options, .. } => {
!options.has_encryption()
}
BasicFileOperation::ShallowCopy(base) => base.is_streamable(),
BasicFileOperation::DeepCopy(base) => base.is_streamable(),
BasicFileOperation::MergeWithOtherFile {
operations,
initial_junk,
} => {
if !initial_junk.is_empty() {
return false;
}
operations.iter().all(|(op, _)| op.is_streamable())
}
_ => true,
}
}
}

#[derive(Arbitrary, Clone)]
Expand Down Expand Up @@ -293,6 +316,9 @@ fn do_operation(

impl FuzzTestCase<'_> {
fn execute(self, stringifier: &mut impl Write, panic_on_error: bool) -> ZipResult<()> {
// Indicates the starting position if we use read_zipfile_from_stream at the end.
let junk_len = self.initial_junk.len();

let mut initial_junk = Cursor::new(self.initial_junk.into_vec());
initial_junk.seek(SeekFrom::End(0))?;
let mut writer = zip::ZipWriter::new(initial_junk);
Expand All @@ -303,6 +329,7 @@ impl FuzzTestCase<'_> {
final_reopen = true;
}
}
let streamable = self.operations.iter().all(|(op, _)| op.is_streamable());
#[allow(unknown_lints)]
#[allow(boxed_slice_into_iter)]
for (operation, abort) in self.operations.into_vec().into_iter() {
Expand All @@ -316,7 +343,18 @@ impl FuzzTestCase<'_> {
panic_on_error,
);
}
if final_reopen {
if streamable {
writeln!(
stringifier,
"let mut stream = writer.finish()?;\n\
stream.seek(SeekFrom::Start({junk_len}))?;\n\
while read_zipfile_from_stream(&mut stream)?.is_some() {{}}"
)
.map_err(|_| ZipError::InvalidArchive("Failed to read from stream".into()))?;
let mut stream = writer.finish()?;
stream.seek(SeekFrom::Start(junk_len as u64))?;
while read_zipfile_from_stream(&mut stream)?.is_some() {}
} else if final_reopen {
writeln!(stringifier, "let _ = writer.finish_into_readable()?;")
.map_err(|_| ZipError::InvalidArchive("".into()))?;
let _ = writer.finish_into_readable()?;
Expand Down
12 changes: 12 additions & 0 deletions src/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,18 @@ impl<T: FileOptionExtension> FileOptions<'_, T> {
*self.permissions.get_or_insert(0o644) |= ffi::S_IFREG;
}

/// Indicates whether this file will be encrypted (whether with AES or ZipCrypto).
pub const fn has_encryption(&self) -> bool {
#[cfg(feature = "aes-crypto")]
{
self.encrypt_with.is_some() || self.aes_mode.is_some()
}
#[cfg(not(feature = "aes-crypto"))]
{
self.encrypt_with.is_some()
}
}

/// Set the compression method for the new file
///
/// The default is `CompressionMethod::Deflated` if it is enabled. If not,
Expand Down
Loading