1#[cfg(feature = "aes-crypto")]
4use crate::aes::AesWriter;
5use crate::compression::CompressionMethod;
6use crate::read::{parse_single_extra_field, Config, ZipArchive, ZipFile};
7use crate::result::{ZipError, ZipResult};
8use crate::spec::{self, FixedSizeBlock, Zip32CDEBlock};
9#[cfg(feature = "aes-crypto")]
10use crate::types::AesMode;
11use crate::types::{
12 ffi, AesVendorVersion, DateTime, Zip64ExtraFieldBlock, ZipFileData, ZipLocalEntryBlock,
13 ZipRawValues, MIN_VERSION,
14};
15use crate::write::ffi::S_IFLNK;
16#[cfg(any(feature = "_deflate-any", feature = "bzip2", feature = "zstd",))]
17use core::num::NonZeroU64;
18use crc32fast::Hasher;
19use indexmap::IndexMap;
20use std::borrow::ToOwned;
21use std::default::Default;
22use std::fmt::{Debug, Formatter};
23use std::io;
24use std::io::prelude::*;
25use std::io::Cursor;
26use std::io::{BufReader, SeekFrom};
27use std::marker::PhantomData;
28use std::mem;
29use std::str::{from_utf8, Utf8Error};
30use std::sync::Arc;
31
32#[cfg(feature = "deflate-flate2")]
33use flate2::{write::DeflateEncoder, Compression};
34
35#[cfg(feature = "bzip2")]
36use bzip2::write::BzEncoder;
37
38#[cfg(feature = "deflate-zopfli")]
39use zopfli::Options;
40
41#[cfg(feature = "deflate-zopfli")]
42use std::io::BufWriter;
43use std::mem::size_of;
44use std::path::Path;
45
46#[cfg(feature = "zstd")]
47use zstd::stream::write::Encoder as ZstdEncoder;
48
49enum MaybeEncrypted<W> {
50 Unencrypted(W),
51 #[cfg(feature = "aes-crypto")]
52 Aes(AesWriter<W>),
53 ZipCrypto(crate::zipcrypto::ZipCryptoWriter<W>),
54}
55
56impl<W> Debug for MaybeEncrypted<W> {
57 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
58 f.write_str(match self {
60 MaybeEncrypted::Unencrypted(_) => "Unencrypted",
61 #[cfg(feature = "aes-crypto")]
62 MaybeEncrypted::Aes(_) => "AES",
63 MaybeEncrypted::ZipCrypto(_) => "ZipCrypto",
64 })
65 }
66}
67
68impl<W: Write> Write for MaybeEncrypted<W> {
69 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
70 match self {
71 MaybeEncrypted::Unencrypted(w) => w.write(buf),
72 #[cfg(feature = "aes-crypto")]
73 MaybeEncrypted::Aes(w) => w.write(buf),
74 MaybeEncrypted::ZipCrypto(w) => w.write(buf),
75 }
76 }
77 fn flush(&mut self) -> io::Result<()> {
78 match self {
79 MaybeEncrypted::Unencrypted(w) => w.flush(),
80 #[cfg(feature = "aes-crypto")]
81 MaybeEncrypted::Aes(w) => w.flush(),
82 MaybeEncrypted::ZipCrypto(w) => w.flush(),
83 }
84 }
85}
86
87enum GenericZipWriter<W: Write + Seek> {
88 Closed,
89 Storer(MaybeEncrypted<W>),
90 #[cfg(feature = "deflate-flate2")]
91 Deflater(DeflateEncoder<MaybeEncrypted<W>>),
92 #[cfg(feature = "deflate-zopfli")]
93 ZopfliDeflater(zopfli::DeflateEncoder<MaybeEncrypted<W>>),
94 #[cfg(feature = "deflate-zopfli")]
95 BufferedZopfliDeflater(BufWriter<zopfli::DeflateEncoder<MaybeEncrypted<W>>>),
96 #[cfg(feature = "bzip2")]
97 Bzip2(BzEncoder<MaybeEncrypted<W>>),
98 #[cfg(feature = "zstd")]
99 Zstd(ZstdEncoder<'static, MaybeEncrypted<W>>),
100 #[cfg(feature = "xz")]
101 Xz(xz2::write::XzEncoder<MaybeEncrypted<W>>),
102}
103
104impl<W: Write + Seek> Debug for GenericZipWriter<W> {
105 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
106 match self {
107 Closed => f.write_str("Closed"),
108 Storer(w) => f.write_fmt(format_args!("Storer({:?})", w)),
109 #[cfg(feature = "deflate-flate2")]
110 GenericZipWriter::Deflater(w) => {
111 f.write_fmt(format_args!("Deflater({:?})", w.get_ref()))
112 }
113 #[cfg(feature = "deflate-zopfli")]
114 GenericZipWriter::ZopfliDeflater(_) => f.write_str("ZopfliDeflater"),
115 #[cfg(feature = "deflate-zopfli")]
116 GenericZipWriter::BufferedZopfliDeflater(_) => f.write_str("BufferedZopfliDeflater"),
117 #[cfg(feature = "bzip2")]
118 GenericZipWriter::Bzip2(w) => f.write_fmt(format_args!("Bzip2({:?})", w.get_ref())),
119 #[cfg(feature = "zstd")]
120 GenericZipWriter::Zstd(w) => f.write_fmt(format_args!("Zstd({:?})", w.get_ref())),
121 #[cfg(feature = "xz")]
122 GenericZipWriter::Xz(w) => f.write_fmt(format_args!("Xz({:?})", w.get_ref())),
123 }
124 }
125}
126
127pub(crate) mod zip_writer {
129 use super::*;
130 pub struct ZipWriter<W: Write + Seek> {
159 pub(super) inner: GenericZipWriter<W>,
160 pub(super) files: IndexMap<Box<str>, ZipFileData>,
161 pub(super) stats: ZipWriterStats,
162 pub(super) writing_to_file: bool,
163 pub(super) writing_raw: bool,
164 pub(super) comment: Box<[u8]>,
165 pub(super) zip64_comment: Option<Box<[u8]>>,
166 pub(super) flush_on_finish_file: bool,
167 }
168
169 impl<W: Write + Seek> Debug for ZipWriter<W> {
170 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
171 f.write_fmt(format_args!(
172 "ZipWriter {{files: {:?}, stats: {:?}, writing_to_file: {}, writing_raw: {}, comment: {:?}, flush_on_finish_file: {}}}",
173 self.files, self.stats, self.writing_to_file, self.writing_raw,
174 self.comment, self.flush_on_finish_file))
175 }
176 }
177}
178#[doc(inline)]
179pub use self::sealed::FileOptionExtension;
180use crate::result::ZipError::{InvalidArchive, UnsupportedArchive};
181use crate::unstable::path_to_string;
182use crate::unstable::LittleEndianWriteExt;
183use crate::write::GenericZipWriter::{Closed, Storer};
184use crate::zipcrypto::ZipCryptoKeys;
185use crate::CompressionMethod::Stored;
186pub use zip_writer::ZipWriter;
187
188#[derive(Default, Debug)]
189struct ZipWriterStats {
190 hasher: Hasher,
191 start: u64,
192 bytes_written: u64,
193}
194
195mod sealed {
196 use std::sync::Arc;
197
198 use super::ExtendedFileOptions;
199
200 pub trait Sealed {}
201 #[doc(hidden)]
203 pub trait FileOptionExtension: Default + Sealed {
204 fn extra_data(&self) -> Option<&Arc<Vec<u8>>>;
206 fn central_extra_data(&self) -> Option<&Arc<Vec<u8>>>;
208 }
209 impl Sealed for () {}
210 impl FileOptionExtension for () {
211 fn extra_data(&self) -> Option<&Arc<Vec<u8>>> {
212 None
213 }
214 fn central_extra_data(&self) -> Option<&Arc<Vec<u8>>> {
215 None
216 }
217 }
218 impl Sealed for ExtendedFileOptions {}
219
220 impl FileOptionExtension for ExtendedFileOptions {
221 fn extra_data(&self) -> Option<&Arc<Vec<u8>>> {
222 Some(&self.extra_data)
223 }
224 fn central_extra_data(&self) -> Option<&Arc<Vec<u8>>> {
225 Some(&self.central_extra_data)
226 }
227 }
228}
229
230#[derive(Copy, Clone, Debug, Eq, PartialEq)]
231pub(crate) enum EncryptWith<'k> {
232 #[cfg(feature = "aes-crypto")]
233 Aes {
234 mode: AesMode,
235 password: &'k str,
236 },
237 ZipCrypto(ZipCryptoKeys, PhantomData<&'k ()>),
238}
239
240#[cfg(fuzzing)]
241impl<'a> arbitrary::Arbitrary<'a> for EncryptWith<'a> {
242 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
243 #[cfg(feature = "aes-crypto")]
244 if bool::arbitrary(u)? {
245 return Ok(EncryptWith::Aes {
246 mode: AesMode::arbitrary(u)?,
247 password: u.arbitrary::<&str>()?,
248 });
249 }
250
251 Ok(EncryptWith::ZipCrypto(
252 ZipCryptoKeys::arbitrary(u)?,
253 PhantomData,
254 ))
255 }
256}
257
258#[derive(Clone, Debug, Copy, Eq, PartialEq)]
260pub struct FileOptions<'k, T: FileOptionExtension> {
261 pub(crate) compression_method: CompressionMethod,
262 pub(crate) compression_level: Option<i64>,
263 pub(crate) last_modified_time: DateTime,
264 pub(crate) permissions: Option<u32>,
265 pub(crate) large_file: bool,
266 pub(crate) encrypt_with: Option<EncryptWith<'k>>,
267 pub(crate) extended_options: T,
268 pub(crate) alignment: u16,
269 #[cfg(feature = "deflate-zopfli")]
270 pub(super) zopfli_buffer_size: Option<usize>,
271}
272pub type SimpleFileOptions = FileOptions<'static, ()>;
274pub type FullFileOptions<'k> = FileOptions<'k, ExtendedFileOptions>;
276#[derive(Clone, Default, Eq, PartialEq)]
278pub struct ExtendedFileOptions {
279 extra_data: Arc<Vec<u8>>,
280 central_extra_data: Arc<Vec<u8>>,
281}
282
283impl ExtendedFileOptions {
284 pub fn add_extra_data(
286 &mut self,
287 header_id: u16,
288 data: Box<[u8]>,
289 central_only: bool,
290 ) -> ZipResult<()> {
291 let len = data.len() + 4;
292 if self.extra_data.len() + self.central_extra_data.len() + len > u16::MAX as usize {
293 Err(InvalidArchive(
294 "Extra data field would be longer than allowed",
295 ))
296 } else {
297 let field = if central_only {
298 &mut self.central_extra_data
299 } else {
300 &mut self.extra_data
301 };
302 let vec = Arc::get_mut(field);
303 let vec = match vec {
304 Some(exclusive) => exclusive,
305 None => {
306 *field = Arc::new(field.to_vec());
307 Arc::get_mut(field).unwrap()
308 }
309 };
310 Self::add_extra_data_unchecked(vec, header_id, data)?;
311 Self::validate_extra_data(vec, true)?;
312 Ok(())
313 }
314 }
315
316 pub(crate) fn add_extra_data_unchecked(
317 vec: &mut Vec<u8>,
318 header_id: u16,
319 data: Box<[u8]>,
320 ) -> Result<(), ZipError> {
321 vec.reserve_exact(data.len() + 4);
322 vec.write_u16_le(header_id)?;
323 vec.write_u16_le(data.len() as u16)?;
324 vec.write_all(&data)?;
325 Ok(())
326 }
327
328 fn validate_extra_data(data: &[u8], disallow_zip64: bool) -> ZipResult<()> {
329 let len = data.len() as u64;
330 if len == 0 {
331 return Ok(());
332 }
333 if len > u16::MAX as u64 {
334 return Err(ZipError::Io(io::Error::new(
335 io::ErrorKind::Other,
336 "Extra-data field can't exceed u16::MAX bytes",
337 )));
338 }
339 let mut data = Cursor::new(data);
340 let mut pos = data.position();
341 while pos < len {
342 if len - data.position() < 4 {
343 return Err(ZipError::Io(io::Error::new(
344 io::ErrorKind::Other,
345 "Extra-data field doesn't have room for ID and length",
346 )));
347 }
348 #[cfg(not(feature = "unreserved"))]
349 {
350 use crate::unstable::LittleEndianReadExt;
351 let header_id = data.read_u16_le()?;
352 if EXTRA_FIELD_MAPPING.contains(&header_id) {
353 return Err(ZipError::Io(io::Error::new(
354 io::ErrorKind::Other,
355 format!(
356 "Extra data header ID {header_id:#06} requires crate feature \"unreserved\"",
357 ),
358 )));
359 }
360 data.seek(SeekFrom::Current(-2))?;
361 }
362 parse_single_extra_field(&mut ZipFileData::default(), &mut data, pos, disallow_zip64)?;
363 pos = data.position();
364 }
365 Ok(())
366 }
367}
368
369impl Debug for ExtendedFileOptions {
370 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
371 f.write_fmt(format_args!("ExtendedFileOptions {{extra_data: vec!{:?}.into(), central_extra_data: vec!{:?}.into()}}",
372 self.extra_data, self.central_extra_data))
373 }
374}
375
376#[cfg(fuzzing)]
377impl<'a> arbitrary::Arbitrary<'a> for FileOptions<'a, ExtendedFileOptions> {
378 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
379 let mut options = FullFileOptions {
380 compression_method: CompressionMethod::arbitrary(u)?,
381 compression_level: if bool::arbitrary(u)? {
382 Some(u.int_in_range(0..=24)?)
383 } else {
384 None
385 },
386 last_modified_time: DateTime::arbitrary(u)?,
387 permissions: Option::<u32>::arbitrary(u)?,
388 large_file: bool::arbitrary(u)?,
389 encrypt_with: Option::<EncryptWith>::arbitrary(u)?,
390 alignment: u16::arbitrary(u)?,
391 #[cfg(feature = "deflate-zopfli")]
392 zopfli_buffer_size: None,
393 ..Default::default()
394 };
395 #[cfg(feature = "deflate-zopfli")]
396 if options.compression_method == CompressionMethod::Deflated && bool::arbitrary(u)? {
397 options.zopfli_buffer_size =
398 Some(if bool::arbitrary(u)? { 2 } else { 3 } << u.int_in_range(8..=20)?);
399 }
400 u.arbitrary_loop(Some(0), Some(10), |u| {
401 options
402 .add_extra_data(
403 u.int_in_range(2..=u16::MAX)?,
404 Box::<[u8]>::arbitrary(u)?,
405 bool::arbitrary(u)?,
406 )
407 .map_err(|_| arbitrary::Error::IncorrectFormat)?;
408 Ok(core::ops::ControlFlow::Continue(()))
409 })?;
410 ZipWriter::new(Cursor::new(Vec::new()))
411 .start_file("", options.clone())
412 .map_err(|_| arbitrary::Error::IncorrectFormat)?;
413 Ok(options)
414 }
415}
416
417impl<T: FileOptionExtension> FileOptions<'_, T> {
418 pub(crate) fn normalize(&mut self) {
419 if !self.last_modified_time.is_valid() {
420 self.last_modified_time = FileOptions::<T>::default().last_modified_time;
421 }
422
423 *self.permissions.get_or_insert(0o644) |= ffi::S_IFREG;
424 }
425
426 #[must_use]
433 pub const fn compression_method(mut self, method: CompressionMethod) -> Self {
434 self.compression_method = method;
435 self
436 }
437
438 #[must_use]
449 pub const fn compression_level(mut self, level: Option<i64>) -> Self {
450 self.compression_level = level;
451 self
452 }
453
454 #[must_use]
459 pub const fn last_modified_time(mut self, mod_time: DateTime) -> Self {
460 self.last_modified_time = mod_time;
461 self
462 }
463
464 #[must_use]
474 pub const fn unix_permissions(mut self, mode: u32) -> Self {
475 self.permissions = Some(mode & 0o777);
476 self
477 }
478
479 #[must_use]
485 pub const fn large_file(mut self, large: bool) -> Self {
486 self.large_file = large;
487 self
488 }
489
490 pub(crate) fn with_deprecated_encryption(self, password: &[u8]) -> FileOptions<'static, T> {
491 FileOptions {
492 encrypt_with: Some(EncryptWith::ZipCrypto(
493 ZipCryptoKeys::derive(password),
494 PhantomData,
495 )),
496 ..self
497 }
498 }
499
500 #[cfg(feature = "aes-crypto")]
502 pub fn with_aes_encryption(self, mode: AesMode, password: &str) -> FileOptions<'_, T> {
503 FileOptions {
504 encrypt_with: Some(EncryptWith::Aes { mode, password }),
505 ..self
506 }
507 }
508
509 #[must_use]
514 #[cfg(feature = "deflate-zopfli")]
515 pub const fn with_zopfli_buffer(mut self, size: Option<usize>) -> Self {
516 self.zopfli_buffer_size = size;
517 self
518 }
519
520 pub const fn get_compression_level(&self) -> Option<i64> {
522 self.compression_level
523 }
524 #[must_use]
526 pub const fn with_alignment(mut self, alignment: u16) -> Self {
527 self.alignment = alignment;
528 self
529 }
530}
531impl FileOptions<'_, ExtendedFileOptions> {
532 pub fn add_extra_data(
534 &mut self,
535 header_id: u16,
536 data: Box<[u8]>,
537 central_only: bool,
538 ) -> ZipResult<()> {
539 self.extended_options
540 .add_extra_data(header_id, data, central_only)
541 }
542
543 #[must_use]
545 pub fn clear_extra_data(mut self) -> Self {
546 if !self.extended_options.extra_data.is_empty() {
547 self.extended_options.extra_data = Arc::new(vec![]);
548 }
549 if !self.extended_options.central_extra_data.is_empty() {
550 self.extended_options.central_extra_data = Arc::new(vec![]);
551 }
552 self
553 }
554}
555impl<T: FileOptionExtension> Default for FileOptions<'_, T> {
556 fn default() -> Self {
558 Self {
559 compression_method: Default::default(),
560 compression_level: None,
561 last_modified_time: DateTime::default_for_write(),
562 permissions: None,
563 large_file: false,
564 encrypt_with: None,
565 extended_options: T::default(),
566 alignment: 1,
567 #[cfg(feature = "deflate-zopfli")]
568 zopfli_buffer_size: Some(1 << 15),
569 }
570 }
571}
572
573impl<W: Write + Seek> Write for ZipWriter<W> {
574 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
575 if !self.writing_to_file {
576 return Err(io::Error::new(
577 io::ErrorKind::Other,
578 "No file has been started",
579 ));
580 }
581 if buf.is_empty() {
582 return Ok(0);
583 }
584 match self.inner.ref_mut() {
585 Some(ref mut w) => {
586 let write_result = w.write(buf);
587 if let Ok(count) = write_result {
588 self.stats.update(&buf[0..count]);
589 if self.stats.bytes_written > spec::ZIP64_BYTES_THR
590 && !self.files.last_mut().unwrap().1.large_file
591 {
592 let _ = self.abort_file();
593 return Err(io::Error::new(
594 io::ErrorKind::Other,
595 "Large file option has not been set",
596 ));
597 }
598 }
599 write_result
600 }
601 None => Err(io::Error::new(
602 io::ErrorKind::BrokenPipe,
603 "write(): ZipWriter was already closed",
604 )),
605 }
606 }
607
608 fn flush(&mut self) -> io::Result<()> {
609 match self.inner.ref_mut() {
610 Some(ref mut w) => w.flush(),
611 None => Err(io::Error::new(
612 io::ErrorKind::BrokenPipe,
613 "flush(): ZipWriter was already closed",
614 )),
615 }
616 }
617}
618
619impl ZipWriterStats {
620 fn update(&mut self, buf: &[u8]) {
621 self.hasher.update(buf);
622 self.bytes_written += buf.len() as u64;
623 }
624}
625
626impl<A: Read + Write + Seek> ZipWriter<A> {
627 pub fn new_append(readwriter: A) -> ZipResult<ZipWriter<A>> {
631 Self::new_append_with_config(Default::default(), readwriter)
632 }
633
634 pub fn new_append_with_config(config: Config, mut readwriter: A) -> ZipResult<ZipWriter<A>> {
638 readwriter.seek(SeekFrom::Start(0))?;
639
640 let shared = ZipArchive::get_metadata(config, &mut readwriter)?;
641
642 Ok(ZipWriter {
643 inner: Storer(MaybeEncrypted::Unencrypted(readwriter)),
644 files: shared.files,
645 stats: Default::default(),
646 writing_to_file: false,
647 comment: shared.comment,
648 zip64_comment: shared.zip64_comment,
649 writing_raw: true, flush_on_finish_file: false,
651 })
652 }
653
654 pub fn set_flush_on_finish_file(&mut self, flush_on_finish_file: bool) {
668 self.flush_on_finish_file = flush_on_finish_file;
669 }
670}
671
672impl<A: Read + Write + Seek> ZipWriter<A> {
673 pub fn deep_copy_file(&mut self, src_name: &str, dest_name: &str) -> ZipResult<()> {
676 self.finish_file()?;
677 if src_name == dest_name || self.files.contains_key(dest_name) {
678 return Err(InvalidArchive("That file already exists"));
679 }
680 let write_position = self.inner.get_plain().stream_position()?;
681 let src_index = self.index_by_name(src_name)?;
682 let src_data = &mut self.files[src_index];
683 let src_data_start = src_data.data_start();
684 debug_assert!(src_data_start <= write_position);
685 let mut compressed_size = src_data.compressed_size;
686 if compressed_size > (write_position - src_data_start) {
687 compressed_size = write_position - src_data_start;
688 src_data.compressed_size = compressed_size;
689 }
690 let mut reader = BufReader::new(self.inner.get_plain());
691 reader.seek(SeekFrom::Start(src_data_start))?;
692 let mut copy = vec![0; compressed_size as usize];
693 reader.take(compressed_size).read_exact(&mut copy)?;
694 self.inner
695 .get_plain()
696 .seek(SeekFrom::Start(write_position))?;
697 let mut new_data = src_data.clone();
698 let dest_name_raw = dest_name.as_bytes();
699 new_data.file_name = dest_name.into();
700 new_data.file_name_raw = dest_name_raw.into();
701 new_data.is_utf8 = !dest_name.is_ascii();
702 new_data.header_start = write_position;
703 let extra_data_start = write_position
704 + size_of::<ZipLocalEntryBlock>() as u64
705 + new_data.file_name_raw.len() as u64;
706 new_data.extra_data_start = Some(extra_data_start);
707 let mut data_start = extra_data_start;
708 if let Some(extra) = &src_data.extra_field {
709 data_start += extra.len() as u64;
710 }
711 new_data.data_start.take();
712 new_data.data_start.get_or_init(|| data_start);
713 new_data.central_header_start = 0;
714 let block = new_data.local_block()?;
715 let index = self.insert_file_data(new_data)?;
716 let new_data = &self.files[index];
717 let result: io::Result<()> = (|| {
718 let plain_writer = self.inner.get_plain();
719 block.write(plain_writer)?;
720 plain_writer.write_all(&new_data.file_name_raw)?;
721 if let Some(data) = &new_data.extra_field {
722 plain_writer.write_all(data)?;
723 }
724 debug_assert_eq!(data_start, plain_writer.stream_position()?);
725 self.writing_to_file = true;
726 plain_writer.write_all(©)?;
727 if self.flush_on_finish_file {
728 plain_writer.flush()?;
729 }
730 Ok(())
731 })();
732 self.ok_or_abort_file(result)?;
733 self.writing_to_file = false;
734 Ok(())
735 }
736
737 pub fn deep_copy_file_from_path<T: AsRef<Path>, U: AsRef<Path>>(
743 &mut self,
744 src_path: T,
745 dest_path: U,
746 ) -> ZipResult<()> {
747 let src = path_to_string(src_path);
748 let dest = path_to_string(dest_path);
749 self.deep_copy_file(&src, &dest)
750 }
751
752 pub fn finish_into_readable(mut self) -> ZipResult<ZipArchive<A>> {
777 let central_start = self.finalize()?;
778 let inner = mem::replace(&mut self.inner, Closed).unwrap();
779 let comment = mem::take(&mut self.comment);
780 let zip64_comment = mem::take(&mut self.zip64_comment);
781 let files = mem::take(&mut self.files);
782
783 let archive =
784 ZipArchive::from_finalized_writer(files, comment, zip64_comment, inner, central_start)?;
785 Ok(archive)
786 }
787}
788
789impl<W: Write + Seek> ZipWriter<W> {
790 pub fn new(inner: W) -> ZipWriter<W> {
796 ZipWriter {
797 inner: Storer(MaybeEncrypted::Unencrypted(inner)),
798 files: IndexMap::new(),
799 stats: Default::default(),
800 writing_to_file: false,
801 writing_raw: false,
802 comment: Box::new([]),
803 zip64_comment: None,
804 flush_on_finish_file: false,
805 }
806 }
807
808 pub const fn is_writing_file(&self) -> bool {
810 self.writing_to_file && !self.inner.is_closed()
811 }
812
813 pub fn set_comment<S>(&mut self, comment: S)
815 where
816 S: Into<Box<str>>,
817 {
818 self.set_raw_comment(comment.into().into_boxed_bytes())
819 }
820
821 pub fn set_raw_comment(&mut self, comment: Box<[u8]>) {
826 self.comment = comment;
827 }
828
829 pub fn get_comment(&mut self) -> Result<&str, Utf8Error> {
831 from_utf8(self.get_raw_comment())
832 }
833
834 pub const fn get_raw_comment(&self) -> &[u8] {
839 &self.comment
840 }
841
842 pub fn set_zip64_comment<S>(&mut self, comment: Option<S>)
844 where
845 S: Into<Box<str>>,
846 {
847 self.set_raw_zip64_comment(comment.map(|v| v.into().into_boxed_bytes()))
848 }
849
850 pub fn set_raw_zip64_comment(&mut self, comment: Option<Box<[u8]>>) {
855 self.zip64_comment = comment;
856 }
857
858 pub fn get_zip64_comment(&mut self) -> Option<Result<&str, Utf8Error>> {
860 self.get_raw_zip64_comment().map(from_utf8)
861 }
862
863 pub fn get_raw_zip64_comment(&self) -> Option<&[u8]> {
868 self.zip64_comment.as_deref()
869 }
870
871 pub unsafe fn set_file_metadata(&mut self, length: u64, crc32: u32) -> ZipResult<()> {
878 if !self.writing_to_file {
879 return Err(ZipError::Io(io::Error::new(
880 io::ErrorKind::Other,
881 "No file has been started",
882 )));
883 }
884 self.stats.hasher = Hasher::new_with_initial_len(crc32, length);
885 self.stats.bytes_written = length;
886 Ok(())
887 }
888
889 fn ok_or_abort_file<T, E: Into<ZipError>>(&mut self, result: Result<T, E>) -> ZipResult<T> {
890 match result {
891 Err(e) => {
892 let _ = self.abort_file();
893 Err(e.into())
894 }
895 Ok(t) => Ok(t),
896 }
897 }
898
899 fn start_entry<S: ToString, T: FileOptionExtension>(
901 &mut self,
902 name: S,
903 options: FileOptions<T>,
904 raw_values: Option<ZipRawValues>,
905 ) -> ZipResult<()> {
906 self.finish_file()?;
907
908 let header_start = self.inner.get_plain().stream_position()?;
909 let raw_values = raw_values.unwrap_or(ZipRawValues {
910 crc32: 0,
911 compressed_size: 0,
912 uncompressed_size: 0,
913 });
914
915 let mut extra_data = match options.extended_options.extra_data() {
916 Some(data) => data.to_vec(),
917 None => vec![],
918 };
919 let central_extra_data = options.extended_options.central_extra_data();
920 if let Some(zip64_block) =
921 Zip64ExtraFieldBlock::maybe_new(options.large_file, 0, 0, header_start)
922 {
923 let mut new_extra_data = zip64_block.serialize().into_vec();
924 new_extra_data.append(&mut extra_data);
925 extra_data = new_extra_data;
926 }
927 #[allow(unused_mut)]
929 let mut aes_extra_data_start = 0;
930 #[cfg(feature = "aes-crypto")]
931 if let Some(EncryptWith::Aes { mode, .. }) = options.encrypt_with {
932 let aes_dummy_extra_data =
933 vec![0x02, 0x00, 0x41, 0x45, mode as u8, 0x00, 0x00].into_boxed_slice();
934 aes_extra_data_start = extra_data.len() as u64;
935 ExtendedFileOptions::add_extra_data_unchecked(
936 &mut extra_data,
937 0x9901,
938 aes_dummy_extra_data,
939 )?;
940 }
941
942 let (compression_method, aes_mode) = match options.encrypt_with {
943 #[cfg(feature = "aes-crypto")]
944 Some(EncryptWith::Aes { mode, .. }) => (
945 CompressionMethod::Aes,
946 Some((mode, AesVendorVersion::Ae2, options.compression_method)),
947 ),
948 _ => (options.compression_method, None),
949 };
950 let header_end =
951 header_start + size_of::<ZipLocalEntryBlock>() as u64 + name.to_string().len() as u64;
952
953 if options.alignment > 1 {
954 let extra_data_end = header_end + extra_data.len() as u64;
955 let align = options.alignment as u64;
956 let unaligned_header_bytes = extra_data_end % align;
957 if unaligned_header_bytes != 0 {
958 let mut pad_length = (align - unaligned_header_bytes) as usize;
959 while pad_length < 6 {
960 pad_length += align as usize;
961 }
962 let mut pad_body = vec![0; pad_length - 4];
964 debug_assert!(pad_body.len() >= 2);
965 [pad_body[0], pad_body[1]] = options.alignment.to_le_bytes();
966 ExtendedFileOptions::add_extra_data_unchecked(
967 &mut extra_data,
968 0xa11e,
969 pad_body.into_boxed_slice(),
970 )?;
971 debug_assert_eq!((extra_data.len() as u64 + header_end) % align, 0);
972 }
973 }
974 let extra_data_len = extra_data.len();
975 if let Some(data) = central_extra_data {
976 if extra_data_len + data.len() > u16::MAX as usize {
977 return Err(InvalidArchive(
978 "Extra data and central extra data must be less than 64KiB when combined",
979 ));
980 }
981 ExtendedFileOptions::validate_extra_data(data, true)?;
982 }
983 let mut file = ZipFileData::initialize_local_block(
984 name,
985 &options,
986 raw_values,
987 header_start,
988 None,
989 aes_extra_data_start,
990 compression_method,
991 aes_mode,
992 &extra_data,
993 );
994 file.version_made_by = file.version_made_by.max(file.version_needed() as u8);
995 file.extra_data_start = Some(header_end);
996 let index = self.insert_file_data(file)?;
997 self.writing_to_file = true;
998 let result: ZipResult<()> = (|| {
999 ExtendedFileOptions::validate_extra_data(&extra_data, false)?;
1000 let file = &mut self.files[index];
1001 let block = file.local_block()?;
1002 let writer = self.inner.get_plain();
1003 block.write(writer)?;
1004 writer.write_all(&file.file_name_raw)?;
1006 if extra_data_len > 0 {
1007 writer.write_all(&extra_data)?;
1008 file.extra_field = Some(extra_data.into());
1009 }
1010 Ok(())
1011 })();
1012 self.ok_or_abort_file(result)?;
1013 let writer = self.inner.get_plain();
1014 self.stats.start = writer.stream_position()?;
1015 match options.encrypt_with {
1016 #[cfg(feature = "aes-crypto")]
1017 Some(EncryptWith::Aes { mode, password }) => {
1018 let aeswriter = AesWriter::new(
1019 mem::replace(&mut self.inner, Closed).unwrap(),
1020 mode,
1021 password.as_bytes(),
1022 )?;
1023 self.inner = Storer(MaybeEncrypted::Aes(aeswriter));
1024 }
1025 Some(EncryptWith::ZipCrypto(keys, ..)) => {
1026 let mut zipwriter = crate::zipcrypto::ZipCryptoWriter {
1027 writer: mem::replace(&mut self.inner, Closed).unwrap(),
1028 buffer: vec![],
1029 keys,
1030 };
1031 self.stats.start = zipwriter.writer.stream_position()?;
1032 let crypto_header = [0u8; 12];
1034 let result = zipwriter.write_all(&crypto_header);
1035 self.ok_or_abort_file(result)?;
1036 self.inner = Storer(MaybeEncrypted::ZipCrypto(zipwriter));
1037 }
1038 None => {}
1039 }
1040 let file = &mut self.files[index];
1041 debug_assert!(file.data_start.get().is_none());
1042 file.data_start.get_or_init(|| self.stats.start);
1043 self.stats.bytes_written = 0;
1044 self.stats.hasher = Hasher::new();
1045 Ok(())
1046 }
1047
1048 fn insert_file_data(&mut self, file: ZipFileData) -> ZipResult<usize> {
1049 if self.files.contains_key(&file.file_name) {
1050 return Err(InvalidArchive("Duplicate filename"));
1051 }
1052 let name = file.file_name.to_owned();
1053 self.files.insert(name.clone(), file);
1054 Ok(self.files.get_index_of(&name).unwrap())
1055 }
1056
1057 fn finish_file(&mut self) -> ZipResult<()> {
1058 if !self.writing_to_file {
1059 return Ok(());
1060 }
1061
1062 let make_plain_writer = self.inner.prepare_next_writer(
1063 Stored,
1064 None,
1065 #[cfg(feature = "deflate-zopfli")]
1066 None,
1067 )?;
1068 self.inner.switch_to(make_plain_writer)?;
1069 self.switch_to_non_encrypting_writer()?;
1070 let writer = self.inner.get_plain();
1071
1072 if !self.writing_raw {
1073 let file = match self.files.last_mut() {
1074 None => return Ok(()),
1075 Some((_, f)) => f,
1076 };
1077 file.uncompressed_size = self.stats.bytes_written;
1078
1079 let file_end = writer.stream_position()?;
1080 debug_assert!(file_end >= self.stats.start);
1081 file.compressed_size = file_end - self.stats.start;
1082 let mut crc = true;
1083 if let Some(aes_mode) = &mut file.aes_mode {
1084 aes_mode.1 = if self.stats.bytes_written < 20 {
1090 crc = false;
1091 AesVendorVersion::Ae2
1092 } else {
1093 AesVendorVersion::Ae1
1094 };
1095 }
1096 file.crc32 = if crc {
1097 self.stats.hasher.clone().finalize()
1098 } else {
1099 0
1100 };
1101 update_aes_extra_data(writer, file)?;
1102 update_local_file_header(writer, file)?;
1103 writer.seek(SeekFrom::Start(file_end))?;
1104 }
1105 if self.flush_on_finish_file {
1106 let result = writer.flush();
1107 self.ok_or_abort_file(result)?;
1108 }
1109
1110 self.writing_to_file = false;
1111 Ok(())
1112 }
1113
1114 fn switch_to_non_encrypting_writer(&mut self) -> Result<(), ZipError> {
1115 match mem::replace(&mut self.inner, Closed) {
1116 #[cfg(feature = "aes-crypto")]
1117 Storer(MaybeEncrypted::Aes(writer)) => {
1118 self.inner = Storer(MaybeEncrypted::Unencrypted(writer.finish()?));
1119 }
1120 Storer(MaybeEncrypted::ZipCrypto(writer)) => {
1121 let crc32 = self.stats.hasher.clone().finalize();
1122 self.inner = Storer(MaybeEncrypted::Unencrypted(writer.finish(crc32)?))
1123 }
1124 Storer(MaybeEncrypted::Unencrypted(w)) => {
1125 self.inner = Storer(MaybeEncrypted::Unencrypted(w))
1126 }
1127 _ => unreachable!(),
1128 }
1129 Ok(())
1130 }
1131
1132 pub fn abort_file(&mut self) -> ZipResult<()> {
1135 let (_, last_file) = self.files.pop().ok_or(ZipError::FileNotFound)?;
1136 let make_plain_writer = self.inner.prepare_next_writer(
1137 Stored,
1138 None,
1139 #[cfg(feature = "deflate-zopfli")]
1140 None,
1141 )?;
1142 self.inner.switch_to(make_plain_writer)?;
1143 self.switch_to_non_encrypting_writer()?;
1144 let rewind_safe: bool = match last_file.data_start.get() {
1147 None => self.files.is_empty(),
1148 Some(last_file_start) => self.files.values().all(|file| {
1149 file.data_start
1150 .get()
1151 .is_some_and(|start| start < last_file_start)
1152 }),
1153 };
1154 if rewind_safe {
1155 self.inner
1156 .get_plain()
1157 .seek(SeekFrom::Start(last_file.header_start))?;
1158 }
1159 self.writing_to_file = false;
1160 Ok(())
1161 }
1162
1163 pub fn start_file<S: ToString, T: FileOptionExtension>(
1168 &mut self,
1169 name: S,
1170 mut options: FileOptions<T>,
1171 ) -> ZipResult<()> {
1172 options.normalize();
1173 let make_new_self = self.inner.prepare_next_writer(
1174 options.compression_method,
1175 options.compression_level,
1176 #[cfg(feature = "deflate-zopfli")]
1177 options.zopfli_buffer_size,
1178 )?;
1179 self.start_entry(name, options, None)?;
1180 let result = self.inner.switch_to(make_new_self);
1181 self.ok_or_abort_file(result)?;
1182 self.writing_raw = false;
1183 Ok(())
1184 }
1185
1186 pub fn merge_archive<R>(&mut self, mut source: ZipArchive<R>) -> ZipResult<()>
1228 where
1229 R: Read + Seek,
1230 {
1231 self.finish_file()?;
1232
1233 self.writing_to_file = true;
1236 self.writing_raw = true;
1237
1238 let writer = self.inner.get_plain();
1239 let new_files = source.merge_contents(writer)?;
1241
1242 self.files.extend(new_files);
1244
1245 Ok(())
1246 }
1247
1248 pub fn start_file_from_path<E: FileOptionExtension, P: AsRef<Path>>(
1254 &mut self,
1255 path: P,
1256 options: FileOptions<E>,
1257 ) -> ZipResult<()> {
1258 self.start_file(path_to_string(path), options)
1259 }
1260
1261 pub fn raw_copy_file_rename<S: ToString>(&mut self, file: ZipFile, name: S) -> ZipResult<()> {
1288 let options = file.options();
1289 self.raw_copy_file_rename_internal(file, name, options)
1290 }
1291
1292 fn raw_copy_file_rename_internal<S: ToString>(
1293 &mut self,
1294 mut file: ZipFile,
1295 name: S,
1296 options: SimpleFileOptions,
1297 ) -> ZipResult<()> {
1298 let raw_values = ZipRawValues {
1299 crc32: file.crc32(),
1300 compressed_size: file.compressed_size(),
1301 uncompressed_size: file.size(),
1302 };
1303
1304 self.start_entry(name, options, Some(raw_values))?;
1305 self.writing_to_file = true;
1306 self.writing_raw = true;
1307
1308 io::copy(&mut file.take_raw_reader()?, self)?;
1309 self.finish_file()
1310 }
1311
1312 pub fn raw_copy_file_to_path<P: AsRef<Path>>(
1318 &mut self,
1319 file: ZipFile,
1320 path: P,
1321 ) -> ZipResult<()> {
1322 self.raw_copy_file_rename(file, path_to_string(path))
1323 }
1324
1325 pub fn raw_copy_file(&mut self, file: ZipFile) -> ZipResult<()> {
1349 let name = file.name().to_owned();
1350 self.raw_copy_file_rename(file, name)
1351 }
1352
1353 pub fn raw_copy_file_touch(
1377 &mut self,
1378 file: ZipFile,
1379 last_modified_time: DateTime,
1380 unix_mode: Option<u32>,
1381 ) -> ZipResult<()> {
1382 let name = file.name().to_owned();
1383
1384 let mut options = file.options();
1385
1386 options = options.last_modified_time(last_modified_time);
1387
1388 if let Some(perms) = unix_mode {
1389 options = options.unix_permissions(perms);
1390 }
1391
1392 options.normalize();
1393
1394 self.raw_copy_file_rename_internal(file, name, options)
1395 }
1396
1397 pub fn add_directory<S, T: FileOptionExtension>(
1401 &mut self,
1402 name: S,
1403 mut options: FileOptions<T>,
1404 ) -> ZipResult<()>
1405 where
1406 S: Into<String>,
1407 {
1408 if options.permissions.is_none() {
1409 options.permissions = Some(0o755);
1410 }
1411 *options.permissions.as_mut().unwrap() |= 0o40000;
1412 options.compression_method = Stored;
1413 options.encrypt_with = None;
1414
1415 let name_as_string = name.into();
1416 let name_with_slash = match name_as_string.chars().last() {
1418 Some('/') | Some('\\') => name_as_string,
1419 _ => name_as_string + "/",
1420 };
1421
1422 self.start_entry(name_with_slash, options, None)?;
1423 self.writing_to_file = false;
1424 self.switch_to_non_encrypting_writer()?;
1425 Ok(())
1426 }
1427
1428 pub fn add_directory_from_path<T: FileOptionExtension, P: AsRef<Path>>(
1434 &mut self,
1435 path: P,
1436 options: FileOptions<T>,
1437 ) -> ZipResult<()> {
1438 self.add_directory(path_to_string(path), options)
1439 }
1440
1441 pub fn finish(mut self) -> ZipResult<W> {
1446 let _central_start = self.finalize()?;
1447 let inner = mem::replace(&mut self.inner, Closed);
1448 Ok(inner.unwrap())
1449 }
1450
1451 pub fn add_symlink<N: ToString, T: ToString, E: FileOptionExtension>(
1464 &mut self,
1465 name: N,
1466 target: T,
1467 mut options: FileOptions<E>,
1468 ) -> ZipResult<()> {
1469 if options.permissions.is_none() {
1470 options.permissions = Some(0o777);
1471 }
1472 *options.permissions.as_mut().unwrap() |= S_IFLNK;
1473 options.compression_method = Stored;
1476
1477 self.start_entry(name, options, None)?;
1478 self.writing_to_file = true;
1479 let result = self.write_all(target.to_string().as_bytes());
1480 self.ok_or_abort_file(result)?;
1481 self.writing_raw = false;
1482 self.finish_file()?;
1483
1484 Ok(())
1485 }
1486
1487 pub fn add_symlink_from_path<P: AsRef<Path>, T: AsRef<Path>, E: FileOptionExtension>(
1493 &mut self,
1494 path: P,
1495 target: T,
1496 options: FileOptions<E>,
1497 ) -> ZipResult<()> {
1498 self.add_symlink(path_to_string(path), path_to_string(target), options)
1499 }
1500
1501 fn finalize(&mut self) -> ZipResult<u64> {
1502 self.finish_file()?;
1503
1504 let mut central_start = self.write_central_and_footer()?;
1505 let writer = self.inner.get_plain();
1506 let footer_end = writer.stream_position()?;
1507 let archive_end = writer.seek(SeekFrom::End(0))?;
1508 if footer_end < archive_end {
1509 writer.seek(SeekFrom::Start(central_start))?;
1513 writer.write_u32_le(0)?;
1514 writer.seek(SeekFrom::Start(
1515 footer_end - size_of::<Zip32CDEBlock>() as u64 - self.comment.len() as u64,
1516 ))?;
1517 writer.write_u32_le(0)?;
1518
1519 let central_and_footer_size = footer_end - central_start;
1521 writer.seek(SeekFrom::End(-(central_and_footer_size as i64)))?;
1522 central_start = self.write_central_and_footer()?;
1523 debug_assert!(self.inner.get_plain().stream_position()? == archive_end);
1524 }
1525
1526 Ok(central_start)
1527 }
1528
1529 fn write_central_and_footer(&mut self) -> Result<u64, ZipError> {
1530 let writer = self.inner.get_plain();
1531
1532 let mut version_needed = MIN_VERSION as u16;
1533 let central_start = writer.stream_position()?;
1534 for file in self.files.values() {
1535 write_central_directory_header(writer, file)?;
1536 version_needed = version_needed.max(file.version_needed());
1537 }
1538 let central_size = writer.stream_position()? - central_start;
1539 let is64 = self.files.len() > spec::ZIP64_ENTRY_THR
1540 || central_size.max(central_start) > spec::ZIP64_BYTES_THR
1541 || self.zip64_comment.is_some();
1542
1543 if is64 {
1544 let comment = self.zip64_comment.clone().unwrap_or_default();
1545
1546 let zip64_footer = spec::Zip64CentralDirectoryEnd {
1547 record_size: comment.len() as u64 + 44,
1548 version_made_by: version_needed,
1549 version_needed_to_extract: version_needed,
1550 disk_number: 0,
1551 disk_with_central_directory: 0,
1552 number_of_files_on_this_disk: self.files.len() as u64,
1553 number_of_files: self.files.len() as u64,
1554 central_directory_size: central_size,
1555 central_directory_offset: central_start,
1556 extensible_data_sector: comment,
1557 };
1558
1559 zip64_footer.write(writer)?;
1560
1561 let zip64_footer = spec::Zip64CentralDirectoryEndLocator {
1562 disk_with_central_directory: 0,
1563 end_of_central_directory_offset: central_start + central_size,
1564 number_of_disks: 1,
1565 };
1566
1567 zip64_footer.write(writer)?;
1568 }
1569
1570 let number_of_files = self.files.len().min(spec::ZIP64_ENTRY_THR) as u16;
1571 let footer = spec::Zip32CentralDirectoryEnd {
1572 disk_number: 0,
1573 disk_with_central_directory: 0,
1574 zip_file_comment: self.comment.clone(),
1575 number_of_files_on_this_disk: number_of_files,
1576 number_of_files,
1577 central_directory_size: central_size.min(spec::ZIP64_BYTES_THR) as u32,
1578 central_directory_offset: central_start.min(spec::ZIP64_BYTES_THR) as u32,
1579 };
1580
1581 footer.write(writer)?;
1582 Ok(central_start)
1583 }
1584
1585 fn index_by_name(&self, name: &str) -> ZipResult<usize> {
1586 self.files.get_index_of(name).ok_or(ZipError::FileNotFound)
1587 }
1588
1589 pub fn shallow_copy_file(&mut self, src_name: &str, dest_name: &str) -> ZipResult<()> {
1595 self.finish_file()?;
1596 if src_name == dest_name {
1597 return Err(InvalidArchive("Trying to copy a file to itself"));
1598 }
1599 let src_index = self.index_by_name(src_name)?;
1600 let mut dest_data = self.files[src_index].to_owned();
1601 dest_data.file_name = dest_name.to_string().into();
1602 dest_data.file_name_raw = dest_name.to_string().into_bytes().into();
1603 dest_data.central_header_start = 0;
1604 self.insert_file_data(dest_data)?;
1605
1606 Ok(())
1607 }
1608
1609 pub fn shallow_copy_file_from_path<T: AsRef<Path>, U: AsRef<Path>>(
1615 &mut self,
1616 src_path: T,
1617 dest_path: U,
1618 ) -> ZipResult<()> {
1619 self.shallow_copy_file(&path_to_string(src_path), &path_to_string(dest_path))
1620 }
1621}
1622
1623impl<W: Write + Seek> Drop for ZipWriter<W> {
1624 fn drop(&mut self) {
1625 if !self.inner.is_closed() {
1626 if let Err(e) = self.finalize() {
1627 let _ = write!(io::stderr(), "ZipWriter drop failed: {:?}", e);
1628 }
1629 }
1630 }
1631}
1632
1633type SwitchWriterFunction<W> = Box<dyn FnOnce(MaybeEncrypted<W>) -> GenericZipWriter<W>>;
1634
1635impl<W: Write + Seek> GenericZipWriter<W> {
1636 fn prepare_next_writer(
1637 &self,
1638 compression: CompressionMethod,
1639 compression_level: Option<i64>,
1640 #[cfg(feature = "deflate-zopfli")] zopfli_buffer_size: Option<usize>,
1641 ) -> ZipResult<SwitchWriterFunction<W>> {
1642 if let Closed = self {
1643 return Err(
1644 io::Error::new(io::ErrorKind::BrokenPipe, "ZipWriter was already closed").into(),
1645 );
1646 }
1647
1648 {
1649 #[allow(deprecated)]
1650 #[allow(unreachable_code)]
1651 match compression {
1652 Stored => {
1653 if compression_level.is_some() {
1654 Err(UnsupportedArchive("Unsupported compression level"))
1655 } else {
1656 Ok(Box::new(|bare| Storer(bare)))
1657 }
1658 }
1659 #[cfg(feature = "_deflate-any")]
1660 CompressionMethod::Deflated => {
1661 let default = if cfg!(all(
1662 feature = "deflate-zopfli",
1663 not(feature = "deflate-flate2")
1664 )) {
1665 24
1666 } else {
1667 Compression::default().level() as i64
1668 };
1669
1670 let level = clamp_opt(
1671 compression_level.unwrap_or(default),
1672 deflate_compression_level_range(),
1673 )
1674 .ok_or(UnsupportedArchive("Unsupported compression level"))?
1675 as u32;
1676
1677 #[cfg(feature = "deflate-zopfli")]
1678 {
1679 let best_non_zopfli = Compression::best().level();
1680 if level > best_non_zopfli {
1681 let options = Options {
1682 iteration_count: NonZeroU64::try_from(
1683 (level - best_non_zopfli) as u64,
1684 )
1685 .unwrap(),
1686 ..Default::default()
1687 };
1688 return Ok(Box::new(move |bare| match zopfli_buffer_size {
1689 Some(size) => GenericZipWriter::BufferedZopfliDeflater(
1690 BufWriter::with_capacity(
1691 size,
1692 zopfli::DeflateEncoder::new(
1693 options,
1694 Default::default(),
1695 bare,
1696 ),
1697 ),
1698 ),
1699 None => GenericZipWriter::ZopfliDeflater(
1700 zopfli::DeflateEncoder::new(options, Default::default(), bare),
1701 ),
1702 }));
1703 }
1704 }
1705
1706 #[cfg(feature = "deflate-flate2")]
1707 {
1708 Ok(Box::new(move |bare| {
1709 GenericZipWriter::Deflater(DeflateEncoder::new(
1710 bare,
1711 Compression::new(level),
1712 ))
1713 }))
1714 }
1715 }
1716 #[cfg(feature = "deflate64")]
1717 CompressionMethod::Deflate64 => {
1718 Err(UnsupportedArchive("Compressing Deflate64 is not supported"))
1719 }
1720 #[cfg(feature = "bzip2")]
1721 CompressionMethod::Bzip2 => {
1722 let level = clamp_opt(
1723 compression_level.unwrap_or(bzip2::Compression::default().level() as i64),
1724 bzip2_compression_level_range(),
1725 )
1726 .ok_or(UnsupportedArchive("Unsupported compression level"))?
1727 as u32;
1728 Ok(Box::new(move |bare| {
1729 GenericZipWriter::Bzip2(BzEncoder::new(
1730 bare,
1731 bzip2::Compression::new(level),
1732 ))
1733 }))
1734 }
1735 CompressionMethod::AES => Err(UnsupportedArchive(
1736 "AES encryption is enabled through FileOptions::with_aes_encryption",
1737 )),
1738 #[cfg(feature = "zstd")]
1739 CompressionMethod::Zstd => {
1740 let level = clamp_opt(
1741 compression_level.unwrap_or(zstd::DEFAULT_COMPRESSION_LEVEL as i64),
1742 zstd::compression_level_range(),
1743 )
1744 .ok_or(UnsupportedArchive("Unsupported compression level"))?;
1745 Ok(Box::new(move |bare| {
1746 GenericZipWriter::Zstd(ZstdEncoder::new(bare, level as i32).unwrap())
1747 }))
1748 }
1749 #[cfg(feature = "lzma")]
1750 CompressionMethod::Lzma => {
1751 Err(UnsupportedArchive("LZMA isn't supported for compression"))
1752 }
1753 #[cfg(feature = "xz")]
1754 CompressionMethod::Xz => {
1755 let level = clamp_opt(compression_level.unwrap_or(6), 0..=9)
1756 .ok_or(UnsupportedArchive("Unsupported compression level"))?
1757 as u32;
1758 Ok(Box::new(move |bare| {
1759 GenericZipWriter::Xz(xz2::write::XzEncoder::new(bare, level))
1760 }))
1761 }
1762 CompressionMethod::Unsupported(..) => {
1763 Err(UnsupportedArchive("Unsupported compression"))
1764 }
1765 }
1766 }
1767 }
1768
1769 fn switch_to(&mut self, make_new_self: SwitchWriterFunction<W>) -> ZipResult<()> {
1770 let bare = match mem::replace(self, Closed) {
1771 Storer(w) => w,
1772 #[cfg(feature = "deflate-flate2")]
1773 GenericZipWriter::Deflater(w) => w.finish()?,
1774 #[cfg(feature = "deflate-zopfli")]
1775 GenericZipWriter::ZopfliDeflater(w) => w.finish()?,
1776 #[cfg(feature = "deflate-zopfli")]
1777 GenericZipWriter::BufferedZopfliDeflater(w) => w
1778 .into_inner()
1779 .map_err(|e| ZipError::Io(e.into_error()))?
1780 .finish()?,
1781 #[cfg(feature = "bzip2")]
1782 GenericZipWriter::Bzip2(w) => w.finish()?,
1783 #[cfg(feature = "zstd")]
1784 GenericZipWriter::Zstd(w) => w.finish()?,
1785 #[cfg(feature = "xz")]
1786 GenericZipWriter::Xz(w) => w.finish()?,
1787 Closed => {
1788 return Err(io::Error::new(
1789 io::ErrorKind::BrokenPipe,
1790 "ZipWriter was already closed",
1791 )
1792 .into());
1793 }
1794 };
1795 *self = make_new_self(bare);
1796 Ok(())
1797 }
1798
1799 fn ref_mut(&mut self) -> Option<&mut dyn Write> {
1800 match self {
1801 Storer(ref mut w) => Some(w as &mut dyn Write),
1802 #[cfg(feature = "deflate-flate2")]
1803 GenericZipWriter::Deflater(ref mut w) => Some(w as &mut dyn Write),
1804 #[cfg(feature = "deflate-zopfli")]
1805 GenericZipWriter::ZopfliDeflater(w) => Some(w as &mut dyn Write),
1806 #[cfg(feature = "deflate-zopfli")]
1807 GenericZipWriter::BufferedZopfliDeflater(w) => Some(w as &mut dyn Write),
1808 #[cfg(feature = "bzip2")]
1809 GenericZipWriter::Bzip2(ref mut w) => Some(w as &mut dyn Write),
1810 #[cfg(feature = "zstd")]
1811 GenericZipWriter::Zstd(ref mut w) => Some(w as &mut dyn Write),
1812 #[cfg(feature = "xz")]
1813 GenericZipWriter::Xz(ref mut w) => Some(w as &mut dyn Write),
1814 Closed => None,
1815 }
1816 }
1817
1818 const fn is_closed(&self) -> bool {
1819 matches!(*self, Closed)
1820 }
1821
1822 fn get_plain(&mut self) -> &mut W {
1823 match *self {
1824 Storer(MaybeEncrypted::Unencrypted(ref mut w)) => w,
1825 _ => panic!("Should have switched to stored and unencrypted beforehand"),
1826 }
1827 }
1828
1829 fn unwrap(self) -> W {
1830 match self {
1831 Storer(MaybeEncrypted::Unencrypted(w)) => w,
1832 _ => panic!("Should have switched to stored and unencrypted beforehand"),
1833 }
1834 }
1835}
1836
1837#[cfg(feature = "_deflate-any")]
1838fn deflate_compression_level_range() -> std::ops::RangeInclusive<i64> {
1839 let min = if cfg!(feature = "deflate-flate2") {
1840 Compression::fast().level() as i64
1841 } else {
1842 Compression::best().level() as i64 + 1
1843 };
1844
1845 let max = Compression::best().level() as i64
1846 + if cfg!(feature = "deflate-zopfli") {
1847 u8::MAX as i64
1848 } else {
1849 0
1850 };
1851
1852 min..=max
1853}
1854
1855#[cfg(feature = "bzip2")]
1856fn bzip2_compression_level_range() -> std::ops::RangeInclusive<i64> {
1857 let min = bzip2::Compression::fast().level() as i64;
1858 let max = bzip2::Compression::best().level() as i64;
1859 min..=max
1860}
1861
1862#[cfg(any(feature = "_deflate-any", feature = "bzip2", feature = "zstd"))]
1863fn clamp_opt<T: Ord + Copy, U: Ord + Copy + TryFrom<T>>(
1864 value: T,
1865 range: std::ops::RangeInclusive<U>,
1866) -> Option<T> {
1867 if range.contains(&value.try_into().ok()?) {
1868 Some(value)
1869 } else {
1870 None
1871 }
1872}
1873
1874fn update_aes_extra_data<W: Write + Seek>(writer: &mut W, file: &mut ZipFileData) -> ZipResult<()> {
1875 let Some((aes_mode, version, compression_method)) = file.aes_mode else {
1876 return Ok(());
1877 };
1878
1879 let extra_data_start = file.extra_data_start.unwrap();
1880
1881 writer.seek(SeekFrom::Start(
1882 extra_data_start + file.aes_extra_data_start,
1883 ))?;
1884
1885 let mut buf = Vec::new();
1886
1887 buf.write_u16_le(0x9901)?;
1890 buf.write_u16_le(7)?;
1892 buf.write_u16_le(version as u16)?;
1894 buf.write_all(b"AE")?;
1896 buf.write_all(&[aes_mode as u8])?;
1898 buf.write_u16_le(compression_method.serialize_to_u16())?;
1900
1901 writer.write_all(&buf)?;
1902
1903 let aes_extra_data_start = file.aes_extra_data_start as usize;
1904 let extra_field = Arc::get_mut(file.extra_field.as_mut().unwrap()).unwrap();
1905 extra_field[aes_extra_data_start..aes_extra_data_start + buf.len()].copy_from_slice(&buf);
1906
1907 Ok(())
1908}
1909
1910fn update_local_file_header<T: Write + Seek>(
1911 writer: &mut T,
1912 file: &mut ZipFileData,
1913) -> ZipResult<()> {
1914 const CRC32_OFFSET: u64 = 14;
1915 writer.seek(SeekFrom::Start(file.header_start + CRC32_OFFSET))?;
1916 writer.write_u32_le(file.crc32)?;
1917 if file.large_file {
1918 writer.write_u32_le(spec::ZIP64_BYTES_THR as u32)?;
1919 writer.write_u32_le(spec::ZIP64_BYTES_THR as u32)?;
1920
1921 update_local_zip64_extra_field(writer, file)?;
1922
1923 file.compressed_size = spec::ZIP64_BYTES_THR;
1924 file.uncompressed_size = spec::ZIP64_BYTES_THR;
1925 } else {
1926 if file.compressed_size > spec::ZIP64_BYTES_THR {
1928 return Err(ZipError::Io(io::Error::new(
1929 io::ErrorKind::Other,
1930 "Large file option has not been set",
1931 )));
1932 }
1933 writer.write_u32_le(file.compressed_size as u32)?;
1934 writer.write_u32_le(file.uncompressed_size as u32)?;
1936 }
1937 Ok(())
1938}
1939
1940fn write_central_directory_header<T: Write>(writer: &mut T, file: &ZipFileData) -> ZipResult<()> {
1941 let block = file.block()?;
1942 block.write(writer)?;
1943 writer.write_all(&file.file_name_raw)?;
1945 if let Some(extra_field) = &file.extra_field {
1947 writer.write_all(extra_field)?;
1948 }
1949 if let Some(central_extra_field) = &file.central_extra_field {
1950 writer.write_all(central_extra_field)?;
1951 }
1952 writer.write_all(file.file_comment.as_bytes())?;
1954
1955 Ok(())
1956}
1957
1958fn update_local_zip64_extra_field<T: Write + Seek>(
1959 writer: &mut T,
1960 file: &mut ZipFileData,
1961) -> ZipResult<()> {
1962 let block = file.zip64_extra_field_block().ok_or(InvalidArchive(
1963 "Attempted to update a nonexistent ZIP64 extra field",
1964 ))?;
1965
1966 let zip64_extra_field_start = file.header_start
1967 + size_of::<ZipLocalEntryBlock>() as u64
1968 + file.file_name_raw.len() as u64;
1969
1970 writer.seek(SeekFrom::Start(zip64_extra_field_start))?;
1971 let block = block.serialize();
1972 writer.write_all(&block)?;
1973
1974 let extra_field = Arc::get_mut(file.extra_field.as_mut().unwrap()).unwrap();
1975 extra_field[..block.len()].copy_from_slice(&block);
1976
1977 Ok(())
1978}
1979
1980#[cfg(not(feature = "unreserved"))]
1981const EXTRA_FIELD_MAPPING: [u16; 43] = [
1982 0x0007, 0x0008, 0x0009, 0x000a, 0x000c, 0x000d, 0x000e, 0x000f, 0x0014, 0x0015, 0x0016, 0x0017,
1983 0x0018, 0x0019, 0x0020, 0x0021, 0x0022, 0x0023, 0x0065, 0x0066, 0x4690, 0x07c8, 0x2605, 0x2705,
1984 0x2805, 0x334d, 0x4341, 0x4453, 0x4704, 0x470f, 0x4b46, 0x4c41, 0x4d49, 0x4f4c, 0x5356, 0x554e,
1985 0x5855, 0x6542, 0x756e, 0x7855, 0xa220, 0xfd4a, 0x9902,
1986];
1987
1988#[cfg(test)]
1989#[allow(unknown_lints)] #[allow(clippy::needless_update)] #[allow(clippy::octal_escapes)] mod test {
1993 use super::{ExtendedFileOptions, FileOptions, FullFileOptions, ZipWriter};
1994 use crate::compression::CompressionMethod;
1995 use crate::result::ZipResult;
1996 use crate::types::DateTime;
1997 use crate::write::EncryptWith::ZipCrypto;
1998 use crate::write::SimpleFileOptions;
1999 use crate::zipcrypto::ZipCryptoKeys;
2000 use crate::CompressionMethod::Stored;
2001 use crate::ZipArchive;
2002 use std::io::{Cursor, Read, Write};
2003 use std::marker::PhantomData;
2004 use std::path::PathBuf;
2005
2006 #[test]
2007 fn write_empty_zip() {
2008 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2009 writer.set_comment("ZIP");
2010 let result = writer.finish().unwrap();
2011 assert_eq!(result.get_ref().len(), 25);
2012 assert_eq!(
2013 *result.get_ref(),
2014 [80, 75, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 90, 73, 80]
2015 );
2016 }
2017
2018 #[test]
2019 fn unix_permissions_bitmask() {
2020 let options = SimpleFileOptions::default().unix_permissions(0o120777);
2022 assert_eq!(options.permissions, Some(0o777));
2023 }
2024
2025 #[test]
2026 fn write_zip_dir() {
2027 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2028 writer
2029 .add_directory(
2030 "test",
2031 SimpleFileOptions::default().last_modified_time(
2032 DateTime::from_date_and_time(2018, 8, 15, 20, 45, 6).unwrap(),
2033 ),
2034 )
2035 .unwrap();
2036 assert!(writer
2037 .write(b"writing to a directory is not allowed, and will not write any data")
2038 .is_err());
2039 let result = writer.finish().unwrap();
2040 assert_eq!(result.get_ref().len(), 108);
2041 assert_eq!(
2042 *result.get_ref(),
2043 &[
2044 80u8, 75, 3, 4, 20, 0, 0, 0, 0, 0, 163, 165, 15, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2045 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 47, 80, 75, 1, 2, 20, 3, 20, 0, 0, 0, 0, 0,
2046 163, 165, 15, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2047 0, 0, 237, 65, 0, 0, 0, 0, 116, 101, 115, 116, 47, 80, 75, 5, 6, 0, 0, 0, 0, 1, 0,
2048 1, 0, 51, 0, 0, 0, 35, 0, 0, 0, 0, 0,
2049 ] as &[u8]
2050 );
2051 }
2052
2053 #[test]
2054 fn write_symlink_simple() {
2055 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2056 writer
2057 .add_symlink(
2058 "name",
2059 "target",
2060 SimpleFileOptions::default().last_modified_time(
2061 DateTime::from_date_and_time(2018, 8, 15, 20, 45, 6).unwrap(),
2062 ),
2063 )
2064 .unwrap();
2065 assert!(writer
2066 .write(b"writing to a symlink is not allowed and will not write any data")
2067 .is_err());
2068 let result = writer.finish().unwrap();
2069 assert_eq!(result.get_ref().len(), 112);
2070 assert_eq!(
2071 *result.get_ref(),
2072 &[
2073 80u8, 75, 3, 4, 10, 0, 0, 0, 0, 0, 163, 165, 15, 77, 252, 47, 111, 70, 6, 0, 0, 0,
2074 6, 0, 0, 0, 4, 0, 0, 0, 110, 97, 109, 101, 116, 97, 114, 103, 101, 116, 80, 75, 1,
2075 2, 10, 3, 10, 0, 0, 0, 0, 0, 163, 165, 15, 77, 252, 47, 111, 70, 6, 0, 0, 0, 6, 0,
2076 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 161, 0, 0, 0, 0, 110, 97, 109, 101,
2077 80, 75, 5, 6, 0, 0, 0, 0, 1, 0, 1, 0, 50, 0, 0, 0, 40, 0, 0, 0, 0, 0
2078 ] as &[u8],
2079 );
2080 }
2081
2082 #[test]
2083 fn test_path_normalization() {
2084 let mut path = PathBuf::new();
2085 path.push("foo");
2086 path.push("bar");
2087 path.push("..");
2088 path.push(".");
2089 path.push("example.txt");
2090 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2091 writer
2092 .start_file_from_path(path, SimpleFileOptions::default())
2093 .unwrap();
2094 let archive = writer.finish_into_readable().unwrap();
2095 assert_eq!(Some("foo/example.txt"), archive.name_for_index(0));
2096 }
2097
2098 #[test]
2099 fn write_symlink_wonky_paths() {
2100 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2101 writer
2102 .add_symlink(
2103 "directory\\link",
2104 "/absolute/symlink\\with\\mixed/slashes",
2105 SimpleFileOptions::default().last_modified_time(
2106 DateTime::from_date_and_time(2018, 8, 15, 20, 45, 6).unwrap(),
2107 ),
2108 )
2109 .unwrap();
2110 assert!(writer
2111 .write(b"writing to a symlink is not allowed and will not write any data")
2112 .is_err());
2113 let result = writer.finish().unwrap();
2114 assert_eq!(result.get_ref().len(), 162);
2115 assert_eq!(
2116 *result.get_ref(),
2117 &[
2118 80u8, 75, 3, 4, 10, 0, 0, 0, 0, 0, 163, 165, 15, 77, 95, 41, 81, 245, 36, 0, 0, 0,
2119 36, 0, 0, 0, 14, 0, 0, 0, 100, 105, 114, 101, 99, 116, 111, 114, 121, 92, 108, 105,
2120 110, 107, 47, 97, 98, 115, 111, 108, 117, 116, 101, 47, 115, 121, 109, 108, 105,
2121 110, 107, 92, 119, 105, 116, 104, 92, 109, 105, 120, 101, 100, 47, 115, 108, 97,
2122 115, 104, 101, 115, 80, 75, 1, 2, 10, 3, 10, 0, 0, 0, 0, 0, 163, 165, 15, 77, 95,
2123 41, 81, 245, 36, 0, 0, 0, 36, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
2124 161, 0, 0, 0, 0, 100, 105, 114, 101, 99, 116, 111, 114, 121, 92, 108, 105, 110,
2125 107, 80, 75, 5, 6, 0, 0, 0, 0, 1, 0, 1, 0, 60, 0, 0, 0, 80, 0, 0, 0, 0, 0
2126 ] as &[u8],
2127 );
2128 }
2129
2130 #[test]
2131 fn write_mimetype_zip() {
2132 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2133 let options = FileOptions {
2134 compression_method: Stored,
2135 compression_level: None,
2136 last_modified_time: DateTime::default(),
2137 permissions: Some(33188),
2138 large_file: false,
2139 encrypt_with: None,
2140 extended_options: (),
2141 alignment: 1,
2142 #[cfg(feature = "deflate-zopfli")]
2143 zopfli_buffer_size: None,
2144 };
2145 writer.start_file("mimetype", options).unwrap();
2146 writer
2147 .write_all(b"application/vnd.oasis.opendocument.text")
2148 .unwrap();
2149 let result = writer.finish().unwrap();
2150
2151 assert_eq!(result.get_ref().len(), 153);
2152 let mut v = Vec::new();
2153 v.extend_from_slice(include_bytes!("../tests/data/mimetype.zip"));
2154 assert_eq!(result.get_ref(), &v);
2155 }
2156
2157 const RT_TEST_TEXT: &str = "And I can't stop thinking about the moments that I lost to you\
2158 And I can't stop thinking of things I used to do\
2159 And I can't stop making bad decisions\
2160 And I can't stop eating stuff you make me chew\
2161 I put on a smile like you wanna see\
2162 Another day goes by that I long to be like you";
2163 const RT_TEST_FILENAME: &str = "subfolder/sub-subfolder/can't_stop.txt";
2164 const SECOND_FILENAME: &str = "different_name.xyz";
2165 const THIRD_FILENAME: &str = "third_name.xyz";
2166
2167 #[test]
2168 fn write_non_utf8() {
2169 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2170 let options = FileOptions {
2171 compression_method: Stored,
2172 compression_level: None,
2173 last_modified_time: DateTime::default(),
2174 permissions: Some(33188),
2175 large_file: false,
2176 encrypt_with: None,
2177 extended_options: (),
2178 alignment: 1,
2179 #[cfg(feature = "deflate-zopfli")]
2180 zopfli_buffer_size: None,
2181 };
2182
2183 let filename = unsafe { String::from_utf8_unchecked(vec![214, 208, 206, 196]) };
2186 writer.start_file(filename, options).unwrap();
2187 writer.write_all(b"encoding GB18030").unwrap();
2188
2189 let filename = unsafe { String::from_utf8_unchecked(vec![147, 250, 149, 182]) };
2192 writer.start_file(filename, options).unwrap();
2193 writer.write_all(b"encoding SHIFT_JIS").unwrap();
2194 let result = writer.finish().unwrap();
2195
2196 assert_eq!(result.get_ref().len(), 224);
2197
2198 let mut v = Vec::new();
2199 v.extend_from_slice(include_bytes!("../tests/data/non_utf8.zip"));
2200
2201 assert_eq!(result.get_ref(), &v);
2202 }
2203
2204 #[test]
2205 fn path_to_string() {
2206 let mut path = PathBuf::new();
2207 #[cfg(windows)]
2208 path.push(r"C:\");
2209 #[cfg(unix)]
2210 path.push("/");
2211 path.push("windows");
2212 path.push("..");
2213 path.push(".");
2214 path.push("system32");
2215 let path_str = super::path_to_string(&path);
2216 assert_eq!(&*path_str, "system32");
2217 }
2218
2219 #[test]
2220 fn test_shallow_copy() {
2221 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2222 let options = FileOptions {
2223 compression_method: CompressionMethod::default(),
2224 compression_level: None,
2225 last_modified_time: DateTime::default(),
2226 permissions: Some(33188),
2227 large_file: false,
2228 encrypt_with: None,
2229 extended_options: (),
2230 alignment: 0,
2231 #[cfg(feature = "deflate-zopfli")]
2232 zopfli_buffer_size: None,
2233 };
2234 writer.start_file(RT_TEST_FILENAME, options).unwrap();
2235 writer.write_all(RT_TEST_TEXT.as_ref()).unwrap();
2236 writer
2237 .shallow_copy_file(RT_TEST_FILENAME, SECOND_FILENAME)
2238 .unwrap();
2239 writer
2240 .shallow_copy_file(RT_TEST_FILENAME, SECOND_FILENAME)
2241 .expect_err("Duplicate filename");
2242 let zip = writer.finish().unwrap();
2243 let mut writer = ZipWriter::new_append(zip).unwrap();
2244 writer
2245 .shallow_copy_file(SECOND_FILENAME, SECOND_FILENAME)
2246 .expect_err("Duplicate filename");
2247 let mut reader = writer.finish_into_readable().unwrap();
2248 let mut file_names: Vec<&str> = reader.file_names().collect();
2249 file_names.sort();
2250 let mut expected_file_names = vec![RT_TEST_FILENAME, SECOND_FILENAME];
2251 expected_file_names.sort();
2252 assert_eq!(file_names, expected_file_names);
2253 let mut first_file_content = String::new();
2254 reader
2255 .by_name(RT_TEST_FILENAME)
2256 .unwrap()
2257 .read_to_string(&mut first_file_content)
2258 .unwrap();
2259 assert_eq!(first_file_content, RT_TEST_TEXT);
2260 let mut second_file_content = String::new();
2261 reader
2262 .by_name(SECOND_FILENAME)
2263 .unwrap()
2264 .read_to_string(&mut second_file_content)
2265 .unwrap();
2266 assert_eq!(second_file_content, RT_TEST_TEXT);
2267 }
2268
2269 #[test]
2270 fn test_deep_copy() {
2271 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2272 let options = FileOptions {
2273 compression_method: CompressionMethod::default(),
2274 compression_level: None,
2275 last_modified_time: DateTime::default(),
2276 permissions: Some(33188),
2277 large_file: false,
2278 encrypt_with: None,
2279 extended_options: (),
2280 alignment: 0,
2281 #[cfg(feature = "deflate-zopfli")]
2282 zopfli_buffer_size: None,
2283 };
2284 writer.start_file(RT_TEST_FILENAME, options).unwrap();
2285 writer.write_all(RT_TEST_TEXT.as_ref()).unwrap();
2286 writer
2287 .deep_copy_file(RT_TEST_FILENAME, SECOND_FILENAME)
2288 .unwrap();
2289 let zip = writer.finish().unwrap().into_inner();
2290 zip.iter().copied().for_each(|x| print!("{:02x}", x));
2291 println!();
2292 let mut writer = ZipWriter::new_append(Cursor::new(zip)).unwrap();
2293 writer
2294 .deep_copy_file(RT_TEST_FILENAME, THIRD_FILENAME)
2295 .unwrap();
2296 let zip = writer.finish().unwrap();
2297 let mut reader = ZipArchive::new(zip).unwrap();
2298 let mut file_names: Vec<&str> = reader.file_names().collect();
2299 file_names.sort();
2300 let mut expected_file_names = vec![RT_TEST_FILENAME, SECOND_FILENAME, THIRD_FILENAME];
2301 expected_file_names.sort();
2302 assert_eq!(file_names, expected_file_names);
2303 let mut first_file_content = String::new();
2304 reader
2305 .by_name(RT_TEST_FILENAME)
2306 .unwrap()
2307 .read_to_string(&mut first_file_content)
2308 .unwrap();
2309 assert_eq!(first_file_content, RT_TEST_TEXT);
2310 let mut second_file_content = String::new();
2311 reader
2312 .by_name(SECOND_FILENAME)
2313 .unwrap()
2314 .read_to_string(&mut second_file_content)
2315 .unwrap();
2316 assert_eq!(second_file_content, RT_TEST_TEXT);
2317 }
2318
2319 #[test]
2320 fn duplicate_filenames() {
2321 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2322 writer
2323 .start_file("foo/bar/test", SimpleFileOptions::default())
2324 .unwrap();
2325 writer
2326 .write_all("The quick brown 🦊 jumps over the lazy 🐕".as_bytes())
2327 .unwrap();
2328 writer
2329 .start_file("foo/bar/test", SimpleFileOptions::default())
2330 .expect_err("Expected duplicate filename not to be allowed");
2331 }
2332
2333 #[test]
2334 fn test_filename_looks_like_zip64_locator() {
2335 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2336 writer
2337 .start_file(
2338 "PK\u{6}\u{7}\0\0\0\u{11}\0\0\0\0\0\0\0\0\0\0\0\0",
2339 SimpleFileOptions::default(),
2340 )
2341 .unwrap();
2342 let zip = writer.finish().unwrap();
2343 let _ = ZipArchive::new(zip).unwrap();
2344 }
2345
2346 #[test]
2347 fn test_filename_looks_like_zip64_locator_2() {
2348 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2349 writer
2350 .start_file(
2351 "PK\u{6}\u{6}\0\0\0\0\0\0\0\0\0\0PK\u{6}\u{7}\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
2352 SimpleFileOptions::default(),
2353 )
2354 .unwrap();
2355 let zip = writer.finish().unwrap();
2356 let _ = ZipArchive::new(zip).unwrap();
2357 }
2358
2359 #[test]
2360 fn test_filename_looks_like_zip64_locator_2a() {
2361 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2362 writer
2363 .start_file(
2364 "PK\u{6}\u{6}PK\u{6}\u{7}\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
2365 SimpleFileOptions::default(),
2366 )
2367 .unwrap();
2368 let zip = writer.finish().unwrap();
2369 let _ = ZipArchive::new(zip).unwrap();
2370 }
2371
2372 #[test]
2373 fn test_filename_looks_like_zip64_locator_3() {
2374 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2375 writer
2376 .start_file("\0PK\u{6}\u{6}", SimpleFileOptions::default())
2377 .unwrap();
2378 writer
2379 .start_file(
2380 "\0\u{4}\0\0PK\u{6}\u{7}\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\u{3}",
2381 SimpleFileOptions::default(),
2382 )
2383 .unwrap();
2384 let zip = writer.finish().unwrap();
2385 let _ = ZipArchive::new(zip).unwrap();
2386 }
2387
2388 #[test]
2389 fn test_filename_looks_like_zip64_locator_4() {
2390 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2391 writer
2392 .start_file("PK\u{6}\u{6}", SimpleFileOptions::default())
2393 .unwrap();
2394 writer
2395 .start_file("\0\0\0\0\0\0", SimpleFileOptions::default())
2396 .unwrap();
2397 writer
2398 .start_file("\0", SimpleFileOptions::default())
2399 .unwrap();
2400 writer.start_file("", SimpleFileOptions::default()).unwrap();
2401 writer
2402 .start_file("\0\0", SimpleFileOptions::default())
2403 .unwrap();
2404 writer
2405 .start_file(
2406 "\0\0\0PK\u{6}\u{7}\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
2407 SimpleFileOptions::default(),
2408 )
2409 .unwrap();
2410 let zip = writer.finish().unwrap();
2411 let _ = ZipArchive::new(zip).unwrap();
2412 }
2413
2414 #[test]
2415 fn test_filename_looks_like_zip64_locator_5() -> ZipResult<()> {
2416 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2417 writer
2418 .add_directory("", SimpleFileOptions::default().with_alignment(21))
2419 .unwrap();
2420 let mut writer = ZipWriter::new_append(writer.finish().unwrap()).unwrap();
2421 writer.shallow_copy_file("/", "").unwrap();
2422 writer.shallow_copy_file("", "\0").unwrap();
2423 writer.shallow_copy_file("\0", "PK\u{6}\u{6}").unwrap();
2424 let mut writer = ZipWriter::new_append(writer.finish().unwrap()).unwrap();
2425 writer
2426 .start_file("\0\0\0\0\0\0", SimpleFileOptions::default())
2427 .unwrap();
2428 let mut writer = ZipWriter::new_append(writer.finish().unwrap()).unwrap();
2429 writer
2430 .start_file(
2431 "#PK\u{6}\u{7}\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
2432 SimpleFileOptions::default(),
2433 )
2434 .unwrap();
2435 let zip = writer.finish().unwrap();
2436 let _ = ZipArchive::new(zip).unwrap();
2437 Ok(())
2438 }
2439
2440 #[test]
2441 fn remove_shallow_copy_keeps_original() -> ZipResult<()> {
2442 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2443 writer
2444 .start_file("original", SimpleFileOptions::default())
2445 .unwrap();
2446 writer.write_all(RT_TEST_TEXT.as_bytes()).unwrap();
2447 writer
2448 .shallow_copy_file("original", "shallow_copy")
2449 .unwrap();
2450 writer.abort_file().unwrap();
2451 let mut zip = ZipArchive::new(writer.finish().unwrap()).unwrap();
2452 let mut file = zip.by_name("original").unwrap();
2453 let mut contents = Vec::new();
2454 file.read_to_end(&mut contents).unwrap();
2455 assert_eq!(RT_TEST_TEXT.as_bytes(), contents);
2456 Ok(())
2457 }
2458
2459 #[test]
2460 fn remove_encrypted_file() -> ZipResult<()> {
2461 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2462 let first_file_options = SimpleFileOptions::default()
2463 .with_alignment(65535)
2464 .with_deprecated_encryption(b"Password");
2465 writer.start_file("", first_file_options).unwrap();
2466 writer.abort_file().unwrap();
2467 let zip = writer.finish().unwrap();
2468 let mut writer = ZipWriter::new(zip);
2469 writer.start_file("", SimpleFileOptions::default()).unwrap();
2470 Ok(())
2471 }
2472
2473 #[test]
2474 fn remove_encrypted_aligned_symlink() -> ZipResult<()> {
2475 let mut options = SimpleFileOptions::default();
2476 options = options.with_deprecated_encryption(b"Password");
2477 options.alignment = 65535;
2478 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2479 writer.add_symlink("", "s\t\0\0ggggg\0\0", options).unwrap();
2480 writer.abort_file().unwrap();
2481 let zip = writer.finish().unwrap();
2482 let mut writer = ZipWriter::new_append(zip).unwrap();
2483 writer.start_file("", SimpleFileOptions::default()).unwrap();
2484 Ok(())
2485 }
2486
2487 #[cfg(feature = "deflate-zopfli")]
2488 #[test]
2489 fn zopfli_empty_write() -> ZipResult<()> {
2490 let mut options = SimpleFileOptions::default();
2491 options = options
2492 .compression_method(CompressionMethod::default())
2493 .compression_level(Some(264));
2494 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2495 writer.start_file("", options).unwrap();
2496 writer.write_all(&[]).unwrap();
2497 writer.write_all(&[]).unwrap();
2498 Ok(())
2499 }
2500
2501 #[test]
2502 fn crash_with_no_features() -> ZipResult<()> {
2503 const ORIGINAL_FILE_NAME: &str = "PK\u{6}\u{6}\0\0\0\0\0\0\0\0\0\u{2}g\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\u{1}\0\0\0\0\0\0\0\0\0\0PK\u{6}\u{7}\0\0\0\0\0\0\0\0\0\0\0\0\u{7}\0\t'";
2504 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2505 let mut options = SimpleFileOptions::default();
2506 options = options.with_alignment(3584).compression_method(Stored);
2507 writer.start_file(ORIGINAL_FILE_NAME, options)?;
2508 let archive = writer.finish()?;
2509 let mut writer = ZipWriter::new_append(archive)?;
2510 writer.shallow_copy_file(ORIGINAL_FILE_NAME, "\u{6}\\")?;
2511 writer.finish()?;
2512 Ok(())
2513 }
2514
2515 #[test]
2516 fn test_alignment() {
2517 let page_size = 4096;
2518 let options = SimpleFileOptions::default()
2519 .compression_method(Stored)
2520 .with_alignment(page_size);
2521 let mut zip = ZipWriter::new(Cursor::new(Vec::new()));
2522 let contents = b"sleeping";
2523 let () = zip.start_file("sleep", options).unwrap();
2524 let _count = zip.write(&contents[..]).unwrap();
2525 let mut zip = zip.finish_into_readable().unwrap();
2526 let file = zip.by_index(0).unwrap();
2527 assert_eq!(file.name(), "sleep");
2528 assert_eq!(file.data_start(), page_size.into());
2529 }
2530
2531 #[test]
2532 fn test_alignment_2() {
2533 let page_size = 4096;
2534 let mut data = Vec::new();
2535 {
2536 let options = SimpleFileOptions::default()
2537 .compression_method(Stored)
2538 .with_alignment(page_size);
2539 let mut zip = ZipWriter::new(Cursor::new(&mut data));
2540 let contents = b"sleeping";
2541 let () = zip.start_file("sleep", options).unwrap();
2542 let _count = zip.write(&contents[..]).unwrap();
2543 }
2544 assert_eq!(data[4096..4104], b"sleeping"[..]);
2545 {
2546 let mut zip = ZipArchive::new(Cursor::new(&mut data)).unwrap();
2547 let file = zip.by_index(0).unwrap();
2548 assert_eq!(file.name(), "sleep");
2549 assert_eq!(file.data_start(), page_size.into());
2550 }
2551 }
2552
2553 #[test]
2554 fn test_crash_short_read() {
2555 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2556 let comment = vec![
2557 1, 80, 75, 5, 6, 237, 237, 237, 237, 237, 237, 237, 237, 44, 255, 191, 255, 255, 255,
2558 255, 255, 255, 255, 255, 16,
2559 ]
2560 .into_boxed_slice();
2561 writer.set_raw_comment(comment);
2562 let options = SimpleFileOptions::default()
2563 .compression_method(Stored)
2564 .with_alignment(11823);
2565 writer.start_file("", options).unwrap();
2566 writer.write_all(&[255, 255, 44, 255, 0]).unwrap();
2567 let written = writer.finish().unwrap();
2568 let _ = ZipWriter::new_append(written).unwrap();
2569 }
2570
2571 #[cfg(all(feature = "_deflate-any", feature = "aes-crypto"))]
2572 #[test]
2573 fn test_fuzz_failure_2024_05_08() -> ZipResult<()> {
2574 let mut first_writer = ZipWriter::new(Cursor::new(Vec::new()));
2575 let mut second_writer = ZipWriter::new(Cursor::new(Vec::new()));
2576 let options = SimpleFileOptions::default()
2577 .compression_method(Stored)
2578 .with_alignment(46036);
2579 second_writer.add_symlink("\0", "", options)?;
2580 let second_archive = second_writer.finish_into_readable()?.into_inner();
2581 let mut second_writer = ZipWriter::new_append(second_archive)?;
2582 let options = SimpleFileOptions::default()
2583 .compression_method(CompressionMethod::Deflated)
2584 .large_file(true)
2585 .with_alignment(46036)
2586 .with_aes_encryption(crate::AesMode::Aes128, "\0\0");
2587 second_writer.add_symlink("", "", options)?;
2588 let second_archive = second_writer.finish_into_readable()?.into_inner();
2589 let mut second_writer = ZipWriter::new_append(second_archive)?;
2590 let options = SimpleFileOptions::default().compression_method(Stored);
2591 second_writer.start_file(" ", options)?;
2592 let second_archive = second_writer.finish_into_readable()?;
2593 first_writer.merge_archive(second_archive)?;
2594 let _ = ZipArchive::new(first_writer.finish()?)?;
2595 Ok(())
2596 }
2597
2598 #[cfg(all(feature = "bzip2", not(miri)))]
2599 #[test]
2600 fn test_fuzz_failure_2024_06_08() -> ZipResult<()> {
2601 use crate::write::ExtendedFileOptions;
2602 use CompressionMethod::Bzip2;
2603
2604 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2605 writer.set_flush_on_finish_file(false);
2606 const SYMLINK_PATH: &str = "PK\u{6}\u{6}K\u{6}\u{6}\u{6}\0\0\0\0\u{18}\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\u{1}\0\0\0\0\0\0\0\0\u{1}\0\0PK\u{1}\u{2},\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\u{1}\0\0PK\u{1}\u{2},\0\0\0\0\0\0\0\0\0\0l\0\0\0\0\0\0PK\u{6}\u{7}P\0\0\0\0\0\0\0\0\0\0\0\0\u{1}\0\0";
2607 let sub_writer = {
2608 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2609 writer.set_flush_on_finish_file(false);
2610 let options = FileOptions {
2611 compression_method: Bzip2,
2612 compression_level: None,
2613 last_modified_time: DateTime::from_date_and_time(1980, 5, 20, 21, 0, 57)?,
2614 permissions: None,
2615 large_file: false,
2616 encrypt_with: None,
2617 extended_options: ExtendedFileOptions {
2618 extra_data: vec![].into(),
2619 central_extra_data: vec![].into(),
2620 },
2621 alignment: 2048,
2622 ..Default::default()
2623 };
2624 writer.add_symlink_from_path(SYMLINK_PATH, "||\0\0\0\0", options)?;
2625 writer = ZipWriter::new_append(writer.finish_into_readable()?.into_inner())?;
2626 writer.deep_copy_file_from_path(SYMLINK_PATH, "")?;
2627 writer = ZipWriter::new_append(writer.finish_into_readable()?.into_inner())?;
2628 writer.abort_file()?;
2629 writer
2630 };
2631 writer.merge_archive(sub_writer.finish_into_readable()?)?;
2632 writer = ZipWriter::new_append(writer.finish_into_readable()?.into_inner())?;
2633 writer.deep_copy_file_from_path(SYMLINK_PATH, "foo")?;
2634 let _ = writer.finish_into_readable()?;
2635 Ok(())
2636 }
2637
2638 #[test]
2639 fn test_short_extra_data() {
2640 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2641 writer.set_flush_on_finish_file(false);
2642 let options = FileOptions {
2643 extended_options: ExtendedFileOptions {
2644 extra_data: vec![].into(),
2645 central_extra_data: vec![99, 0, 15, 0, 207].into(),
2646 },
2647 ..Default::default()
2648 };
2649 assert!(writer.start_file_from_path("", options).is_err());
2650 }
2651
2652 #[test]
2653 #[cfg(not(feature = "unreserved"))]
2654 fn test_invalid_extra_data() -> ZipResult<()> {
2655 use crate::write::ExtendedFileOptions;
2656 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2657 writer.set_flush_on_finish_file(false);
2658 let options = FileOptions {
2659 compression_method: Stored,
2660 compression_level: None,
2661 last_modified_time: DateTime::from_date_and_time(1980, 1, 4, 6, 54, 0)?,
2662 permissions: None,
2663 large_file: false,
2664 encrypt_with: None,
2665 extended_options: ExtendedFileOptions {
2666 extra_data: vec![].into(),
2667 central_extra_data: vec![
2668 7, 0, 15, 0, 207, 117, 177, 117, 112, 2, 0, 255, 255, 131, 255, 255, 255, 80,
2669 185,
2670 ]
2671 .into(),
2672 },
2673 alignment: 32787,
2674 ..Default::default()
2675 };
2676 assert!(writer.start_file_from_path("", options).is_err());
2677 Ok(())
2678 }
2679
2680 #[test]
2681 #[cfg(not(feature = "unreserved"))]
2682 fn test_invalid_extra_data_unreserved() {
2683 use crate::write::ExtendedFileOptions;
2684 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2685 let options = FileOptions {
2686 compression_method: Stored,
2687 compression_level: None,
2688 last_modified_time: DateTime::from_date_and_time(2021, 8, 8, 1, 0, 29).unwrap(),
2689 permissions: None,
2690 large_file: true,
2691 encrypt_with: None,
2692 extended_options: ExtendedFileOptions {
2693 extra_data: vec![].into(),
2694 central_extra_data: vec![
2695 1, 41, 4, 0, 1, 255, 245, 117, 117, 112, 5, 0, 80, 255, 149, 255, 247,
2696 ]
2697 .into(),
2698 },
2699 alignment: 4103,
2700 ..Default::default()
2701 };
2702 assert!(writer.start_file_from_path("", options).is_err());
2703 }
2704
2705 #[cfg(feature = "deflate64")]
2706 #[test]
2707 fn test_fuzz_crash_2024_06_13a() -> ZipResult<()> {
2708 use crate::write::ExtendedFileOptions;
2709 use CompressionMethod::Deflate64;
2710
2711 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2712 writer.set_flush_on_finish_file(false);
2713 let options = FileOptions {
2714 compression_method: Deflate64,
2715 compression_level: None,
2716 last_modified_time: DateTime::from_date_and_time(2039, 4, 17, 6, 18, 19)?,
2717 permissions: None,
2718 large_file: true,
2719 encrypt_with: None,
2720 extended_options: ExtendedFileOptions {
2721 extra_data: vec![].into(),
2722 central_extra_data: vec![].into(),
2723 },
2724 alignment: 4,
2725 ..Default::default()
2726 };
2727 writer.add_directory_from_path("", options)?;
2728 let _ = writer.finish_into_readable()?;
2729 Ok(())
2730 }
2731
2732 #[test]
2733 fn test_fuzz_crash_2024_06_13b() -> ZipResult<()> {
2734 use crate::write::ExtendedFileOptions;
2735 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2736 writer.set_flush_on_finish_file(false);
2737 let sub_writer = {
2738 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2739 writer.set_flush_on_finish_file(false);
2740 let options = FileOptions {
2741 compression_method: Stored,
2742 compression_level: None,
2743 last_modified_time: DateTime::from_date_and_time(1980, 4, 14, 6, 11, 54)?,
2744 permissions: None,
2745 large_file: false,
2746 encrypt_with: None,
2747 extended_options: ExtendedFileOptions {
2748 extra_data: vec![].into(),
2749 central_extra_data: vec![].into(),
2750 },
2751 alignment: 185,
2752 ..Default::default()
2753 };
2754 writer.add_symlink_from_path("", "", options)?;
2755 writer
2756 };
2757 writer.merge_archive(sub_writer.finish_into_readable()?)?;
2758 writer.deep_copy_file_from_path("", "_copy")?;
2759 let _ = writer.finish_into_readable()?;
2760 Ok(())
2761 }
2762
2763 #[test]
2764 fn test_fuzz_crash_2024_06_14() -> ZipResult<()> {
2765 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2766 writer.set_flush_on_finish_file(false);
2767 let sub_writer = {
2768 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2769 writer.set_flush_on_finish_file(false);
2770 let options = FullFileOptions {
2771 compression_method: Stored,
2772 large_file: true,
2773 alignment: 93,
2774 ..Default::default()
2775 };
2776 writer.start_file_from_path("\0", options)?;
2777 writer = ZipWriter::new_append(writer.finish()?)?;
2778 writer.deep_copy_file_from_path("\0", "")?;
2779 writer
2780 };
2781 writer.merge_archive(sub_writer.finish_into_readable()?)?;
2782 writer.deep_copy_file_from_path("", "copy")?;
2783 let _ = writer.finish_into_readable()?;
2784 Ok(())
2785 }
2786
2787 #[test]
2788 fn test_fuzz_crash_2024_06_14a() -> ZipResult<()> {
2789 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2790 writer.set_flush_on_finish_file(false);
2791 let options = FileOptions {
2792 compression_method: Stored,
2793 compression_level: None,
2794 last_modified_time: DateTime::from_date_and_time(2083, 5, 30, 21, 45, 35)?,
2795 permissions: None,
2796 large_file: false,
2797 encrypt_with: None,
2798 extended_options: ExtendedFileOptions {
2799 extra_data: vec![].into(),
2800 central_extra_data: vec![].into(),
2801 },
2802 alignment: 2565,
2803 ..Default::default()
2804 };
2805 writer.add_symlink_from_path("", "", options)?;
2806 writer.abort_file()?;
2807 let options = FileOptions {
2808 compression_method: Stored,
2809 compression_level: None,
2810 last_modified_time: DateTime::default(),
2811 permissions: None,
2812 large_file: false,
2813 encrypt_with: None,
2814 extended_options: ExtendedFileOptions {
2815 extra_data: vec![].into(),
2816 central_extra_data: vec![].into(),
2817 },
2818 alignment: 0,
2819 ..Default::default()
2820 };
2821 writer.start_file_from_path("", options)?;
2822 let _ = writer.finish_into_readable()?;
2823 Ok(())
2824 }
2825
2826 #[allow(deprecated)]
2827 #[test]
2828 fn test_fuzz_crash_2024_06_14b() -> ZipResult<()> {
2829 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2830 writer.set_flush_on_finish_file(false);
2831 let options = FileOptions {
2832 compression_method: Stored,
2833 compression_level: None,
2834 last_modified_time: DateTime::from_date_and_time(2078, 3, 6, 12, 48, 58)?,
2835 permissions: None,
2836 large_file: true,
2837 encrypt_with: None,
2838 extended_options: ExtendedFileOptions {
2839 extra_data: vec![].into(),
2840 central_extra_data: vec![].into(),
2841 },
2842 alignment: 65521,
2843 ..Default::default()
2844 };
2845 writer.start_file_from_path("\u{4}\0@\n//\u{c}", options)?;
2846 writer = ZipWriter::new_append(writer.finish()?)?;
2847 writer.abort_file()?;
2848 let options = FileOptions {
2849 compression_method: CompressionMethod::Unsupported(65535),
2850 compression_level: None,
2851 last_modified_time: DateTime::from_date_and_time(2055, 10, 2, 11, 48, 49)?,
2852 permissions: None,
2853 large_file: true,
2854 encrypt_with: None,
2855 extended_options: ExtendedFileOptions {
2856 extra_data: vec![255, 255, 1, 0, 255, 0, 0, 0, 0].into(),
2857 central_extra_data: vec![].into(),
2858 },
2859 alignment: 65535,
2860 ..Default::default()
2861 };
2862 writer.add_directory_from_path("", options)?;
2863 let _ = writer.finish_into_readable()?;
2864 Ok(())
2865 }
2866
2867 #[test]
2868 fn test_fuzz_crash_2024_06_14c() -> ZipResult<()> {
2869 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2870 writer.set_flush_on_finish_file(false);
2871 let sub_writer = {
2872 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2873 writer.set_flush_on_finish_file(false);
2874 let options = FileOptions {
2875 compression_method: Stored,
2876 compression_level: None,
2877 last_modified_time: DateTime::from_date_and_time(2060, 4, 6, 13, 13, 3)?,
2878 permissions: None,
2879 large_file: true,
2880 encrypt_with: None,
2881 extended_options: ExtendedFileOptions {
2882 extra_data: vec![].into(),
2883 central_extra_data: vec![].into(),
2884 },
2885 alignment: 0,
2886 ..Default::default()
2887 };
2888 writer.start_file_from_path("\0", options)?;
2889 writer.write_all(&([]))?;
2890 writer = ZipWriter::new_append(writer.finish()?)?;
2891 writer.deep_copy_file_from_path("\0", "")?;
2892 writer
2893 };
2894 writer.merge_archive(sub_writer.finish_into_readable()?)?;
2895 writer.deep_copy_file_from_path("", "_copy")?;
2896 let _ = writer.finish_into_readable()?;
2897 Ok(())
2898 }
2899
2900 #[cfg(all(feature = "_deflate-any", feature = "aes-crypto"))]
2901 #[test]
2902 fn test_fuzz_crash_2024_06_14d() -> ZipResult<()> {
2903 use crate::write::EncryptWith::Aes;
2904 use crate::AesMode::Aes256;
2905 use CompressionMethod::Deflated;
2906 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2907 writer.set_flush_on_finish_file(false);
2908 let options = FileOptions {
2909 compression_method: Deflated,
2910 compression_level: Some(5),
2911 last_modified_time: DateTime::from_date_and_time(2107, 4, 8, 15, 54, 19)?,
2912 permissions: None,
2913 large_file: true,
2914 encrypt_with: Some(Aes {
2915 mode: Aes256,
2916 password: "",
2917 }),
2918 extended_options: ExtendedFileOptions {
2919 extra_data: vec![2, 0, 1, 0, 0].into(),
2920 central_extra_data: vec![
2921 35, 229, 2, 0, 41, 41, 231, 44, 2, 0, 52, 233, 82, 201, 0, 0, 3, 0, 2, 0, 233,
2922 255, 3, 0, 2, 0, 26, 154, 38, 251, 0, 0,
2923 ]
2924 .into(),
2925 },
2926 alignment: 65535,
2927 ..Default::default()
2928 };
2929 assert!(writer.add_directory_from_path("", options).is_err());
2930 Ok(())
2931 }
2932
2933 #[test]
2934 fn test_fuzz_crash_2024_06_14e() -> ZipResult<()> {
2935 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2936 writer.set_flush_on_finish_file(false);
2937 let options = FileOptions {
2938 compression_method: Stored,
2939 compression_level: None,
2940 last_modified_time: DateTime::from_date_and_time(1988, 1, 1, 1, 6, 26)?,
2941 permissions: None,
2942 large_file: true,
2943 encrypt_with: None,
2944 extended_options: ExtendedFileOptions {
2945 extra_data: vec![76, 0, 1, 0, 0, 2, 0, 0, 0].into(),
2946 central_extra_data: vec![
2947 1, 149, 1, 0, 255, 3, 0, 0, 0, 2, 255, 0, 0, 12, 65, 1, 0, 0, 67, 149, 0, 0,
2948 76, 149, 2, 0, 149, 149, 67, 149, 0, 0,
2949 ]
2950 .into(),
2951 },
2952 alignment: 65535,
2953 ..Default::default()
2954 };
2955 assert!(writer.add_directory_from_path("", options).is_err());
2956 let _ = writer.finish_into_readable()?;
2957 Ok(())
2958 }
2959
2960 #[allow(deprecated)]
2961 #[test]
2962 fn test_fuzz_crash_2024_06_17() -> ZipResult<()> {
2963 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2964 writer.set_flush_on_finish_file(false);
2965 let sub_writer = {
2966 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2967 writer.set_flush_on_finish_file(false);
2968 let sub_writer = {
2969 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2970 writer.set_flush_on_finish_file(false);
2971 let sub_writer = {
2972 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2973 writer.set_flush_on_finish_file(false);
2974 let sub_writer = {
2975 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2976 writer.set_flush_on_finish_file(false);
2977 let sub_writer = {
2978 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2979 writer.set_flush_on_finish_file(false);
2980 let sub_writer = {
2981 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2982 writer.set_flush_on_finish_file(false);
2983 let sub_writer = {
2984 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2985 writer.set_flush_on_finish_file(false);
2986 let sub_writer = {
2987 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
2988 writer.set_flush_on_finish_file(false);
2989 let sub_writer = {
2990 let mut writer =
2991 ZipWriter::new(Cursor::new(Vec::new()));
2992 writer.set_flush_on_finish_file(false);
2993 let options = FileOptions {
2994 compression_method: CompressionMethod::Unsupported(
2995 65535,
2996 ),
2997 compression_level: Some(5),
2998 last_modified_time: DateTime::from_date_and_time(
2999 2107, 2, 8, 15, 0, 0,
3000 )?,
3001 permissions: None,
3002 large_file: true,
3003 encrypt_with: Some(ZipCrypto(
3004 ZipCryptoKeys::of(
3005 0x63ff, 0xc62d3103, 0xfffe00ea,
3006 ),
3007 PhantomData,
3008 )),
3009 extended_options: ExtendedFileOptions {
3010 extra_data: vec![].into(),
3011 central_extra_data: vec![].into(),
3012 },
3013 alignment: 255,
3014 ..Default::default()
3015 };
3016 writer.add_symlink_from_path("1\0PK\u{6}\u{6}\u{b}\u{6}\u{6}\u{6}\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\u{1}\0\0\0\0\0\0\0\0\u{b}\0\0PK\u{1}\u{2},\0\0\0\0\0\0\0\0\0\0\0\u{10}\0\0\0K\u{6}\u{6}\0\0\0\0\0\0\0\0PK\u{2}\u{6}", "", options)?;
3017 writer = ZipWriter::new_append(
3018 writer.finish_into_readable()?.into_inner(),
3019 )?;
3020 writer
3021 };
3022 writer.merge_archive(sub_writer.finish_into_readable()?)?;
3023 writer = ZipWriter::new_append(
3024 writer.finish_into_readable()?.into_inner(),
3025 )?;
3026 let options = FileOptions {
3027 compression_method: Stored,
3028 compression_level: None,
3029 last_modified_time: DateTime::from_date_and_time(
3030 1992, 7, 3, 0, 0, 0,
3031 )?,
3032 permissions: None,
3033 large_file: true,
3034 encrypt_with: None,
3035 extended_options: ExtendedFileOptions {
3036 extra_data: vec![].into(),
3037 central_extra_data: vec![].into(),
3038 },
3039 alignment: 43,
3040 ..Default::default()
3041 };
3042 writer.start_file_from_path(
3043 "\0\0\0\u{3}\0\u{1a}\u{1a}\u{1a}\u{1a}\u{1a}\u{1a}",
3044 options,
3045 )?;
3046 let options = FileOptions {
3047 compression_method: Stored,
3048 compression_level: None,
3049 last_modified_time: DateTime::from_date_and_time(
3050 2006, 3, 27, 2, 24, 26,
3051 )?,
3052 permissions: None,
3053 large_file: false,
3054 encrypt_with: None,
3055 extended_options: ExtendedFileOptions {
3056 extra_data: vec![].into(),
3057 central_extra_data: vec![].into(),
3058 },
3059 alignment: 26,
3060 ..Default::default()
3061 };
3062 writer.start_file_from_path("\0K\u{6}\u{6}\0PK\u{6}\u{7}PK\u{6}\u{6}\0\0\0\0\0\0\0\0PK\u{2}\u{6}", options)?;
3063 writer = ZipWriter::new_append(
3064 writer.finish_into_readable()?.into_inner(),
3065 )?;
3066 let options = FileOptions {
3067 compression_method: Stored,
3068 compression_level: Some(17),
3069 last_modified_time: DateTime::from_date_and_time(
3070 2103, 4, 10, 23, 15, 18,
3071 )?,
3072 permissions: Some(3284386755),
3073 large_file: true,
3074 encrypt_with: Some(ZipCrypto(
3075 ZipCryptoKeys::of(
3076 0x8888c5bf, 0x88888888, 0xff888888,
3077 ),
3078 PhantomData,
3079 )),
3080 extended_options: ExtendedFileOptions {
3081 extra_data: vec![3, 0, 1, 0, 255, 144, 136, 0, 0]
3082 .into(),
3083 central_extra_data: vec![].into(),
3084 },
3085 alignment: 65535,
3086 ..Default::default()
3087 };
3088 writer.add_symlink_from_path("", "\nu", options)?;
3089 writer = ZipWriter::new_append(writer.finish()?)?;
3090 writer
3091 };
3092 writer.merge_archive(sub_writer.finish_into_readable()?)?;
3093 writer = ZipWriter::new_append(
3094 writer.finish_into_readable()?.into_inner(),
3095 )?;
3096 writer
3097 };
3098 writer.merge_archive(sub_writer.finish_into_readable()?)?;
3099 writer = ZipWriter::new_append(writer.finish()?)?;
3100 writer
3101 };
3102 writer.merge_archive(sub_writer.finish_into_readable()?)?;
3103 writer =
3104 ZipWriter::new_append(writer.finish_into_readable()?.into_inner())?;
3105 writer.abort_file()?;
3106 let options = FileOptions {
3107 compression_method: CompressionMethod::Unsupported(49603),
3108 compression_level: Some(20),
3109 last_modified_time: DateTime::from_date_and_time(
3110 2047, 4, 14, 3, 15, 14,
3111 )?,
3112 permissions: Some(3284386755),
3113 large_file: true,
3114 encrypt_with: Some(ZipCrypto(
3115 ZipCryptoKeys::of(0xc3, 0x0, 0x0),
3116 PhantomData,
3117 )),
3118 extended_options: ExtendedFileOptions {
3119 extra_data: vec![].into(),
3120 central_extra_data: vec![].into(),
3121 },
3122 alignment: 0,
3123 ..Default::default()
3124 };
3125 writer.add_directory_from_path("", options)?;
3126 writer.deep_copy_file_from_path("/", "")?;
3127 writer.shallow_copy_file_from_path("", "copy")?;
3128 assert!(writer.shallow_copy_file_from_path("", "copy").is_err());
3129 assert!(writer.shallow_copy_file_from_path("", "copy").is_err());
3130 assert!(writer.shallow_copy_file_from_path("", "copy").is_err());
3131 assert!(writer.shallow_copy_file_from_path("", "copy").is_err());
3132 assert!(writer.shallow_copy_file_from_path("", "copy").is_err());
3133 assert!(writer.shallow_copy_file_from_path("", "copy").is_err());
3134 writer
3135 };
3136 writer.merge_archive(sub_writer.finish_into_readable()?)?;
3137 writer
3138 };
3139 writer.merge_archive(sub_writer.finish_into_readable()?)?;
3140 writer
3141 };
3142 writer.merge_archive(sub_writer.finish_into_readable()?)?;
3143 writer
3144 };
3145 writer.merge_archive(sub_writer.finish_into_readable()?)?;
3146 writer
3147 };
3148 writer.merge_archive(sub_writer.finish_into_readable()?)?;
3149 let _ = writer.finish_into_readable()?;
3150 Ok(())
3151 }
3152
3153 #[test]
3154 fn test_fuzz_crash_2024_06_17a() -> ZipResult<()> {
3155 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3156 writer.set_flush_on_finish_file(false);
3157 const PATH_1: &str = "\0I\01\0P\0\0\u{2}\0\0\u{1a}\u{1a}\u{1a}\u{1a}\u{1b}\u{1a}UT\u{5}\0\0\u{1a}\u{1a}\u{1a}\u{1a}UT\u{5}\0\u{1}\0\u{1a}\u{1a}\u{1a}UT\t\0uc\u{5}\0\0\0\0\u{7f}\u{7f}\u{7f}\u{7f}PK\u{6}";
3158 let sub_writer = {
3159 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3160 writer.set_flush_on_finish_file(false);
3161 let sub_writer = {
3162 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3163 writer.set_flush_on_finish_file(false);
3164 let options = FileOptions {
3165 compression_method: Stored,
3166 compression_level: None,
3167 last_modified_time: DateTime::from_date_and_time(1981, 1, 1, 0, 24, 21)?,
3168 permissions: Some(16908288),
3169 large_file: false,
3170 encrypt_with: None,
3171 extended_options: ExtendedFileOptions {
3172 extra_data: vec![].into(),
3173 central_extra_data: vec![].into(),
3174 },
3175 alignment: 20555,
3176 ..Default::default()
3177 };
3178 writer.start_file_from_path(
3179 "\0\u{7}\u{1}\0\0\0\0\0\0\0\0\u{1}\0\0PK\u{1}\u{2};",
3180 options,
3181 )?;
3182 writer.write_all(
3183 &([
3184 255, 255, 255, 255, 253, 253, 253, 203, 203, 203, 253, 253, 253, 253, 255,
3185 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 249, 191, 225, 225,
3186 241, 197,
3187 ]),
3188 )?;
3189 writer.write_all(
3190 &([
3191 197, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
3192 255, 75, 0,
3193 ]),
3194 )?;
3195 writer
3196 };
3197 writer.merge_archive(sub_writer.finish_into_readable()?)?;
3198 writer = ZipWriter::new_append(writer.finish_into_readable()?.into_inner())?;
3199 let options = FileOptions {
3200 compression_method: Stored,
3201 compression_level: None,
3202 last_modified_time: DateTime::from_date_and_time(1980, 11, 14, 10, 46, 47)?,
3203 permissions: None,
3204 large_file: false,
3205 encrypt_with: None,
3206 extended_options: ExtendedFileOptions {
3207 extra_data: vec![].into(),
3208 central_extra_data: vec![].into(),
3209 },
3210 alignment: 0,
3211 ..Default::default()
3212 };
3213 writer.start_file_from_path(PATH_1, options)?;
3214 writer.deep_copy_file_from_path(PATH_1, "eee\u{6}\0\0\0\0\0\0\0\0\0\0\0$\0\0\0\0\0\0\u{7f}\u{7f}PK\u{6}\u{6}K\u{6}\u{6}\u{6}\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\u{1}\0\0\0\0\0\0\0\0\u{1}\0\0PK\u{1}\u{1e},\0\0\0\0\0\0\0\0\0\0\0\u{8}\0*\0\0\u{1}PK\u{6}\u{7}PK\u{6}\u{6}\0\0\0\0\0\0\0\0}K\u{2}\u{6}")?;
3215 writer
3216 };
3217 writer.merge_archive(sub_writer.finish_into_readable()?)?;
3218 writer = ZipWriter::new_append(writer.finish_into_readable()?.into_inner())?;
3219 writer.deep_copy_file_from_path(PATH_1, "")?;
3220 writer = ZipWriter::new_append(writer.finish_into_readable()?.into_inner())?;
3221 writer.shallow_copy_file_from_path("", "copy")?;
3222 writer = ZipWriter::new_append(writer.finish_into_readable()?.into_inner())?;
3223 let _ = writer.finish_into_readable()?;
3224 Ok(())
3225 }
3226
3227 #[test]
3228 #[allow(clippy::octal_escapes)]
3229 #[cfg(all(feature = "bzip2", not(miri)))]
3230 fn test_fuzz_crash_2024_06_17b() -> ZipResult<()> {
3231 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3232 writer.set_flush_on_finish_file(false);
3233 let sub_writer = {
3234 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3235 writer.set_flush_on_finish_file(false);
3236 let sub_writer = {
3237 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3238 writer.set_flush_on_finish_file(false);
3239 let sub_writer = {
3240 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3241 writer.set_flush_on_finish_file(false);
3242 let sub_writer = {
3243 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3244 writer.set_flush_on_finish_file(false);
3245 let sub_writer = {
3246 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3247 writer.set_flush_on_finish_file(false);
3248 let sub_writer = {
3249 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3250 writer.set_flush_on_finish_file(false);
3251 let sub_writer = {
3252 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3253 writer.set_flush_on_finish_file(false);
3254 let sub_writer = {
3255 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3256 writer.set_flush_on_finish_file(false);
3257 let options = FileOptions {
3258 compression_method: Stored,
3259 compression_level: None,
3260 last_modified_time: DateTime::from_date_and_time(
3261 1981, 1, 1, 0, 0, 21,
3262 )?,
3263 permissions: Some(16908288),
3264 large_file: false,
3265 encrypt_with: None,
3266 extended_options: ExtendedFileOptions {
3267 extra_data: vec![].into(),
3268 central_extra_data: vec![].into(),
3269 },
3270 alignment: 20555,
3271 ..Default::default()
3272 };
3273 writer.start_file_from_path("\0\u{7}\u{1}\0\0\0\0\0\0\0\0\u{1}\0\0PK\u{1}\u{2};\u{1a}\u{18}\u{1a}UT\t.........................\0u", options)?;
3274 writer
3275 };
3276 writer.merge_archive(sub_writer.finish_into_readable()?)?;
3277 let options = FileOptions {
3278 compression_method: CompressionMethod::Bzip2,
3279 compression_level: Some(5),
3280 last_modified_time: DateTime::from_date_and_time(
3281 2055, 7, 7, 3, 6, 6,
3282 )?,
3283 permissions: None,
3284 large_file: false,
3285 encrypt_with: None,
3286 extended_options: ExtendedFileOptions {
3287 extra_data: vec![].into(),
3288 central_extra_data: vec![].into(),
3289 },
3290 alignment: 0,
3291 ..Default::default()
3292 };
3293 writer.start_file_from_path("\0\0\0\0..\0\0\0\0\0\u{7f}\u{7f}PK\u{6}\u{6}K\u{6}\u{6}\u{6}\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\u{1}\0\0\0\0\0\0\0\0\u{1}\0\0PK\u{1}\u{1e},\0\0\0\0\0\0\0\0\0\0\0\u{8}\0*\0\0\u{1}PK\u{6}\u{7}PK\u{6}\u{6}\0\0\0\0\0\0\0\0}K\u{2}\u{6}", options)?;
3294 writer = ZipWriter::new_append(
3295 writer.finish_into_readable()?.into_inner(),
3296 )?;
3297 writer
3298 };
3299 writer.merge_archive(sub_writer.finish_into_readable()?)?;
3300 writer = ZipWriter::new_append(
3301 writer.finish_into_readable()?.into_inner(),
3302 )?;
3303 writer
3304 };
3305 writer.merge_archive(sub_writer.finish_into_readable()?)?;
3306 writer =
3307 ZipWriter::new_append(writer.finish_into_readable()?.into_inner())?;
3308 writer
3309 };
3310 writer.merge_archive(sub_writer.finish_into_readable()?)?;
3311 writer =
3312 ZipWriter::new_append(writer.finish_into_readable()?.into_inner())?;
3313 writer
3314 };
3315 writer.merge_archive(sub_writer.finish_into_readable()?)?;
3316 writer
3317 };
3318 writer.merge_archive(sub_writer.finish_into_readable()?)?;
3319 writer
3320 };
3321 writer.merge_archive(sub_writer.finish_into_readable()?)?;
3322 writer
3323 };
3324 writer.merge_archive(sub_writer.finish_into_readable()?)?;
3325 let _ = writer.finish_into_readable()?;
3326 Ok(())
3327 }
3328
3329 #[test]
3330 fn test_fuzz_crash_2024_06_18() -> ZipResult<()> {
3331 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3332 writer.set_raw_comment(Box::<[u8]>::from([
3333 80, 75, 5, 6, 255, 255, 255, 255, 255, 255, 80, 75, 5, 6, 255, 255, 255, 255, 255, 255,
3334 13, 0, 13, 13, 13, 13, 13, 255, 255, 255, 255, 255, 255, 255, 255,
3335 ]));
3336 let sub_writer = {
3337 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3338 writer.set_flush_on_finish_file(false);
3339 writer.set_raw_comment(Box::new([]));
3340 writer
3341 };
3342 writer.merge_archive(sub_writer.finish_into_readable()?)?;
3343 writer = ZipWriter::new_append(writer.finish()?)?;
3344 let _ = writer.finish_into_readable()?;
3345 Ok(())
3346 }
3347
3348 #[test]
3349 fn test_fuzz_crash_2024_06_18a() -> ZipResult<()> {
3350 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3351 writer.set_flush_on_finish_file(false);
3352 writer.set_raw_comment(Box::<[u8]>::from([]));
3353 let sub_writer = {
3354 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3355 writer.set_flush_on_finish_file(false);
3356 let sub_writer = {
3357 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3358 writer.set_flush_on_finish_file(false);
3359 let sub_writer = {
3360 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3361 writer.set_flush_on_finish_file(false);
3362 let options = FullFileOptions {
3363 compression_method: Stored,
3364 compression_level: None,
3365 last_modified_time: DateTime::from_date_and_time(2107, 4, 8, 14, 0, 19)?,
3366 permissions: None,
3367 large_file: false,
3368 encrypt_with: None,
3369 extended_options: ExtendedFileOptions {
3370 extra_data: vec![
3371 182, 180, 1, 0, 180, 182, 74, 0, 0, 200, 0, 0, 0, 2, 0, 0, 0,
3372 ]
3373 .into(),
3374 central_extra_data: vec![].into(),
3375 },
3376 alignment: 1542,
3377 ..Default::default()
3378 };
3379 writer.start_file_from_path("\0\0PK\u{6}\u{6}K\u{6}PK\u{3}\u{4}\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\u{1}\0\0\0\0\0\0\0\0\u{1}\u{1}\0PK\u{1}\u{2},\0\0\0\0\0\0\0\0\0\0\0P\u{7}\u{4}/.\0KP\0\0;\0\0\0\u{1e}\0\0\0\0\0\0\0\0\0\0\0\0\0", options)?;
3380 let finished = writer.finish_into_readable()?;
3381 assert_eq!(1, finished.file_names().count());
3382 writer = ZipWriter::new_append(finished.into_inner())?;
3383 let options = FullFileOptions {
3384 compression_method: Stored,
3385 compression_level: Some(5),
3386 last_modified_time: DateTime::from_date_and_time(2107, 4, 1, 0, 0, 0)?,
3387 permissions: None,
3388 large_file: false,
3389 encrypt_with: Some(ZipCrypto(
3390 ZipCryptoKeys::of(0x0, 0x62e4b50, 0x100),
3391 PhantomData,
3392 )),
3393 ..Default::default()
3394 };
3395 writer.add_symlink_from_path(
3396 "\0K\u{6}\0PK\u{6}\u{7}PK\u{6}\u{6}\0\0\0\0\0\0\0\0PK\u{2}\u{6}",
3397 "\u{8}\0\0\0\0/\0",
3398 options,
3399 )?;
3400 let finished = writer.finish_into_readable()?;
3401 assert_eq!(2, finished.file_names().count());
3402 writer = ZipWriter::new_append(finished.into_inner())?;
3403 assert_eq!(2, writer.files.len());
3404 writer
3405 };
3406 let finished = sub_writer.finish_into_readable()?;
3407 assert_eq!(2, finished.file_names().count());
3408 writer.merge_archive(finished)?;
3409 writer = ZipWriter::new_append(writer.finish_into_readable()?.into_inner())?;
3410 writer
3411 };
3412 writer.merge_archive(sub_writer.finish_into_readable()?)?;
3413 writer
3414 };
3415 writer.merge_archive(sub_writer.finish_into_readable()?)?;
3416 let _ = writer.finish_into_readable()?;
3417 Ok(())
3418 }
3419
3420 #[cfg(all(feature = "bzip2", feature = "aes-crypto", not(miri)))]
3421 #[test]
3422 fn test_fuzz_crash_2024_06_18b() -> ZipResult<()> {
3423 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3424 writer.set_flush_on_finish_file(true);
3425 writer.set_raw_comment([0].into());
3426 writer = ZipWriter::new_append(writer.finish_into_readable()?.into_inner())?;
3427 assert_eq!(writer.get_raw_comment()[0], 0);
3428 let options = FileOptions {
3429 compression_method: CompressionMethod::Bzip2,
3430 compression_level: None,
3431 last_modified_time: DateTime::from_date_and_time(2009, 6, 3, 13, 37, 39)?,
3432 permissions: Some(2644352413),
3433 large_file: true,
3434 encrypt_with: Some(crate::write::EncryptWith::Aes {
3435 mode: crate::AesMode::Aes256,
3436 password: "",
3437 }),
3438 extended_options: ExtendedFileOptions {
3439 extra_data: vec![].into(),
3440 central_extra_data: vec![].into(),
3441 },
3442 alignment: 255,
3443 ..Default::default()
3444 };
3445 writer.add_symlink_from_path("", "", options)?;
3446 writer.deep_copy_file_from_path("", "PK\u{5}\u{6}\0\0\0\0\0\0\0\0\0\0\0\0\0\u{4}\0\0\0")?;
3447 writer = ZipWriter::new_append(writer.finish_into_readable()?.into_inner())?;
3448 assert_eq!(writer.get_raw_comment()[0], 0);
3449 writer.deep_copy_file_from_path(
3450 "PK\u{5}\u{6}\0\0\0\0\0\0\0\0\0\0\0\0\0\u{4}\0\0\0",
3451 "\u{2}yy\u{5}qu\0",
3452 )?;
3453 let finished = writer.finish()?;
3454 let archive = ZipArchive::new(finished.clone())?;
3455 assert_eq!(archive.comment(), [0]);
3456 writer = ZipWriter::new_append(finished)?;
3457 assert_eq!(writer.get_raw_comment()[0], 0);
3458 let _ = writer.finish_into_readable()?;
3459 Ok(())
3460 }
3461
3462 #[test]
3463 fn test_fuzz_crash_2024_06_19() -> ZipResult<()> {
3464 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3465 writer.set_flush_on_finish_file(false);
3466 let options = FileOptions {
3467 compression_method: Stored,
3468 compression_level: None,
3469 last_modified_time: DateTime::from_date_and_time(1980, 3, 1, 19, 55, 58)?,
3470 permissions: None,
3471 large_file: false,
3472 encrypt_with: None,
3473 extended_options: ExtendedFileOptions {
3474 extra_data: vec![].into(),
3475 central_extra_data: vec![].into(),
3476 },
3477 alignment: 256,
3478 ..Default::default()
3479 };
3480 writer.start_file_from_path(
3481 "\0\0\0PK\u{5}\u{6}\0\0\0\0\u{1}\0\u{12}\u{6}\0\0\0\0\0\u{1}\0\0\0\0\0\0\0\0\0",
3482 options,
3483 )?;
3484 writer.set_flush_on_finish_file(false);
3485 writer.shallow_copy_file_from_path(
3486 "\0\0\0PK\u{5}\u{6}\0\0\0\0\u{1}\0\u{12}\u{6}\0\0\0\0\0\u{1}\0\0\0\0\0\0\0\0\0",
3487 "",
3488 )?;
3489 writer.set_flush_on_finish_file(false);
3490 writer.deep_copy_file_from_path("", "copy")?;
3491 writer.abort_file()?;
3492 writer.set_flush_on_finish_file(false);
3493 writer.set_raw_comment([255, 0].into());
3494 writer.abort_file()?;
3495 assert_eq!(writer.get_raw_comment(), [255, 0]);
3496 writer = ZipWriter::new_append(writer.finish_into_readable()?.into_inner())?;
3497 assert_eq!(writer.get_raw_comment(), [255, 0]);
3498 writer.set_flush_on_finish_file(false);
3499 let options = FileOptions {
3500 compression_method: Stored,
3501 compression_level: None,
3502 last_modified_time: DateTime::default(),
3503 permissions: None,
3504 large_file: false,
3505 encrypt_with: None,
3506 extended_options: ExtendedFileOptions {
3507 extra_data: vec![].into(),
3508 central_extra_data: vec![].into(),
3509 },
3510 ..Default::default()
3511 };
3512 writer.start_file_from_path("", options)?;
3513 assert_eq!(writer.get_raw_comment(), [255, 0]);
3514 let archive = writer.finish_into_readable()?;
3515 assert_eq!(archive.comment(), [255, 0]);
3516 Ok(())
3517 }
3518
3519 #[test]
3520 fn fuzz_crash_2024_06_21() -> ZipResult<()> {
3521 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3522 writer.set_flush_on_finish_file(false);
3523 let options = FullFileOptions {
3524 compression_method: Stored,
3525 compression_level: None,
3526 last_modified_time: DateTime::from_date_and_time(1980, 2, 1, 0, 0, 0)?,
3527 permissions: None,
3528 large_file: false,
3529 encrypt_with: None,
3530 ..Default::default()
3531 };
3532 const LONG_PATH: &str = "\0@PK\u{6}\u{6}\u{7}\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0@/\0\0\00ΝPK\u{5}\u{6}O\0\u{10}\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0@PK\u{6}\u{7}\u{6}\0/@\0\0\0\0\0\0\0\0 \0\0";
3533 writer.start_file_from_path(LONG_PATH, options)?;
3534 writer = ZipWriter::new_append(writer.finish()?)?;
3535 writer.deep_copy_file_from_path(LONG_PATH, "oo\0\0\0")?;
3536 writer.abort_file()?;
3537 writer.set_raw_comment([33].into());
3538 let archive = writer.finish_into_readable()?;
3539 writer = ZipWriter::new_append(archive.into_inner())?;
3540 assert!(writer.get_raw_comment().starts_with(&[33]));
3541 let archive = writer.finish_into_readable()?;
3542 assert!(archive.comment().starts_with(&[33]));
3543 Ok(())
3544 }
3545
3546 #[test]
3547 #[cfg(all(feature = "bzip2", not(miri)))]
3548 fn fuzz_crash_2024_07_17() -> ZipResult<()> {
3549 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3550 writer.set_flush_on_finish_file(false);
3551 let options = FileOptions {
3552 compression_method: CompressionMethod::Bzip2,
3553 compression_level: None,
3554 last_modified_time: DateTime::from_date_and_time(2095, 2, 16, 21, 0, 1)?,
3555 permissions: Some(84238341),
3556 large_file: true,
3557 encrypt_with: None,
3558 extended_options: ExtendedFileOptions {
3559 extra_data: vec![117, 99, 6, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 2, 0, 0, 0].into(),
3560 central_extra_data: vec![].into(),
3561 },
3562 alignment: 65535,
3563 ..Default::default()
3564 };
3565 writer.start_file_from_path("", options)?;
3566 writer.deep_copy_file_from_path("", "copy")?;
3568 let _ = ZipWriter::new_append(writer.finish_into_readable()?.into_inner())?;
3569 Ok(())
3570 }
3571
3572 #[test]
3573 fn fuzz_crash_2024_07_19() -> ZipResult<()> {
3574 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3575 writer.set_flush_on_finish_file(false);
3576 let options = FileOptions {
3577 compression_method: Stored,
3578 compression_level: None,
3579 last_modified_time: DateTime::from_date_and_time(1980, 6, 1, 0, 34, 47)?,
3580 permissions: None,
3581 large_file: true,
3582 encrypt_with: None,
3583 extended_options: ExtendedFileOptions {
3584 extra_data: vec![].into(),
3585 central_extra_data: vec![].into(),
3586 },
3587 alignment: 45232,
3588 ..Default::default()
3589 };
3590 writer.add_directory_from_path("", options)?;
3591 writer.deep_copy_file_from_path("/", "")?;
3592 writer = ZipWriter::new_append(writer.finish_into_readable()?.into_inner())?;
3593 writer.deep_copy_file_from_path("", "copy")?;
3594 let _ = ZipWriter::new_append(writer.finish_into_readable()?.into_inner())?;
3595 Ok(())
3596 }
3597
3598 #[test]
3599 #[cfg(feature = "aes-crypto")]
3600 fn fuzz_crash_2024_07_19a() -> ZipResult<()> {
3601 use crate::write::EncryptWith::Aes;
3602 use crate::AesMode::Aes128;
3603 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3604 writer.set_flush_on_finish_file(false);
3605 let options = FileOptions {
3606 compression_method: Stored,
3607 compression_level: None,
3608 last_modified_time: DateTime::from_date_and_time(2107, 6, 5, 13, 0, 21)?,
3609 permissions: None,
3610 large_file: true,
3611 encrypt_with: Some(Aes {
3612 mode: Aes128,
3613 password: "",
3614 }),
3615 extended_options: ExtendedFileOptions {
3616 extra_data: vec![3, 0, 4, 0, 209, 53, 53, 8, 2, 61, 0, 0].into(),
3617 central_extra_data: vec![].into(),
3618 },
3619 alignment: 65535,
3620 ..Default::default()
3621 };
3622 writer.start_file_from_path("", options)?;
3623 let _ = ZipWriter::new_append(writer.finish_into_readable()?.into_inner())?;
3624 Ok(())
3625 }
3626
3627 #[test]
3628 fn fuzz_crash_2024_07_20() -> ZipResult<()> {
3629 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3630 writer.set_flush_on_finish_file(true);
3631 let options = FileOptions {
3632 compression_method: Stored,
3633 compression_level: None,
3634 last_modified_time: DateTime::from_date_and_time(2041, 8, 2, 19, 38, 0)?,
3635 permissions: None,
3636 large_file: false,
3637 encrypt_with: None,
3638 extended_options: ExtendedFileOptions {
3639 extra_data: vec![].into(),
3640 central_extra_data: vec![].into(),
3641 },
3642 alignment: 0,
3643 ..Default::default()
3644 };
3645 writer.add_directory_from_path("\0\0\0\0\0\0\07黻", options)?;
3646 let sub_writer = {
3647 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3648 writer.set_flush_on_finish_file(false);
3649 let options = FileOptions {
3650 compression_method: Stored,
3651 compression_level: None,
3652 last_modified_time: DateTime::default(),
3653 permissions: None,
3654 large_file: false,
3655 encrypt_with: None,
3656 extended_options: ExtendedFileOptions {
3657 extra_data: vec![].into(),
3658 central_extra_data: vec![].into(),
3659 },
3660 alignment: 4,
3661 ..Default::default()
3662 };
3663 writer.add_directory_from_path("\0\0\0黻", options)?;
3664 writer = ZipWriter::new_append(writer.finish_into_readable()?.into_inner())?;
3665 writer.abort_file()?;
3666 let options = FileOptions {
3667 compression_method: Stored,
3668 compression_level: None,
3669 last_modified_time: DateTime::from_date_and_time(1980, 1, 1, 0, 7, 0)?,
3670 permissions: Some(2663103419),
3671 large_file: false,
3672 encrypt_with: None,
3673 extended_options: ExtendedFileOptions {
3674 extra_data: vec![].into(),
3675 central_extra_data: vec![].into(),
3676 },
3677 alignment: 32256,
3678 ..Default::default()
3679 };
3680 writer.add_directory_from_path("\0", options)?;
3681 writer = ZipWriter::new_append(writer.finish()?)?;
3682 writer
3683 };
3684 writer.merge_archive(sub_writer.finish_into_readable()?)?;
3685 let _ = ZipWriter::new_append(writer.finish_into_readable()?.into_inner())?;
3686 Ok(())
3687 }
3688
3689 #[test]
3690 fn fuzz_crash_2024_07_21() -> ZipResult<()> {
3691 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3692 let sub_writer = {
3693 let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
3694 writer.add_directory_from_path(
3695 "",
3696 FileOptions {
3697 compression_method: Stored,
3698 compression_level: None,
3699 last_modified_time: DateTime::from_date_and_time(2105, 8, 1, 15, 0, 0)?,
3700 permissions: None,
3701 large_file: false,
3702 encrypt_with: None,
3703 extended_options: ExtendedFileOptions {
3704 extra_data: vec![].into(),
3705 central_extra_data: vec![].into(),
3706 },
3707 alignment: 0,
3708 ..Default::default()
3709 },
3710 )?;
3711 writer.abort_file()?;
3712 let mut writer = ZipWriter::new_append(writer.finish()?)?;
3713 writer.add_directory_from_path(
3714 "",
3715 FileOptions {
3716 compression_method: Stored,
3717 compression_level: None,
3718 last_modified_time: DateTime::default(),
3719 permissions: None,
3720 large_file: false,
3721 encrypt_with: None,
3722 extended_options: ExtendedFileOptions {
3723 extra_data: vec![].into(),
3724 central_extra_data: vec![].into(),
3725 },
3726 alignment: 16,
3727 ..Default::default()
3728 },
3729 )?;
3730 ZipWriter::new_append(writer.finish()?)?
3731 };
3732 writer.merge_archive(sub_writer.finish_into_readable()?)?;
3733 let writer = ZipWriter::new_append(writer.finish()?)?;
3734 let _ = writer.finish_into_readable()?;
3735
3736 Ok(())
3737 }
3738}