Coverage Report

Created: 2026-11-11 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/zip-9.5.1/src/write/options.rs
Line
Count
Source
1
//! Write Options
2
3
use crate::extra_fields::ExtendedTimestamp;
4
use core::fmt::Debug;
5
use core::marker::PhantomData;
6
use std::sync::Arc;
7
8
use crate::CompressionMethod;
9
use crate::datetime::DateTime;
10
use crate::extra_fields::CustomExtraField;
11
use crate::format::extra_fields::ExtraFieldId;
12
use crate::format::ffi;
13
use crate::format::system::System;
14
use crate::result::{ZipResult, invalid};
15
use crate::zipcrypto::ZipCryptoKeys;
16
17
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
18
pub(crate) enum EncryptWith<'k> {
19
    #[cfg(feature = "aes-crypto")]
20
    Aes {
21
        mode: crate::format::aes::AesMode,
22
        vendor_version: crate::format::aes::AesVendorVersion,
23
        // When the password is None, it means that we are reusing the previous encryption
24
        password: Option<&'k [u8]>,
25
        salt: Option<crate::aes::AesSalt>,
26
    },
27
    ZipCrypto(ZipCryptoKeys, PhantomData<&'k ()>),
28
}
29
30
#[cfg(feature = "_arbitrary")]
31
impl<'a> arbitrary::Arbitrary<'a> for EncryptWith<'a> {
32
    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
33
        #[cfg(feature = "aes-crypto")]
34
        if bool::arbitrary(u)? {
35
            return Ok(EncryptWith::Aes {
36
                mode: crate::format::aes::AesMode::arbitrary(u)?,
37
                password: Some(u.arbitrary::<&[u8]>()?),
38
                vendor_version: crate::format::aes::AesVendorVersion::Ae2,
39
                salt: None, // We don't need to test with random salt. It's only for testing or reproducible zips
40
            });
41
        }
42
43
        Ok(EncryptWith::ZipCrypto(
44
            ZipCryptoKeys::arbitrary(u)?,
45
            PhantomData,
46
        ))
47
    }
48
}
49
50
/// Metadata for a file to be written
51
#[non_exhaustive]
52
#[derive(Clone, Debug, Copy, Eq, PartialEq)]
53
pub struct FileOptions<'k, 'n, T: sealed::FileOptionExtension> {
54
    pub(crate) compression_method: CompressionMethod,
55
    pub(crate) compression_level: Option<i64>,
56
    pub(crate) external_attributes: Option<u32>,
57
    pub(crate) last_modified_time: DateTime,
58
    pub(crate) permissions: Option<u32>,
59
    pub(crate) large_file: bool,
60
    pub(crate) encrypt_with: Option<EncryptWith<'k>>,
61
    pub(crate) extended_options: T,
62
    pub(crate) alignment: u16,
63
    pub(crate) alignment_header_id: u16,
64
    #[cfg(feature = "deflate-zopfli")]
65
    pub(super) zopfli_buffer_size: Option<usize>,
66
    pub(crate) system: Option<System>,
67
    pub(crate) name: Option<&'n [u8]>,
68
}
69
/// Simple File Options. Can be copied and good for simple writing zip files
70
pub type SimpleFileOptions = FileOptions<'static, 'static, ()>;
71
72
impl FileOptions<'static, 'static, ()> {
73
    pub(crate) const DEFAULT_FILE_PERMISSION: u32 = 0o644 | ffi::S_IFREG;
74
    pub(crate) const DEFAULT_DIR_PERMISSIONS: u32 = 0o755 | ffi::S_IFDIR; // rwxr-xr-x default for directories
75
    pub(crate) const DEFAULT_SYMLINK_PERMISSIONS: u32 = 0o777 | ffi::S_IFLNK;
76
}
77
78
pub(crate) mod sealed {
79
    use super::ExtendedFileOptions;
80
    use crate::extra_fields::CustomExtraField;
81
    use std::sync::Arc;
82
83
    pub trait Sealed {}
84
    /// File options Extensions
85
    #[doc(hidden)]
86
    pub trait FileOptionExtension: Default + Sealed {
87
        /// Extra Data
88
        fn extra_fields(&self) -> Option<&Arc<Vec<CustomExtraField>>>;
89
        /// File Comment
90
        fn file_comment(&self) -> Option<&str>;
91
        /// Take File Comment (moves ownership)
92
        fn take_file_comment(&mut self) -> Option<Box<str>>;
93
        /// Whether to check the extra fields with the reader's parser before writing
94
0
        fn validate_extra_fields(&self) -> bool {
95
0
            false
96
0
        }
Unexecuted instantiation: <_ as zip::write::options::sealed::FileOptionExtension>::validate_extra_fields
Unexecuted instantiation: <_ as zip::write::options::sealed::FileOptionExtension>::validate_extra_fields
97
    }
98
    impl Sealed for () {}
99
    impl FileOptionExtension for () {
100
0
        fn extra_fields(&self) -> Option<&Arc<Vec<CustomExtraField>>> {
101
0
            None
102
0
        }
Unexecuted instantiation: <() as zip::write::options::sealed::FileOptionExtension>::extra_fields
Unexecuted instantiation: <() as zip::write::options::sealed::FileOptionExtension>::extra_fields
103
0
        fn file_comment(&self) -> Option<&str> {
104
0
            None
105
0
        }
Unexecuted instantiation: <() as zip::write::options::sealed::FileOptionExtension>::file_comment
Unexecuted instantiation: <() as zip::write::options::sealed::FileOptionExtension>::file_comment
106
0
        fn take_file_comment(&mut self) -> Option<Box<str>> {
107
0
            None
108
0
        }
Unexecuted instantiation: <() as zip::write::options::sealed::FileOptionExtension>::take_file_comment
Unexecuted instantiation: <() as zip::write::options::sealed::FileOptionExtension>::take_file_comment
109
    }
110
    impl Sealed for ExtendedFileOptions {}
111
112
    impl FileOptionExtension for ExtendedFileOptions {
113
0
        fn extra_fields(&self) -> Option<&Arc<Vec<CustomExtraField>>> {
114
0
            Some(&self.extra_fields)
115
0
        }
Unexecuted instantiation: <zip::write::options::ExtendedFileOptions as zip::write::options::sealed::FileOptionExtension>::extra_fields
Unexecuted instantiation: <zip::write::options::ExtendedFileOptions as zip::write::options::sealed::FileOptionExtension>::extra_fields
116
0
        fn file_comment(&self) -> Option<&str> {
117
0
            self.file_comment.as_ref().map(Box::as_ref)
118
0
        }
Unexecuted instantiation: <zip::write::options::ExtendedFileOptions as zip::write::options::sealed::FileOptionExtension>::file_comment
Unexecuted instantiation: <zip::write::options::ExtendedFileOptions as zip::write::options::sealed::FileOptionExtension>::file_comment
119
0
        fn take_file_comment(&mut self) -> Option<Box<str>> {
120
0
            self.file_comment.take()
121
0
        }
Unexecuted instantiation: <zip::write::options::ExtendedFileOptions as zip::write::options::sealed::FileOptionExtension>::take_file_comment
Unexecuted instantiation: <zip::write::options::ExtendedFileOptions as zip::write::options::sealed::FileOptionExtension>::take_file_comment
122
0
        fn validate_extra_fields(&self) -> bool {
123
0
            self.validate_extra_fields
124
0
        }
Unexecuted instantiation: <zip::write::options::ExtendedFileOptions as zip::write::options::sealed::FileOptionExtension>::validate_extra_fields
Unexecuted instantiation: <zip::write::options::ExtendedFileOptions as zip::write::options::sealed::FileOptionExtension>::validate_extra_fields
125
    }
126
}
127
128
/// Adds Extra Data and Central Extra Data. It does not implement copy.
129
pub type FullFileOptions<'k, 'n> = FileOptions<'k, 'n, ExtendedFileOptions>;
130
/// The Extension for Extra Data and Central Extra Data
131
#[cfg_attr(feature = "_arbitrary", derive(arbitrary::Arbitrary))]
132
#[derive(Clone, Default, Eq, PartialEq)]
133
pub struct ExtendedFileOptions {
134
    pub(crate) extra_fields: Arc<Vec<CustomExtraField>>,
135
    pub(crate) file_comment: Option<Box<str>>,
136
    pub(crate) validate_extra_fields: bool,
137
}
138
139
impl ExtendedFileOptions {
140
    /// Adds an extra data field, unless we detect that it's invalid.
141
    ///
142
    /// # Parameters
143
    ///
144
    /// * `header_id` – The 2‑byte identifier of the ZIP extra field to add.
145
    ///   This value determines the type/format of `data` and should either be
146
    ///   one of the standard ZIP extra field IDs defined by the ZIP
147
    ///   specification or an application‑specific (vendor) ID.
148
    /// * `data` – The raw payload for the extra field, without the leading
149
    ///   header ID or length; those are derived from `header_id` and
150
    ///   `data.len()` and written automatically.
151
    /// * `central_only` – Controls where the extra field is stored:
152
    ///   * When `true`, the field is appended only to the central directory
153
    ///     extra data (`central_extra_data`), and the corresponding local file
154
    ///     header is left unchanged.
155
    ///   * When `false`, the field is appended to the local file header extra
156
    ///     data (`extra_data`) and may also be reflected in the central
157
    ///     directory, depending on how the ZIP is written.
158
    ///
159
    /// The combined size of all extra data (local + central) must not exceed
160
    /// `u16::MAX`. If adding this field would exceed that limit or produce an
161
    /// invalid extra data structure, an error is returned and no data is
162
    /// added.
163
    #[deprecated = "use add_extra_field()"]
164
0
    pub fn add_extra_data<D: AsRef<[u8]>>(
165
0
        &mut self,
166
0
        header_id: u16,
167
0
        data: D,
168
0
        central_only: bool,
169
0
    ) -> ZipResult<()> {
170
0
        self.add_extra_field(header_id, data, central_only)
171
0
    }
Unexecuted instantiation: <zip::write::options::ExtendedFileOptions>::add_extra_data::<_>
Unexecuted instantiation: <zip::write::options::ExtendedFileOptions>::add_extra_data::<_>
172
    /// Whether the reader's own extra-field parser accepts this field in every header it will be
173
    /// written to (local and central, or central only).
174
0
    pub(crate) fn extra_field_parses(header_id: u16, data: &[u8], central_only: bool) -> bool {
175
        use crate::extra_fields::{ExtraField, UsedExtraField};
176
        use crate::format::blocks::{Pod, ZipLocalEntryBlock};
177
        use std::io::Cursor;
178
179
0
        let Ok(size) = u16::try_from(data.len()) else {
180
0
            return false;
181
        };
182
0
        let mut serialized = Vec::with_capacity(data.len() + 4);
183
0
        serialized.extend_from_slice(&header_id.to_le_bytes());
184
0
        serialized.extend_from_slice(&size.to_le_bytes());
185
0
        serialized.extend_from_slice(data);
186
0
        let block = ZipLocalEntryBlock::zeroed();
187
0
        if ExtraField::parse(&mut Cursor::new(&serialized), &block, false).is_err() {
188
0
            return false;
189
0
        }
190
        // Only extended timestamps interpret their payload differently in local and central headers.
191
0
        central_only
192
0
            || header_id != UsedExtraField::ExtendedTimestamp.as_u16()
193
0
            || ExtendedTimestamp::try_from_reader(&mut Cursor::new(data), size, true).is_ok()
194
0
    }
Unexecuted instantiation: <zip::write::options::ExtendedFileOptions>::extra_field_parses
Unexecuted instantiation: <zip::write::options::ExtendedFileOptions>::extra_field_parses
195
196
    /// Adds an extra field, unless we detect that it's invalid.
197
    ///
198
    /// # Parameters
199
    ///
200
    /// * `header_id` – The 2‑byte identifier of the ZIP extra field to add.
201
    ///   This value determines the type/format of `data` and should either be
202
    ///   one of the standard ZIP extra field IDs defined by the ZIP
203
    ///   specification or an application‑specific (vendor) ID.
204
    /// * `data` – The raw payload for the extra field, without the leading
205
    ///   header ID or length; those are derived from `header_id` and
206
    ///   `data.len()` and written automatically.
207
    /// * `central_only` – Controls where the extra field is stored:
208
    ///   * When `true`, the field is appended only to the central directory
209
    ///     extra data (`central_extra_data`), and the corresponding local file
210
    ///     header is left unchanged.
211
    ///   * When `false`, the field is appended to the local file header extra
212
    ///     data (`extra_data`) and may also be reflected in the central
213
    ///     directory, depending on how the ZIP is written.
214
    ///
215
    /// The combined size of all extra data (local + central) must not exceed
216
    /// `u16::MAX`; if adding this field would exceed that limit, an error is
217
    /// returned and no data is added.
218
    ///
219
    /// By default `data` is otherwise stored verbatim, even for a header ID
220
    /// that this crate's reader interprets (such as `0x7075`, Info-ZIP Unicode
221
    /// Path) and would reject when the archive is read back. Opt in to
222
    /// checking it with [`FileOptions::validate_extra_fields`].
223
0
    pub fn add_extra_field<D: AsRef<[u8]>>(
224
0
        &mut self,
225
0
        header_id: u16,
226
0
        data: D,
227
0
        central_only: bool,
228
0
    ) -> ZipResult<()> {
229
0
        let data = data.as_ref();
230
0
        let len = data.len() + 4;
231
0
        let extra_fields_len: usize = self.extra_fields.iter().map(|x| x.full_size(false)).sum();
Unexecuted instantiation: <zip::write::options::ExtendedFileOptions>::add_extra_field::<_>::{closure#0}
Unexecuted instantiation: <zip::write::options::ExtendedFileOptions>::add_extra_field::<_>::{closure#0}
232
0
        if extra_fields_len + len > u16::MAX as usize {
233
0
            Err(invalid!("Extra data field would be longer than allowed"))
234
0
        } else if self.validate_extra_fields
235
0
            && !Self::extra_field_parses(header_id, data, central_only)
236
        {
237
            // Otherwise the archive is written, but `ZipArchive` (and `ZipWriter::new_append`)
238
            // reject it, e.g. an Info-ZIP Unicode Path field shorter than its 5-byte header.
239
0
            Err(invalid!(
240
0
                "Extra field {header_id:#06x} would not be readable: malformed data"
241
0
            ))
242
        } else {
243
0
            Arc::make_mut(&mut self.extra_fields).push(CustomExtraField::new(
244
0
                central_only,
245
0
                header_id,
246
0
                data.to_vec().into_boxed_slice(),
247
            ));
248
0
            Ok(())
249
        }
250
0
    }
Unexecuted instantiation: <zip::write::options::ExtendedFileOptions>::add_extra_field::<_>
Unexecuted instantiation: <zip::write::options::ExtendedFileOptions>::add_extra_field::<_>
251
}
252
253
impl Debug for ExtendedFileOptions {
254
0
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
255
0
        f.debug_struct("ExtendedFileOptions")
256
0
            .field("extra_fields", &self.extra_fields)
257
0
            .field("file_comment", &self.file_comment)
258
0
            .field("validate_extra_fields", &self.validate_extra_fields)
259
0
            .finish()
260
0
    }
Unexecuted instantiation: <zip::write::options::ExtendedFileOptions as core::fmt::Debug>::fmt
Unexecuted instantiation: <zip::write::options::ExtendedFileOptions as core::fmt::Debug>::fmt
261
}
262
263
#[cfg(feature = "_arbitrary")]
264
impl<'k, 'n, 'a: 'k + 'n> arbitrary::Arbitrary<'a> for FileOptions<'k, 'n, ExtendedFileOptions> {
265
    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
266
        use crate::extra_fields::EXTRA_FIELD_MAPPING;
267
        use crate::extra_fields::UsedExtraField;
268
        use crate::format::blocks::ZipLocalEntryBlock;
269
        use crate::format::magic::Magic;
270
271
        let mut options = FullFileOptions {
272
            compression_method: CompressionMethod::arbitrary(u)?,
273
            compression_level: if bool::arbitrary(u)? {
274
                Some(u.int_in_range(0..=24)?)
275
            } else {
276
                None
277
            },
278
            last_modified_time: DateTime::arbitrary(u)?,
279
            permissions: Option::<u32>::arbitrary(u)?,
280
            large_file: bool::arbitrary(u)?,
281
            encrypt_with: Option::<EncryptWith<'_>>::arbitrary(u)?,
282
            alignment: u16::arbitrary(u)?,
283
            #[cfg(feature = "deflate-zopfli")]
284
            zopfli_buffer_size: None,
285
            ..Default::default()
286
        };
287
        #[cfg(feature = "deflate-zopfli")]
288
        if options.compression_method == CompressionMethod::Deflated && bool::arbitrary(u)? {
289
            options.zopfli_buffer_size =
290
                Some(if bool::arbitrary(u)? { 2 } else { 3 } << u.int_in_range(8..=20)?);
291
        }
292
        u.arbitrary_loop(Some(0), Some(10), |u| {
293
            let header_id = u.int_in_range(2..=u16::MAX)?;
294
            if UsedExtraField::try_from(header_id).is_err()
295
                && EXTRA_FIELD_MAPPING.contains(&header_id)
296
            {
297
                return Err(arbitrary::Error::IncorrectFormat);
298
            }
299
            let data = Box::<[u8]>::arbitrary(u)?;
300
            options
301
                .add_extra_field(header_id, data, bool::arbitrary(u)?)
302
                .map_err(|_| arbitrary::Error::IncorrectFormat)?;
303
            Ok(core::ops::ControlFlow::Continue(()))
304
        })?;
305
        let entry_len: usize = size_of::<Magic>()
306
            + size_of::<ZipLocalEntryBlock>()
307
            + options
308
                .extended_options
309
                .extra_fields
310
                .iter()
311
                .map(|x| x.full_size(true))
312
                .sum::<usize>();
313
        let Some(max_name_len) = (u16::MAX as usize).checked_sub(entry_len) else {
314
            return Err(arbitrary::Error::IncorrectFormat);
315
        };
316
        let len = u.arbitrary_len::<u8>()?.min(max_name_len);
317
        options.name = Some(u.bytes(len)?);
318
        Ok(options)
319
    }
320
}
321
322
impl<'k, 'n, T: sealed::FileOptionExtension> FileOptions<'k, 'n, T> {
323
0
    pub(crate) fn normalize(&mut self) {
324
0
        if !self.last_modified_time.is_valid() {
325
0
            self.last_modified_time = FileOptions::<T>::default().last_modified_time;
326
0
        }
327
        // The permissions set by the user
328
0
        if let Some(perms) = &mut self.permissions
329
0
            && (*perms & !0o777 == 0)
330
0
        {
331
0
            *perms |= ffi::S_IFREG;
332
0
        }
333
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::normalize
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::normalize
334
335
    /// Set the raw name for the new file. The name need not be UTF-8.
336
    #[must_use]
337
0
    pub fn name<'name>(self, name: &'name [u8]) -> FileOptions<'k, 'name, T> {
338
0
        FileOptions {
339
0
            name: Some(name),
340
0
            ..self
341
0
        }
342
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::name
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::name
343
344
    /// Indicates whether this file will be encrypted (whether with AES or `ZipCrypto`).
345
0
    pub const fn has_encryption(&self) -> bool {
346
0
        self.encrypt_with.is_some()
347
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::has_encryption
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::has_encryption
348
349
    /// Set the compression method for the new file
350
    ///
351
    /// The default is `CompressionMethod::Deflated` if it is enabled. If not,
352
    /// `CompressionMethod::Bzip2` is the default if it is enabled. If neither `bzip2` nor `deflate`
353
    /// is enabled, [`CompressionMethod::Stored`] becomes the default and files are written uncompressed.
354
    #[must_use]
355
0
    pub const fn compression_method(mut self, method: CompressionMethod) -> Self {
356
0
        self.compression_method = method;
357
0
        self
358
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::compression_method
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::compression_method
359
360
    /// Set the `system` field for the new file
361
    ///
362
    /// If not set, the `zip` crate will use the current system
363
    #[must_use]
364
0
    pub const fn system(mut self, system: System) -> Self {
365
0
        self.system = Some(system);
366
0
        self
367
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::system
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::system
368
369
    /// Set the compression level for the new file
370
    ///
371
    /// `None` value specifies default compression level.
372
    ///
373
    /// Range of values depends on compression method:
374
    /// * `Deflated`: 10 - 264 for Zopfli, 1 - 9 for other encoders. Default is 24 if Zopfli is the
375
    ///   only encoder, or 6 otherwise.
376
    /// * `Bzip2`: 1 - 9. Default is 6
377
    /// * `Zstd`: -7 - 22, with zero being mapped to default level. Default is 3
378
    /// * others: only `None` is allowed
379
    #[must_use]
380
0
    pub const fn compression_level(mut self, level: Option<i64>) -> Self {
381
0
        self.compression_level = level;
382
0
        self
383
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::compression_level
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::compression_level
384
385
    /// Set the last modified time
386
    ///
387
    /// The default is the current timestamp if the 'time' feature is enabled, and 1980-01-01
388
    /// otherwise
389
    #[must_use]
390
0
    pub const fn last_modified_time(mut self, mod_time: DateTime) -> Self {
391
0
        self.last_modified_time = mod_time;
392
0
        self
393
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<()>>::last_modified_time
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::last_modified_time
Unexecuted instantiation: <zip::write::options::FileOptions<()>>::last_modified_time
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::last_modified_time
394
395
    /// Set the permissions for the new file.
396
    ///
397
    /// The format is represented with unix-style permissions.
398
    /// The default is `0o644`, which represents `rw-r--r--` for files,
399
    /// and `0o755`, which represents `rwxr-xr-x` for directories.
400
    ///
401
    /// This method only preserves the file permissions bits (via a `& 0o777`) and discards
402
    /// higher file mode bits. So it cannot be used to denote an entry as a directory,
403
    /// symlink, or other special file type.
404
    #[must_use]
405
0
    pub const fn unix_permissions(mut self, mode: u32) -> Self {
406
0
        self.permissions = Some(mode & 0o777);
407
0
        self
408
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::unix_permissions
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::unix_permissions
409
410
    /// Set the external attributes for the file.
411
    ///
412
    /// If you use both [`Self::unix_permissions`] and [`Self::external_attributes`], only external
413
    /// attributes are going to be used
414
    #[must_use]
415
0
    pub const fn external_attributes(mut self, external_perms: u32) -> Self {
416
0
        self.external_attributes = Some(external_perms);
417
0
        self
418
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::external_attributes
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::external_attributes
419
420
    /// Set whether the new file's compressed and uncompressed size is less than 4 GiB.
421
    ///
422
    /// If set to `false` and the file exceeds the limit, an I/O error is thrown and the file is
423
    /// aborted. If set to `true`, readers will require ZIP64 support and if the file does not
424
    /// exceed the limit, 20 B are wasted. The default is `false`.
425
    #[must_use]
426
0
    pub const fn large_file(mut self, large: bool) -> Self {
427
0
        self.large_file = large;
428
0
        self
429
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::large_file
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::large_file
430
431
0
    pub(crate) fn with_deprecated_encryption(self, password: &'k [u8]) -> FileOptions<'k, 'n, T> {
432
0
        FileOptions {
433
0
            encrypt_with: Some(EncryptWith::ZipCrypto(
434
0
                ZipCryptoKeys::derive(password),
435
0
                PhantomData,
436
0
            )),
437
0
            ..self
438
0
        }
439
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::with_deprecated_encryption
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::with_deprecated_encryption
440
441
    /// Set the AES encryption parameters.
442
    /// The `salt` must be at least 8 bytes long for AES-128, and at least 16 bytes long for AES-256.
443
    /// This method is not recommended, since having a fixed salt is not secure.
444
    /// Consider using `with_aes_encryption` instead, which uses a random salt and is more secure.
445
    ///
446
    /// # Security Warning
447
    /// Reusing a fixed salt across multiple files (or cloning a [`FileOptions`] configured with a fixed salt)
448
    /// causes keystream reuse in AES-CTR mode, enabling two-time pad / crib-dragging attacks if plaintexts
449
    /// share common prefixes or predictable headers. Note that because ZIP archives compress each file
450
    /// independently without sharing a dictionary across entries, the pseudorandomness of compressed data
451
    /// cannot be relied upon to prevent keystream correlation attacks across multiple files.
452
    #[cfg(feature = "aes-crypto")]
453
    pub fn with_aes_encryption_and_salt(
454
        self,
455
        password: &'k [u8],
456
        salt: crate::aes::AesSalt,
457
    ) -> FileOptions<'k, 'n, T> {
458
        FileOptions {
459
            encrypt_with: Some(EncryptWith::Aes {
460
                mode: salt.mode(),
461
                password: Some(password),
462
                vendor_version: crate::format::aes::AesVendorVersion::Ae2,
463
                salt: Some(salt),
464
            }),
465
            ..self
466
        }
467
    }
468
469
    /// Set the AES encryption parameters.
470
    #[cfg(feature = "aes-crypto")]
471
    pub fn with_aes_encryption(
472
        self,
473
        mode: crate::AesMode,
474
        password: &'k str,
475
    ) -> FileOptions<'k, 'n, T> {
476
        self.with_aes_encryption_bytes(mode, password.as_bytes())
477
    }
478
479
    /// Set the AES encryption parameters.
480
    #[cfg(feature = "aes-crypto")]
481
    pub fn with_aes_encryption_bytes(
482
        self,
483
        mode: crate::AesMode,
484
        password: &'k [u8],
485
    ) -> FileOptions<'k, 'n, T> {
486
        FileOptions {
487
            encrypt_with: Some(EncryptWith::Aes {
488
                mode,
489
                password: Some(password),
490
                vendor_version: crate::format::aes::AesVendorVersion::Ae2,
491
                salt: None,
492
            }),
493
            ..self
494
        }
495
    }
496
497
    /// Sets the size of the buffer used to hold the next block that Zopfli will compress. The
498
    /// larger the buffer, the more effective the compression, but the more memory is required.
499
    /// A value of `None` indicates no buffer, which is recommended only when all non-empty writes
500
    /// are larger than about 32 KiB.
501
    #[must_use]
502
    #[cfg(feature = "deflate-zopfli")]
503
0
    pub const fn with_zopfli_buffer(mut self, size: Option<usize>) -> Self {
504
0
        self.zopfli_buffer_size = size;
505
0
        self
506
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::with_zopfli_buffer
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::with_zopfli_buffer
507
508
    /// Returns the compression level currently set.
509
0
    pub const fn get_compression_level(&self) -> Option<i64> {
510
0
        self.compression_level
511
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::get_compression_level
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::get_compression_level
512
513
    /// Sets the alignment to the given number of bytes using the default header ID (`0xa11e`).
514
    #[must_use]
515
0
    pub const fn with_alignment(mut self, alignment: u16) -> Self {
516
0
        self.alignment = alignment;
517
0
        self.alignment_header_id = ExtraFieldId::DataStreamAlignment.as_u16();
518
0
        self
519
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::with_alignment
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::with_alignment
520
521
    /// Sets the alignment to the given number of bytes using Android's alignment extra field header ID (`0xd935`).
522
    #[must_use]
523
0
    pub const fn with_android_alignment(mut self, alignment: u16) -> Self {
524
0
        self.alignment = alignment;
525
0
        self.alignment_header_id = ExtraFieldId::AndroidZipAlignment.as_u16();
526
0
        self
527
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::with_android_alignment
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::with_android_alignment
528
529
    /// Sets the alignment to the given number of bytes with a specified extra field header ID.
530
    #[must_use]
531
0
    pub const fn with_alignment_and_header_id(mut self, alignment: u16, header_id: u16) -> Self {
532
0
        self.alignment = alignment;
533
0
        self.alignment_header_id = header_id;
534
0
        self
535
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::with_alignment_and_header_id
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::with_alignment_and_header_id
536
537
    /// Returns the alignment multiple.
538
    #[must_use]
539
0
    pub const fn alignment(&self) -> u16 {
540
0
        self.alignment
541
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::alignment
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::alignment
542
543
    /// Returns the alignment extra field header ID.
544
    #[must_use]
545
0
    pub const fn alignment_header_id(&self) -> u16 {
546
0
        self.alignment_header_id
547
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::alignment_header_id
Unexecuted instantiation: <zip::write::options::FileOptions<_>>::alignment_header_id
548
}
549
impl FileOptions<'_, '_, ExtendedFileOptions> {
550
    /// Check every extra field with this crate's own extra-field parser, and
551
    /// return an error instead of writing a field that [`ZipArchive`] would
552
    /// reject (for example an Info-ZIP Unicode Path field `0x7075` shorter than
553
    /// its 5-byte version and CRC-32 header).
554
    ///
555
    /// Opt-in and off by default, so fields are written verbatim as before:
556
    /// the parser rejects some fields that other software may accept. This
557
    /// is likely to become the default in 10.0.0.
558
    ///
559
    /// When enabled, [`Self::add_extra_field`] rejects a malformed field
560
    /// immediately, and starting an entry (for example with
561
    /// [`ZipWriter::start_file`]) rejects any malformed field added before
562
    /// this was enabled.
563
    ///
564
    /// [`ZipArchive`]: crate::ZipArchive
565
    /// [`ZipWriter::start_file`]: crate::ZipWriter::start_file
566
    #[must_use]
567
0
    pub fn validate_extra_fields(mut self, validate: bool) -> Self {
568
0
        self.extended_options.validate_extra_fields = validate;
569
0
        self
570
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<zip::write::options::ExtendedFileOptions>>::validate_extra_fields
Unexecuted instantiation: <zip::write::options::FileOptions<zip::write::options::ExtendedFileOptions>>::validate_extra_fields
571
572
    /// Set the file comment.
573
    #[must_use]
574
0
    pub fn with_file_comment<S: Into<Box<str>>>(mut self, comment: S) -> Self {
575
0
        self.extended_options.file_comment = Some(comment.into());
576
0
        self
577
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<zip::write::options::ExtendedFileOptions>>::with_file_comment::<_>
Unexecuted instantiation: <zip::write::options::FileOptions<zip::write::options::ExtendedFileOptions>>::with_file_comment::<_>
578
579
    /// Adds an extra data field.
580
    #[deprecated = "use add_extra_field()"]
581
0
    pub fn add_extra_data<D: AsRef<[u8]>>(
582
0
        &mut self,
583
0
        header_id: u16,
584
0
        data: D,
585
0
        central_only: bool,
586
0
    ) -> ZipResult<()> {
587
0
        self.add_extra_field(header_id, data, central_only)
588
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<zip::write::options::ExtendedFileOptions>>::add_extra_data::<_>
Unexecuted instantiation: <zip::write::options::FileOptions<zip::write::options::ExtendedFileOptions>>::add_extra_data::<_>
589
590
    /// Adds an extra field; see [`ExtendedFileOptions::add_extra_field`] for
591
    /// what is checked.
592
0
    pub fn add_extra_field<D: AsRef<[u8]>>(
593
0
        &mut self,
594
0
        header_id: u16,
595
0
        data: D,
596
0
        central_only: bool,
597
0
    ) -> ZipResult<()> {
598
0
        self.extended_options
599
0
            .add_extra_field(header_id, data, central_only)
600
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<zip::write::options::ExtendedFileOptions>>::add_extra_field::<_>
Unexecuted instantiation: <zip::write::options::FileOptions<zip::write::options::ExtendedFileOptions>>::add_extra_field::<_>
601
602
    /// Removes the extra fields.
603
    #[must_use]
604
    #[deprecated = "use clear_extra_fields"]
605
0
    pub fn clear_extra_data(self) -> Self {
606
0
        self.clear_extra_fields()
607
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<zip::write::options::ExtendedFileOptions>>::clear_extra_data
Unexecuted instantiation: <zip::write::options::FileOptions<zip::write::options::ExtendedFileOptions>>::clear_extra_data
608
609
    /// Removes the extra fields.
610
    #[must_use]
611
0
    pub fn clear_extra_fields(mut self) -> Self {
612
0
        if !self.extended_options.extra_fields.is_empty() {
613
0
            self.extended_options.extra_fields = Arc::new(vec![]);
614
0
        }
615
0
        self
616
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<zip::write::options::ExtendedFileOptions>>::clear_extra_fields
Unexecuted instantiation: <zip::write::options::FileOptions<zip::write::options::ExtendedFileOptions>>::clear_extra_fields
617
}
618
impl FileOptions<'static, 'static, ()> {
619
    /// Constructs a const `FileOptions` object.
620
    ///
621
    /// Note: This value is different than the return value of [`FileOptions::default()`]:
622
    ///
623
    /// - The `last_modified_time` is [`DateTime::DEFAULT`]. This corresponds to 1980-01-01 00:00:00
624
    pub const DEFAULT: Self = Self {
625
        compression_method: CompressionMethod::DEFAULT,
626
        compression_level: None,
627
        last_modified_time: DateTime::DEFAULT,
628
        large_file: false,
629
        permissions: None,
630
        encrypt_with: None,
631
        extended_options: (),
632
        alignment: 1,
633
        alignment_header_id: ExtraFieldId::DataStreamAlignment.as_u16(),
634
        #[cfg(feature = "deflate-zopfli")]
635
        zopfli_buffer_size: Some(1 << 15),
636
        system: None,
637
        name: None,
638
        external_attributes: None,
639
    };
640
}
641
642
impl<'k, 'n> FileOptions<'k, 'n, ()> {
643
    /// Convert to `FullFileOptions`.
644
    #[must_use]
645
0
    pub fn into_full_options(self) -> FullFileOptions<'k, 'n> {
646
0
        FileOptions {
647
0
            compression_method: self.compression_method,
648
0
            compression_level: self.compression_level,
649
0
            last_modified_time: self.last_modified_time,
650
0
            permissions: self.permissions,
651
0
            large_file: self.large_file,
652
0
            encrypt_with: self.encrypt_with,
653
0
            extended_options: ExtendedFileOptions::default(),
654
0
            alignment: self.alignment,
655
0
            alignment_header_id: self.alignment_header_id,
656
0
            #[cfg(feature = "deflate-zopfli")]
657
0
            zopfli_buffer_size: self.zopfli_buffer_size,
658
0
            system: self.system,
659
0
            name: self.name,
660
0
            external_attributes: self.external_attributes,
661
0
        }
662
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<()>>::into_full_options
Unexecuted instantiation: <zip::write::options::FileOptions<()>>::into_full_options
663
}
664
665
impl<T: sealed::FileOptionExtension> Default for FileOptions<'_, '_, T> {
666
    /// Construct a new `FileOptions` object
667
0
    fn default() -> Self {
668
0
        Self {
669
0
            compression_method: CompressionMethod::default(),
670
0
            compression_level: None,
671
0
            last_modified_time: DateTime::default_for_write(),
672
0
            permissions: None,
673
0
            large_file: false,
674
0
            encrypt_with: None,
675
0
            extended_options: T::default(),
676
0
            alignment: 1,
677
0
            alignment_header_id: ExtraFieldId::DataStreamAlignment.as_u16(),
678
0
            #[cfg(feature = "deflate-zopfli")]
679
0
            zopfli_buffer_size: Some(1 << 15),
680
0
            system: None,
681
0
            name: None,
682
0
            external_attributes: None,
683
0
        }
684
0
    }
Unexecuted instantiation: <zip::write::options::FileOptions<()> as core::default::Default>::default
Unexecuted instantiation: <zip::write::options::FileOptions<_> as core::default::Default>::default
Unexecuted instantiation: <zip::write::options::FileOptions<()> as core::default::Default>::default
Unexecuted instantiation: <zip::write::options::FileOptions<_> as core::default::Default>::default
685
}