Merge branch 'master' into oldpr373

Signed-off-by: Chris Hennick <4961925+Pr0methean@users.noreply.github.com>
This commit is contained in:
Chris Hennick 2024-07-06 12:29:01 -07:00 committed by GitHub
commit 4dfa32f666
Signed by: DevComp
GPG key ID: B5690EEEBB952194
8329 changed files with 7094 additions and 1532 deletions

3
.gitattributes vendored Normal file
View file

@ -0,0 +1,3 @@
tests/data/** binary=true
tests/data/**/LICENSE.*.txt binary=false
fuzz/corpus/** binary=true

41
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,41 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
<!-- ⚠️©️ Please don't include copyrighted ZIP files, unless we have the owners' permission to publish them at https://github.com/zip-rs/zip2/tree/master/tests/data under the MIT License.
ZIP files in your bug report will be used in tests to ensure that bugs aren't reintroduced once fixed. These tests will break if a copyright complaint forces GitHub to remove a file. -->
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

View file

@ -0,0 +1,23 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
<!-- ⚠️©️ Please don't include copyrighted ZIP files, unless we have the owners' permission to publish them at https://github.com/zip-rs/zip2/tree/master/tests/data under the MIT License.
ZIP files in your feature request will be used in tests to ensure that future changes are compatible with the feature you're requesting. These tests will break if a copyright complaint forces GitHub to remove a file. -->
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View file

@ -1,4 +1,4 @@
name: Auto-merge PRs from owner and trusted bots
name: Auto-merge PRs from trusted bots
on: pull_request
permissions:
@ -8,7 +8,7 @@ permissions:
jobs:
auto-merge:
runs-on: ubuntu-latest
if: ${{ github.actor == 'dependabot[bot]' || github.actor == 'github-actions' || github.actor == 'Pr0methean' }}
if: ${{ github.actor == 'dependabot[bot]' || github.actor == 'github-actions' }}
steps:
- name: Dependabot metadata
id: metadata

View file

@ -6,7 +6,6 @@ on:
- 'master'
push:
branches-ignore:
- 'release-plz-**'
- 'gh-readonly-queue/**'
workflow_dispatch:
merge_group:
@ -22,14 +21,15 @@ jobs:
matrix:
os: [ubuntu-latest, macOS-latest, windows-latest]
rustalias: [stable, nightly, msrv]
feature_flag: ["--all-features", "--no-default-features", ""]
include:
- rustalias: stable
rust: stable
- rustalias: msrv
rust: '1.70'
rust: '1.73'
- rustalias: nightly
rust: nightly
name: 'Build and test: ${{ matrix.os }}, ${{ matrix.rustalias }}'
name: 'Build and test ${{ matrix.feature_flag }}: ${{ matrix.os }}, ${{ matrix.rustalias }}'
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@master
@ -44,47 +44,59 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: check
args: --all --bins --examples
args: --all ${{ matrix.feature_flag }} --bins --examples
- name: Tests
uses: actions-rs/cargo@v1
with:
command: test
args: --all
args: --all ${{ matrix.feature_flag }}
- name: Tests (no features)
uses: actions-rs/cargo@v1
with:
command: test
args: --all --no-default-features
style_and_docs:
cargo_fmt:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true
components: rustfmt, clippy
components: rustfmt
- name: fmt
run: cargo fmt --all -- --check
- name: clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --all-targets --all-features -- -D warnings
- name: Docs
run: cargo doc --no-deps
style_and_docs:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
strategy:
matrix:
feature_flag: ["--all-features", "--no-default-features", ""]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true
components: clippy
- name: clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --all-targets ${{ matrix.feature_flag }} -- -D warnings
- name: docs
uses: actions-rs/cargo@v1
with:
command: doc
args: --no-deps ${{ matrix.feature_flag }}
fuzz_read:
runs-on: ubuntu-latest
needs:
- build_and_test
- cargo_fmt
- style_and_docs
steps:
- uses: actions/checkout@v4
@ -103,11 +115,11 @@ jobs:
command: fuzz
args: build --all-features fuzz_read
- name: run fuzz
timeout-minutes: 331
timeout-minutes: 350
uses: actions-rs/cargo@v1
with:
command: fuzz
args: run --all-features fuzz_read -- fuzz/corpus/seed -timeout=10s -fork=2 -runs=25000000 -max_len=1300 -len_control=0 -dict=fuzz/fuzz.dict
args: run --all-features fuzz_read -- fuzz/corpus/fuzz_read -timeout=10s -rss_limit_mb=8192 -fork=2 -runs=25000000 -max_len=70000 -max_total_time=20700 -dict=fuzz/fuzz.dict
- name: Upload any failure inputs
if: always()
uses: actions/upload-artifact@v4
@ -117,21 +129,20 @@ jobs:
if-no-files-found: ignore
- name: Minimize seed corpus
if: always()
uses: actions-rs/cargo@v1
with:
command: fuzz
args: cmin --all-features fuzz_read fuzz/corpus/seed -- fuzz/corpus/new_seed
run: ./recursive-fuzz-cmin.sh read 70000
shell: bash
- name: Upload updated seed corpus
if: always()
uses: actions/upload-artifact@v4
with:
name: fuzz_read_corpus
path: fuzz/corpus/new_seed/*
path: fuzz/corpus/fuzz_read/*
fuzz_read_with_no_features:
runs-on: ubuntu-latest
needs:
- build_and_test
- cargo_fmt
- style_and_docs
steps:
- uses: actions/checkout@v4
@ -150,11 +161,11 @@ jobs:
command: fuzz
args: build --no-default-features fuzz_read
- name: run fuzz
timeout-minutes: 331
timeout-minutes: 350
uses: actions-rs/cargo@v1
with:
command: fuzz
args: run --no-default-features fuzz_read -- fuzz/corpus/seed -timeout=10s -fork=2 -runs=40000000 -max_total_time=19800 -max_len=16384 -len_control=0 -dict=fuzz/fuzz.dict
args: run --no-default-features fuzz_read fuzz/corpus/fuzz_read -- -rss_limit_mb=8192 -timeout=10s -fork=2 -runs=40000000 -max_total_time=20700 -max_len=70000 -dict=fuzz/fuzz.dict
- name: Upload any failure inputs
if: always()
uses: actions/upload-artifact@v4
@ -162,23 +173,12 @@ jobs:
name: fuzz_read_no_features_bad_inputs
path: fuzz/artifacts/fuzz_read/crash-*
if-no-files-found: ignore
- name: Minimize seed corpus
if: always()
uses: actions-rs/cargo@v1
with:
command: fuzz
args: cmin --no-default-features fuzz_read fuzz/corpus/seed -- fuzz/corpus/new_seed
- name: Upload updated seed corpus
if: always()
uses: actions/upload-artifact@v4
with:
name: fuzz_read_no_features_corpus
path: fuzz/corpus/new_seed/*
fuzz_write:
runs-on: ubuntu-latest
needs:
- build_and_test
- cargo_fmt
- style_and_docs
steps:
- uses: actions/checkout@v4
@ -197,35 +197,37 @@ jobs:
command: fuzz
args: build --all-features fuzz_write
- name: run fuzz
timeout-minutes: 331
timeout-minutes: 350
uses: actions-rs/cargo@v1
with:
command: fuzz
args: run --all-features fuzz_write -- -timeout=10s -fork=2 -runs=2500000 -max_len=1100 -len_control=200 -dict=fuzz/fuzz.dict
args: run --all-features fuzz_write fuzz/corpus/fuzz_write -- -rss_limit_mb=8192 -timeout=2s -fork=2 -runs=5000000 -max_len=500 -max_total_time=20700 -dict=fuzz/fuzz.dict
- name: Upload any failure inputs
if: always()
uses: actions/upload-artifact@v4
with:
name: fuzz_write_bad_inputs
path: fuzz/artifacts/fuzz_write/crash-*
path: |
fuzz/artifacts/fuzz_write/crash-*
fuzz/artifacts/fuzz_write/leak-*
fuzz/artifacts/fuzz_write/timeout-*
if-no-files-found: ignore
- name: Minimize seed corpus
if: always()
uses: actions-rs/cargo@v1
with:
command: fuzz
args: cmin --all-features fuzz_write fuzz/corpus/fuzz_write -- fuzz/corpus/new_seed
run: ./recursive-fuzz-cmin.sh write 500
shell: bash
- name: Upload updated seed corpus
if: always()
uses: actions/upload-artifact@v4
with:
name: fuzz_write_corpus
path: fuzz/corpus/new_seed/*
path: fuzz/corpus/fuzz_write/*
fuzz_write_with_no_features:
runs-on: ubuntu-latest
needs:
- build_and_test
- cargo_fmt
- style_and_docs
steps:
- uses: actions/checkout@v4
@ -244,11 +246,11 @@ jobs:
command: fuzz
args: build --no-default-features fuzz_write
- name: run fuzz
timeout-minutes: 331
timeout-minutes: 350
uses: actions-rs/cargo@v1
with:
command: fuzz
args: run --no-default-features fuzz_write -- -timeout=10s -fork=2 -runs=20000000 -max_len=10000 -len_control=200 -dict=fuzz/fuzz.dict
args: run --no-default-features fuzz_write fuzz/corpus/fuzz_write -- -rss_limit_mb=8192 -timeout=10s -fork=2 -runs=50000000 -max_len=500 -max_total_time=20700 -len_control=200 -dict=fuzz/fuzz.dict
- name: Upload any failure inputs
if: always()
uses: actions/upload-artifact@v4
@ -256,15 +258,3 @@ jobs:
name: fuzz_write_no_features_bad_inputs
path: fuzz/artifacts/fuzz_write/crash-*
if-no-files-found: ignore
- name: Minimize seed corpus
if: always()
uses: actions-rs/cargo@v1
with:
command: fuzz
args: cmin --no-default-features fuzz_write fuzz/corpus/fuzz_write -- fuzz/corpus/new_seed
- name: Upload updated seed corpus
if: always()
uses: actions/upload-artifact@v4
with:
name: fuzz_write_no_features_corpus
path: fuzz/corpus/new_seed/*

View file

@ -6,6 +6,8 @@ on:
permissions:
pull-requests: write
contents: write
id-token: write
attestations: write
jobs:
release-plz:
name: Release-plz
@ -22,7 +24,12 @@ jobs:
override: true
profile: minimal
- name: Run release-plz
id: release-plz
uses: MarcoIeni/release-plz-action@v0.5
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_PLZ_PAT }}
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
- uses: actions/attest-build-provenance@v1
with:
subject-path: ${{ github.workspace }}/target/release/libzip.rlib
continue-on-error: true

View file

@ -1,5 +1,179 @@
# Changelog
## [2.1.3](https://github.com/zip-rs/zip2/compare/v2.1.2...v2.1.3) - 2024-06-04
### <!-- 1 -->🐛 Bug Fixes
- Some date/time filters were previously unreliable (i.e. later-pass filters had no earliest-pass or latest-fail, and vice-versa)
- Decode Zip-Info UTF8 name and comment fields ([#159](https://github.com/zip-rs/zip2/pull/159))
### <!-- 2 -->🚜 Refactor
- Return extended timestamp fields copied rather than borrowed ([#183](https://github.com/zip-rs/zip2/pull/183))
### <!-- 7 -->⚙️ Miscellaneous Tasks
- Fix a new Clippy warning
- Fix a bug and inline `deserialize` for safety
- Add check for wrong-length blocks, and incorporate fixed-size requirement into the trait name
- Fix a fuzz failure by using checked_sub
- Add feature gate for new unit test
## [2.1.1](https://github.com/zip-rs/zip2/compare/v2.1.0...v2.1.1) - 2024-05-28
### <!-- 1 -->🐛 Bug Fixes
- Derive `Debug` for `ZipWriter`
- lower default version to 4.5 and use the version-needed-to-extract where feasible.
### <!-- 2 -->🚜 Refactor
- use a MIN_VERSION constant
### <!-- 7 -->⚙️ Miscellaneous Tasks
- Bug fixes for debug implementation
- Bug fixes for debug implementation
- Update unit tests
- Remove unused import
## [2.1.0](https://github.com/zip-rs/zip2/compare/v2.0.0...v2.1.0) - 2024-05-25
### <!-- 0 -->🚀 Features
- Support mutual conversion between `DateTime` and MS-DOS pair
### <!-- 1 -->🐛 Bug Fixes
- version-needed-to-extract was incorrect in central header, and version-made-by could be lower than that ([#100](https://github.com/zip-rs/zip2/pull/100))
- version-needed-to-extract was incorrect in central header, and version-made-by could be lower than that ([#100](https://github.com/zip-rs/zip2/pull/100))
### <!-- 7 -->⚙️ Miscellaneous Tasks
- Another tweak to ensure `version_needed` is applied
- Tweaks to make `version_needed` and `version_made_by` work with recently-merged changes
## [2.0.0](https://github.com/zip-rs/zip2/compare/v1.3.1...v2.0.0) - 2024-05-24
### <!-- 0 -->🚀 Features
- Add `fmt::Display` for `DateTime`
- Implement more traits for `DateTime`
### <!-- 2 -->🚜 Refactor
- Change type of `last_modified_time` to `Option<DateTime>`
- [**breaking**] Rename `from_msdos` to `from_msdos_unchecked`, make it unsafe, and add `try_from_msdos` ([#145](https://github.com/zip-rs/zip2/pull/145))
### <!-- 7 -->⚙️ Miscellaneous Tasks
- Continue to accept archives with invalid DateTime, and use `now_utc()` as default only when writing, not reading
## [1.3.1](https://github.com/zip-rs/zip2/compare/v1.3.0...v1.3.1) - 2024-05-21
### <!-- 2 -->🚜 Refactor
- Make `deflate` enable both default implementations
- Merge the hidden deflate-flate2 flag into the public one
- Rename _deflate-non-zopfli to _deflate-flate2
- Reject encrypted and using_data_descriptor files slightly faster in read_zipfile_from_stream
- Convert `impl TryInto<NaiveDateTime> for DateTime` to `impl TryFrom<DateTime> for NaiveDateTime` ([#136](https://github.com/zip-rs/zip2/pull/136))
### <!-- 4 -->⚡ Performance
- Change default compression implementation to `flate2/zlib-ng`
### <!-- 7 -->⚙️ Miscellaneous Tasks
- chore([#132](https://github.com/zip-rs/zip2/pull/132)): Attribution for some copied test data
- chore([#133](https://github.com/zip-rs/zip2/pull/133)): chmod -x src/result.rs
## [1.3.0](https://github.com/zip-rs/zip2/compare/v1.2.3...v1.3.0) - 2024-05-17
### <!-- 0 -->🚀 Features
- Add `is_symlink` method
### <!-- 1 -->🐛 Bug Fixes
- Extract symlinks into symlinks on Unix and Windows, and fix a bug that affected making directories writable on MacOS
### <!-- 2 -->🚜 Refactor
- Eliminate deprecation warning when `--all-features` implicitly enables the deprecated feature
- Check if archive contains a symlink's target, without borrowing both at the same time
- Eliminate a clone that's no longer necessary
- is_dir only needs to look at the filename
- Remove unnecessary #[cfg] attributes
### <!-- 7 -->⚙️ Miscellaneous Tasks
- Fix borrow-of-moved-value
- Box<str> doesn't directly convert to PathBuf, so convert back to String first
- partial revert - only &str has chars(), but Box<str> should auto-deref
- contains_key needs a `Box<str>`, so generify `is_dir` to accept one
- Add missing `ZipFileData::is_dir()` method
- Fix another Windows-specific error
- More bug fixes for Windows-specific symlink code
- More bug fixes for Windows-specific symlink code
- Bug fix: variable name change
- Bug fix: need both internal and output path to determine whether to symlink_dir
- Another bug fix
- Fix another error-type conversion error
- Fix error-type conversion on Windows
- Fix conditionally-unused import
- Fix continued issues, and factor out the Vec<u8>-to-OsString conversion (cc: [#125](https://github.com/zip-rs/zip2/pull/125))
- Fix CI failure involving conversion to OsString for symlinks (see my comments on [#125](https://github.com/zip-rs/zip2/pull/125))
- Move path join into platform-independent code
## [1.2.3](https://github.com/zip-rs/zip2/compare/v1.2.2...v1.2.3) - 2024-05-10
### <!-- 1 -->🐛 Bug Fixes
- Remove a window when an extracted directory might be unexpectedly listable and/or `cd`able by non-owners
- Extract directory contents on Unix even if the directory doesn't have write permission (https://github.com/zip-rs/zip-old/issues/423)
### <!-- 7 -->⚙️ Miscellaneous Tasks
- More conditionally-unused imports
## [1.2.2](https://github.com/zip-rs/zip2/compare/v1.2.1...v1.2.2) - 2024-05-09
### <!-- 1 -->🐛 Bug Fixes
- Failed to clear "writing_raw" before finishing a symlink, leading to dropped extra fields
### <!-- 4 -->⚡ Performance
- Use boxed slice for archive comment, since it can't be concatenated
- Optimize for the fact that false signatures can't overlap with real ones
## [1.2.1](https://github.com/zip-rs/zip2/compare/v1.2.0...v1.2.1) - 2024-05-06
### <!-- 1 -->🐛 Bug Fixes
- Prevent panic when trying to read a file with an unsupported compression method
- Prevent panic after reading an invalid LZMA file
- Make `Stored` the default compression method if `Deflated` isn't available, so that zip files are readable by as much software as possible
- version_needed was wrong when e.g. cfg(bzip2) but current file wasn't bzip2 ([#100](https://github.com/zip-rs/zip2/pull/100))
- file paths shouldn't start with slashes ([#102](https://github.com/zip-rs/zip2/pull/102))
### <!-- 2 -->🚜 Refactor
- Overhaul `impl Arbitrary for FileOptions`
- Remove unused `atomic` module
## [1.2.0](https://github.com/zip-rs/zip2/compare/v1.1.4...v1.2.0) - 2024-05-06
### <!-- 0 -->🚀 Features
- Add method `decompressed_size()` so non-recursive ZIP bombs can be detected
### <!-- 2 -->🚜 Refactor
- Make `ZipWriter::finish()` consume the `ZipWriter`
### <!-- 7 -->⚙️ Miscellaneous Tasks
- Use panic! rather than abort to ensure the fuzz harness can process the failure
- Update fuzz_write to use replace_with
- Remove a drop that can no longer be explicit
- Add `#![allow(unexpected_cfgs)]` in nightly
## [1.1.4](https://github.com/zip-rs/zip2/compare/v1.1.3...v1.1.4) - 2024-05-04
### <!-- 1 -->🐛 Bug Fixes
- Build was failing with bzip2 enabled
- use is_dir in more places where Windows paths might be handled incorrectly
### <!-- 4 -->⚡ Performance
- Quick filter for paths that contain "/../" or "/./" or start with "./" or "../"
- Fast handling for separator-free paths
- Speed up logic if main separator isn't '/'
- Drop `normalized_components` slightly sooner when not using it
- Speed up `path_to_string` in cases where the path is already in the proper format
### <!-- 7 -->⚙️ Miscellaneous Tasks
- Refactor: can short-circuit handling of paths that start with MAIN_SEPARATOR, no matter what MAIN_SEPARATOR is
- Bug fix: non-canonical path detection when MAIN_SEPARATOR is not slash or occurs twice in a row
- Bug fix: must recreate if . or .. is a path element
- Bug fix
### <!-- 9 -->◀️ Revert
- [#58](https://github.com/zip-rs/zip2/pull/58) (partial): `bzip2-rs` can't replace `bzip2` because it's decompress-only
## [1.1.3](https://github.com/zip-rs/zip2/compare/v1.1.2...v1.1.3) - 2024-04-30
### <!-- 1 -->🐛 Bug Fixes

View file

@ -1,7 +1 @@
Pull requests are welcome, but they're subject to some requirements:
* They must build against the MSRV, the latest stable Rust version, and the nightly Rust version, both with `--no-default-features` and with `--all-features`.
* They must pass fuzz tests (see the Actions tab).
* Commit messages must conform to [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) and start with
one of the types specified by the [Angular convention](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#type).
* All commits must be signed.
Pull requests are welcome, but they're subject to some requirements that a lot of them don't meet. See https://github.com/zip-rs/zip2/raw/master/pull_request_template.md for details.

View file

@ -1,6 +1,6 @@
[package]
name = "zip"
version = "1.1.3"
version = "2.1.3"
authors = [
"Mathijs van de Nes <git@mathijs.vd-nes.nl>",
"Marli Frost <marli@frost.red>",
@ -9,8 +9,8 @@ authors = [
]
license = "MIT"
repository = "https://github.com/zip-rs/zip2.git"
keywords = ["zip", "archive"]
rust-version = "1.70.0"
keywords = ["zip", "archive", "compression"]
rust-version = "1.73.0"
description = """
Library to support the reading and writing of zip files.
"""
@ -18,64 +18,76 @@ edition = "2021"
exclude = ["tests/**", "examples/**", ".github/**", "fuzz/**"]
build = "src/build.rs"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[workspace.dependencies]
time = { version = "0.3.36", default-features = false }
[dependencies]
aes = { version = "0.8.4", optional = true }
byteorder = "1.5.0"
bzip2 = { version = "0.4.4", optional = true }
chrono = { version = "0.4.38", optional = true }
constant_time_eq = { version = "0.3.0", optional = true }
crc32fast = "1.4.0"
flate2 = { version = "1.0.28", default-features = false, optional = true }
crc32fast = "1.4.2"
displaydoc = { version = "0.2.4", default-features = false }
flate2 = { version = "1.0.30", default-features = false, optional = true }
indexmap = "2"
hmac = { version = "0.12.1", optional = true, features = ["reset"] }
memchr = "2.7.2"
pbkdf2 = { version = "0.12.2", optional = true }
rand = { version = "0.8.5", optional = true }
sha1 = { version = "0.10.6", optional = true }
thiserror = "1.0.61"
time = { workspace = true, optional = true, features = [
"std",
] }
zeroize = { version = "1.8.1", optional = true, features = ["zeroize_derive"] }
zstd = { version = "0.13.1", optional = true, default-features = false }
zopfli = { version = "0.8.0", optional = true }
zopfli = { version = "0.8.1", optional = true }
deflate64 = { version = "0.1.8", optional = true }
lzma-rs = { version = "0.3.0", optional = true }
lzma-rs = { version = "0.3.0", default-features = false, optional = true }
[target.'cfg(any(all(target_arch = "arm", target_pointer_width = "32"), target_arch = "mips", target_arch = "powerpc"))'.dependencies]
crossbeam-utils = "0.8.19"
crossbeam-utils = "0.8.20"
[target.'cfg(fuzzing)'.dependencies]
arbitrary = { version = "1.3.2", features = ["derive"] }
[dev-dependencies]
bencher = "0.1.5"
getrandom = { version = "0.2.14", features = ["js"] }
getrandom = { version = "0.2.15", features = ["js", "std"] }
walkdir = "2.5.0"
time = { workspace = true, features = ["formatting", "macros"] }
anyhow = "1"
clap = { version = "=4.4.18", features = ["derive"] }
tempdir = "0.3.7"
[features]
aes-crypto = ["aes", "constant_time_eq", "hmac", "pbkdf2", "sha1"]
aes-crypto = ["aes", "constant_time_eq", "hmac", "pbkdf2", "sha1", "rand", "zeroize"]
chrono = ["chrono/default"]
_deflate-any = []
deflate = ["flate2/rust_backend", "_deflate-any"]
_all-features = [] # Detect when --all-features is used
deflate = ["flate2/rust_backend", "deflate-zopfli", "deflate-flate2"]
deflate-flate2 = ["flate2/any_impl", "_deflate-any"]
# DEPRECATED: previously enabled `flate2/miniz_oxide` which is equivalent to `flate2/rust_backend`
deflate-miniz = ["deflate", "_deflate-any"]
deflate-zlib = ["flate2/zlib", "_deflate-any"]
deflate-zlib-ng = ["flate2/zlib-ng", "_deflate-any"]
deflate-miniz = ["deflate", "deflate-flate2"]
deflate-zlib = ["flate2/zlib", "deflate-flate2"]
deflate-zlib-ng = ["flate2/zlib-ng", "deflate-flate2"]
deflate-zopfli = ["zopfli", "_deflate-any"]
lzma = ["lzma-rs/stream"]
unreserved = []
xz = ["lzma-rs/raw_decoder"]
default = [
"aes-crypto",
"bzip2",
"deflate",
"deflate64",
"deflate-zlib-ng",
"deflate-zopfli",
"deflate",
"lzma",
"time",
"zstd",
"xz",
]
[[bench]]
@ -85,3 +97,7 @@ harness = false
[[bench]]
name = "read_metadata"
harness = false
[[bench]]
name = "merge_archive"
harness = false

View file

@ -18,4 +18,7 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.
Some files in the "tests/data" subdirectory of this repository are under other
licences; see files named LICENSE.*.txt for details.

View file

@ -32,12 +32,12 @@ Features
The features available are:
* `aes-crypto`: Enables decryption of files which were encrypted with AES. Supports AE-1 and AE-2 methods.
* `deflate`: Enables decompressing the deflate compression algorithm, which is the default for zip files.
* `deflate-zlib`: Enables deflating files with the `zlib` library (used when compression quality is 0..=9).
* `deflate-zlib-ng`: Enables deflating files with the `zlib-ng` library (used when compression quality is 0..=9).
This is the fastest `deflate` implementation available.
* `deflate`: Enables compressing and decompressing an unspecified implementation (that may change in future versions) of
the deflate compression algorithm, which is the default for zip files. Supports compression quality 1..=264.
* `deflate-flate2`: Combine this with any `flate2` feature flag that enables a back-end, to support deflate compression
at quality 1..=9.
* `deflate-zopfli`: Enables deflating files with the `zopfli` library (used when compression quality is 10..=264). This
is the most effective `deflate` implementation available.
is the most effective `deflate` implementation available, but also among the slowest.
* `deflate64`: Enables the deflate64 compression algorithm. Only decompression is supported.
* `lzma`: Enables the LZMA compression algorithm. Only decompression is supported.
* `bzip2`: Enables the BZip2 compression algorithm.
@ -45,7 +45,7 @@ The features available are:
* `chrono`: Enables converting last-modified `zip::DateTime` to and from `chrono::NaiveDateTime`.
* `zstd`: Enables the Zstandard compression algorithm.
By default `aes-crypto`, `deflate`, `deflate-zlib-ng`, `deflate-zopfli`, `bzip2`, `time` and `zstd` are enabled.
By default `aes-crypto`, `bzip2`, `deflate`, `deflate64`, `lzma`, `time` and `zstd` are enabled.
The following feature flags are deprecated:
@ -54,7 +54,7 @@ The following feature flags are deprecated:
MSRV
----
Our current Minimum Supported Rust Version is **1.70**. When adding features,
Our current Minimum Supported Rust Version is **1.73**. When adding features,
we will follow these guidelines:
- We will always support the latest four minor Rust versions. This gives you a 6
@ -70,6 +70,7 @@ See the [examples directory](examples) for:
* How to extract a zip file.
* How to extract a single file from a zip.
* How to read a zip from the standard input.
* How to append a directory to an existing archive
Fuzzing
-------

131
benches/merge_archive.rs Normal file
View file

@ -0,0 +1,131 @@
use bencher::{benchmark_group, benchmark_main};
use std::io::{Cursor, Read, Seek, Write};
use bencher::Bencher;
use getrandom::getrandom;
use zip::{result::ZipResult, write::SimpleFileOptions, ZipArchive, ZipWriter};
fn generate_random_archive(
num_entries: usize,
entry_size: usize,
options: SimpleFileOptions,
) -> ZipResult<(usize, ZipArchive<Cursor<Vec<u8>>>)> {
let buf = Cursor::new(Vec::new());
let mut zip = ZipWriter::new(buf);
let mut bytes = vec![0u8; entry_size];
for i in 0..num_entries {
let name = format!("random{}.dat", i);
zip.start_file(name, options)?;
getrandom(&mut bytes).unwrap();
zip.write_all(&bytes)?;
}
let buf = zip.finish()?.into_inner();
let len = buf.len();
Ok((len, ZipArchive::new(Cursor::new(buf))?))
}
fn perform_merge<R: Read + Seek, W: Write + Seek>(
src: ZipArchive<R>,
mut target: ZipWriter<W>,
) -> ZipResult<ZipWriter<W>> {
target.merge_archive(src)?;
Ok(target)
}
fn perform_raw_copy_file<R: Read + Seek, W: Write + Seek>(
mut src: ZipArchive<R>,
mut target: ZipWriter<W>,
) -> ZipResult<ZipWriter<W>> {
for i in 0..src.len() {
let entry = src.by_index(i)?;
target.raw_copy_file(entry)?;
}
Ok(target)
}
const NUM_ENTRIES: usize = 100;
const ENTRY_SIZE: usize = 1024;
fn merge_archive_stored(bench: &mut Bencher) {
let options = SimpleFileOptions::default().compression_method(zip::CompressionMethod::Stored);
let (len, src) = generate_random_archive(NUM_ENTRIES, ENTRY_SIZE, options).unwrap();
bench.bytes = len as u64;
bench.iter(|| {
let buf = Cursor::new(Vec::new());
let zip = ZipWriter::new(buf);
let zip = perform_merge(src.clone(), zip).unwrap();
let buf = zip.finish().unwrap().into_inner();
assert_eq!(buf.len(), len);
});
}
#[cfg(feature = "_deflate-any")]
fn merge_archive_compressed(bench: &mut Bencher) {
let options = SimpleFileOptions::default().compression_method(zip::CompressionMethod::Deflated);
let (len, src) = generate_random_archive(NUM_ENTRIES, ENTRY_SIZE, options).unwrap();
bench.bytes = len as u64;
bench.iter(|| {
let buf = Cursor::new(Vec::new());
let zip = ZipWriter::new(buf);
let zip = perform_merge(src.clone(), zip).unwrap();
let buf = zip.finish().unwrap().into_inner();
assert_eq!(buf.len(), len);
});
}
fn merge_archive_raw_copy_file_stored(bench: &mut Bencher) {
let options = SimpleFileOptions::default().compression_method(zip::CompressionMethod::Stored);
let (len, src) = generate_random_archive(NUM_ENTRIES, ENTRY_SIZE, options).unwrap();
bench.bytes = len as u64;
bench.iter(|| {
let buf = Cursor::new(Vec::new());
let zip = ZipWriter::new(buf);
let zip = perform_raw_copy_file(src.clone(), zip).unwrap();
let buf = zip.finish().unwrap().into_inner();
assert_eq!(buf.len(), len);
});
}
#[cfg(feature = "_deflate-any")]
fn merge_archive_raw_copy_file_compressed(bench: &mut Bencher) {
let options = SimpleFileOptions::default().compression_method(zip::CompressionMethod::Deflated);
let (len, src) = generate_random_archive(NUM_ENTRIES, ENTRY_SIZE, options).unwrap();
bench.bytes = len as u64;
bench.iter(|| {
let buf = Cursor::new(Vec::new());
let zip = ZipWriter::new(buf);
let zip = perform_raw_copy_file(src.clone(), zip).unwrap();
let buf = zip.finish().unwrap().into_inner();
assert_eq!(buf.len(), len);
});
}
#[cfg(feature = "_deflate-any")]
benchmark_group!(
benches,
merge_archive_stored,
merge_archive_compressed,
merge_archive_raw_copy_file_stored,
merge_archive_raw_copy_file_compressed,
);
#[cfg(not(feature = "_deflate-any"))]
benchmark_group!(
benches,
merge_archive_stored,
merge_archive_raw_copy_file_stored,
);
benchmark_main!(benches);

View file

@ -1,38 +1,126 @@
use bencher::{benchmark_group, benchmark_main};
use std::io::{Cursor, Write};
use std::fs;
use std::io::{self, prelude::*, Cursor};
use bencher::Bencher;
use getrandom::getrandom;
use tempdir::TempDir;
use zip::write::SimpleFileOptions;
use zip::{CompressionMethod, ZipArchive, ZipWriter};
use zip::{result::ZipResult, CompressionMethod, ZipArchive, ZipWriter};
const FILE_COUNT: usize = 15_000;
const FILE_SIZE: usize = 1024;
fn generate_random_archive(count_files: usize, file_size: usize) -> Vec<u8> {
fn generate_random_archive(count_files: usize, file_size: usize) -> ZipResult<Vec<u8>> {
let data = Vec::new();
let mut writer = ZipWriter::new(Cursor::new(data));
let options = SimpleFileOptions::default().compression_method(CompressionMethod::Stored);
let bytes = vec![0u8; file_size];
let mut bytes = vec![0u8; file_size];
for i in 0..count_files {
let name = format!("file_deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef_{i}.dat");
writer.start_file(name, options).unwrap();
writer.write_all(&bytes).unwrap();
writer.start_file(name, options)?;
getrandom(&mut bytes).map_err(io::Error::from)?;
writer.write_all(&bytes)?;
}
writer.finish().unwrap().into_inner()
Ok(writer.finish()?.into_inner())
}
fn read_metadata(bench: &mut Bencher) {
let bytes = generate_random_archive(FILE_COUNT, FILE_SIZE);
let bytes = generate_random_archive(FILE_COUNT, FILE_SIZE).unwrap();
bench.iter(|| {
let archive = ZipArchive::new(Cursor::new(bytes.as_slice())).unwrap();
archive.len()
});
bench.bytes = bytes.len() as u64;
}
benchmark_group!(benches, read_metadata);
const COMMENT_SIZE: usize = 50_000;
fn generate_zip32_archive_with_random_comment(comment_length: usize) -> ZipResult<Vec<u8>> {
let data = Vec::new();
let mut writer = ZipWriter::new(Cursor::new(data));
let options = SimpleFileOptions::default().compression_method(CompressionMethod::Stored);
let mut bytes = vec![0u8; comment_length];
getrandom(&mut bytes).unwrap();
writer.set_raw_comment(bytes.into_boxed_slice());
writer.start_file("asdf.txt", options)?;
writer.write_all(b"asdf")?;
Ok(writer.finish()?.into_inner())
}
fn parse_archive_with_comment(bench: &mut Bencher) {
let bytes = generate_zip32_archive_with_random_comment(COMMENT_SIZE).unwrap();
bench.bench_n(1, |_| {
let archive = ZipArchive::new(Cursor::new(bytes.as_slice())).unwrap();
let _ = archive.comment().len();
});
bench.bytes = bytes.len() as u64;
}
const COMMENT_SIZE_64: usize = 500_000;
fn generate_zip64_archive_with_random_comment(comment_length: usize) -> ZipResult<Vec<u8>> {
let data = Vec::new();
let mut writer = ZipWriter::new(Cursor::new(data));
let options = SimpleFileOptions::default()
.compression_method(CompressionMethod::Stored)
.large_file(true);
let mut bytes = vec![0u8; comment_length];
getrandom(&mut bytes).unwrap();
writer.set_raw_comment(bytes.into_boxed_slice());
writer.start_file("asdf.txt", options)?;
writer.write_all(b"asdf")?;
Ok(writer.finish()?.into_inner())
}
fn parse_zip64_archive_with_comment(bench: &mut Bencher) {
let bytes = generate_zip64_archive_with_random_comment(COMMENT_SIZE_64).unwrap();
bench.iter(|| {
let archive = ZipArchive::new(Cursor::new(bytes.as_slice())).unwrap();
archive.comment().len()
});
bench.bytes = bytes.len() as u64;
}
fn parse_stream_archive(bench: &mut Bencher) {
const STREAM_ZIP_ENTRIES: usize = 5;
const STREAM_FILE_SIZE: usize = 5;
let bytes = generate_random_archive(STREAM_ZIP_ENTRIES, STREAM_FILE_SIZE).unwrap();
/* Write to a temporary file path to incur some filesystem overhead from repeated reads */
let dir = TempDir::new("stream-bench").unwrap();
let out = dir.path().join("bench-out.zip");
fs::write(&out, &bytes).unwrap();
bench.iter(|| {
let mut f = fs::File::open(&out).unwrap();
while zip::read::read_zipfile_from_stream(&mut f)
.unwrap()
.is_some()
{}
});
bench.bytes = bytes.len() as u64;
}
benchmark_group!(
benches,
read_metadata,
parse_archive_with_comment,
parse_zip64_archive_with_comment,
parse_stream_archive,
);
benchmark_main!(benches);

View file

@ -0,0 +1,20 @@
#!/bin/bash
set -euxo pipefail
ncpus=$(nproc || getconf NPROCESSORS_ONLN)
ncpus=$(( ncpus / ( 1 + $(cat /sys/devices/system/cpu/smt/active))))
RESTARTS=10
mv "fuzz/corpus/fuzz_$1" "fuzz/corpus/fuzz_$1_pre_fresh_blood" || true
for i in $(seq 1 $RESTARTS); do
echo "RESTART ${i}"
mkdir "fuzz/corpus/fuzz_$1"
cargo fuzz run --all-features "fuzz_$1" "fuzz/corpus/fuzz_$1" -- \
-dict=fuzz/fuzz.dict -max_len="$2" -fork="$ncpus" \
-max_total_time=5100 -runs=100000000
mv "fuzz/corpus/fuzz_$1" "fuzz/corpus/fuzz_$1_restart_${i}"
done
mkdir "fuzz/corpus/fuzz_$1"
for i in $(seq 1 $RESTARTS); do
mv "fuzz/corpus/fuzz_$1_restart_${i}"/* "fuzz/corpus/fuzz_$1"
rmdir "fuzz/corpus/fuzz_$1_restart_${i}"
done
./fuzz-until-converged.sh $1 $2

63
examples/append.rs Normal file
View file

@ -0,0 +1,63 @@
use std::{
fs::{File, OpenOptions},
path::{Path, PathBuf},
str::FromStr,
};
use zip::write::SimpleFileOptions;
fn gather_files<'a, T: Into<&'a Path>>(path: T, files: &mut Vec<PathBuf>) {
let path: &Path = path.into();
for entry in path.read_dir().unwrap() {
match entry {
Ok(e) => {
if e.path().is_dir() {
gather_files(e.path().as_ref(), files);
} else if e.path().is_file() {
files.push(e.path());
}
}
Err(_) => todo!(),
}
}
}
fn real_main() -> i32 {
let args: Vec<_> = std::env::args().collect();
if args.len() < 3 {
println!("Usage: {} <existing archive> <folder_to_append>", args[0]);
return 1;
}
let existing_archive_path = &*args[1];
let append_dir_path = &*args[2];
let archive = PathBuf::from_str(existing_archive_path).unwrap();
let to_append = PathBuf::from_str(append_dir_path).unwrap();
let existing_zip = OpenOptions::new()
.read(true)
.write(true)
.open(archive)
.unwrap();
let mut append_zip = zip::ZipWriter::new_append(existing_zip).unwrap();
let mut files: Vec<PathBuf> = vec![];
gather_files(to_append.as_ref(), &mut files);
for file in files {
append_zip
.start_file(file.to_string_lossy(), SimpleFileOptions::default())
.unwrap();
let mut f = File::open(file).unwrap();
let _ = std::io::copy(&mut f, &mut append_zip);
}
append_zip.finish().unwrap();
0
}
fn main() {
std::process::exit(real_main());
}

View file

@ -19,7 +19,7 @@ fn real_main() -> i32 {
for i in 0..archive.len() {
let mut file = archive.by_index(i).unwrap();
let outpath = match file.enclosed_name() {
Some(path) => path.to_owned(),
Some(path) => path,
None => continue,
};
@ -30,7 +30,7 @@ fn real_main() -> i32 {
}
}
if (*file.name()).ends_with('/') {
if file.is_dir() {
println!("File {} extracted to \"{}\"", i, outpath.display());
fs::create_dir_all(&outpath).unwrap();
} else {

View file

@ -34,7 +34,7 @@ fn real_main() -> i32 {
}
}
if (*file.name()).ends_with('/') {
if file.is_dir() {
println!(
"Entry {} is a directory with name \"{}\"",
i,

View file

@ -0,0 +1,37 @@
//! Write a huge file with lots of zeros, that should compress perfectly.
fn main() -> Result<(), Box<dyn std::error::Error>> {
if !cfg!(feature = "_deflate-any") {
return Err("Please enable one of the deflate features".into());
}
let args: Vec<_> = std::env::args().collect();
if args.len() < 2 {
return Err(format!("Usage: {} <filename>", args[0]).into());
}
#[cfg(feature = "_deflate-any")]
{
let filename = &*args[1];
use std::io::Write;
use zip::write::SimpleFileOptions;
let file = std::fs::File::create(filename)?;
let mut zip = zip::ZipWriter::new(file);
let options = SimpleFileOptions::default()
.compression_method(zip::CompressionMethod::Deflated)
// files over u32::MAX require this flag set.
.large_file(true)
.unix_permissions(0o755);
zip.start_file("huge-file-of-zeroes", options)?;
let content: Vec<_> = std::iter::repeat(0_u8).take(65 * 1024).collect();
let mut bytes_written = 0_u64;
while bytes_written < u32::MAX as u64 {
zip.write_all(&content)?;
bytes_written += content.len() as u64;
}
zip.finish()?;
}
Ok(())
}

View file

@ -1,52 +1,95 @@
#![allow(unused_variables)]
#![allow(dead_code)]
use anyhow::Context;
use clap::{Parser, ValueEnum};
use std::io::prelude::*;
use zip::{result::ZipError, write::SimpleFileOptions};
use std::fs::File;
use std::path::Path;
use std::path::{Path, PathBuf};
use walkdir::{DirEntry, WalkDir};
#[derive(Parser)]
#[command(about, long_about = None)]
struct Args {
// Source directory
source: PathBuf,
// Destination zipfile
destination: PathBuf,
// Compression method
#[arg(value_enum)]
compression_method: CompressionMethod,
}
#[derive(Clone, ValueEnum)]
enum CompressionMethod {
Stored,
Deflated,
DeflatedZlib,
DeflatedZlibNg,
Bzip2,
Zstd,
}
fn main() {
std::process::exit(real_main());
}
const METHOD_STORED: Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Stored);
#[cfg(feature = "_deflate-any")]
const METHOD_DEFLATED: Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Deflated);
#[cfg(not(feature = "_deflate-any"))]
const METHOD_DEFLATED: Option<zip::CompressionMethod> = None;
#[cfg(feature = "bzip2")]
const METHOD_BZIP2: Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Bzip2);
#[cfg(not(feature = "bzip2"))]
const METHOD_BZIP2: Option<zip::CompressionMethod> = None;
#[cfg(feature = "zstd")]
const METHOD_ZSTD: Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Zstd);
#[cfg(not(feature = "zstd"))]
const METHOD_ZSTD: Option<zip::CompressionMethod> = None;
fn real_main() -> i32 {
let args: Vec<_> = std::env::args().collect();
if args.len() < 3 {
println!(
"Usage: {} <source_directory> <destination_zipfile>",
args[0]
);
return 1;
}
let src_dir = &*args[1];
let dst_file = &*args[2];
for &method in [METHOD_STORED, METHOD_DEFLATED, METHOD_BZIP2, METHOD_ZSTD].iter() {
if method.is_none() {
continue;
let args = Args::parse();
let src_dir = &args.source;
let dst_file = &args.destination;
let method = match args.compression_method {
CompressionMethod::Stored => zip::CompressionMethod::Stored,
CompressionMethod::Deflated => {
#[cfg(not(feature = "deflate-flate2"))]
{
println!("The `deflate-flate2` feature is not enabled");
return 1;
}
#[cfg(feature = "deflate-flate2")]
zip::CompressionMethod::Deflated
}
match doit(src_dir, dst_file, method.unwrap()) {
Ok(_) => println!("done: {src_dir} written to {dst_file}"),
Err(e) => eprintln!("Error: {e:?}"),
CompressionMethod::DeflatedZlib => {
#[cfg(not(feature = "deflate-zlib"))]
{
println!("The `deflate-zlib` feature is not enabled");
return 1;
}
#[cfg(feature = "deflate-zlib")]
zip::CompressionMethod::Deflated
}
CompressionMethod::DeflatedZlibNg => {
#[cfg(not(feature = "deflate-zlib-ng"))]
{
println!("The `deflate-zlib-ng` feature is not enabled");
return 1;
}
#[cfg(feature = "deflate-zlib-ng")]
zip::CompressionMethod::Deflated
}
CompressionMethod::Bzip2 => {
#[cfg(not(feature = "bzip2"))]
{
println!("The `bzip2` feature is not enabled");
return 1;
}
#[cfg(feature = "bzip2")]
zip::CompressionMethod::Bzip2
}
CompressionMethod::Zstd => {
#[cfg(not(feature = "zstd"))]
{
println!("The `zstd` feature is not enabled");
return 1;
}
#[cfg(feature = "zstd")]
zip::CompressionMethod::Zstd
}
};
match doit(src_dir, dst_file, method) {
Ok(_) => println!("done: {:?} written to {:?}", src_dir, dst_file),
Err(e) => eprintln!("Error: {e:?}"),
}
0
@ -54,7 +97,7 @@ fn real_main() -> i32 {
fn zip_dir<T>(
it: &mut dyn Iterator<Item = DirEntry>,
prefix: &str,
prefix: &Path,
writer: T,
method: zip::CompressionMethod,
) -> anyhow::Result<()>
@ -97,7 +140,7 @@ where
Ok(())
}
fn doit(src_dir: &str, dst_file: &str, method: zip::CompressionMethod) -> anyhow::Result<()> {
fn doit(src_dir: &Path, dst_file: &Path, method: zip::CompressionMethod) -> anyhow::Result<()> {
if !Path::new(src_dir).is_dir() {
return Err(ZipError::FileNotFound.into());
}

View file

@ -1,5 +1,7 @@
use std::io::prelude::*;
use zip::write::SimpleFileOptions;
#[cfg(feature = "aes-crypto")]
use zip::{AesMode, CompressionMethod};
fn main() {
std::process::exit(real_main());
@ -38,6 +40,24 @@ fn doit(filename: &str) -> zip::result::ZipResult<()> {
zip.start_file("test/lorem_ipsum.txt", options)?;
zip.write_all(LOREM_IPSUM)?;
#[cfg(feature = "aes-crypto")]
{
zip.start_file(
"test/lorem_ipsum.aes.txt",
options
.compression_method(CompressionMethod::Zstd)
.with_aes_encryption(AesMode::Aes256, "password"),
)?;
zip.write_all(LOREM_IPSUM)?;
// This should use AE-1 due to the short file length.
zip.start_file(
"test/short.aes.txt",
options.with_aes_encryption(AesMode::Aes256, "password"),
)?;
zip.write_all(b"short text\n")?;
}
zip.finish()?;
Ok(())
}

21
fuzz-until-converged.sh Executable file
View file

@ -0,0 +1,21 @@
#!/bin/bash
set -euxo pipefail
rm -r "fuzz/corpus/fuzz_$1_old" || true
ncpus=$(nproc || getconf NPROCESSORS_ONLN)
ncpus=$(( ncpus / ( 1 + $(cat /sys/devices/system/cpu/smt/active))))
MAX_ITERS_WITHOUT_IMPROVEMENT=3
iters_without_improvement=0
while [[ $iters_without_improvement -lt $MAX_ITERS_WITHOUT_IMPROVEMENT ]]; do
cp -r "fuzz/corpus/fuzz_$1" "fuzz/corpus/fuzz_$1_old"
cargo fuzz run --all-features "fuzz_$1" "fuzz/corpus/fuzz_$1" -- \
-dict=fuzz/fuzz.dict -max_len="$2" -fork="$ncpus" \
-max_total_time=1800 -runs=20000000 -rss_limit_mb=8192 -timeout=30
./recursive-fuzz-cmin.sh "$1" "$2"
if diff "fuzz/corpus/fuzz_$1" "fuzz/corpus/fuzz_$1_old"; then
iters_without_improvement=$(( iters_without_improvement + 1 ))
echo "$iters_without_improvement iterations without improvement"
else
iters_without_improvement=0
fi
rm -r "fuzz/corpus/fuzz_$1_old"
done

4
fuzz/.gitignore vendored
View file

@ -1,3 +1,5 @@
target
corpus/fuzz_*
artifacts
corpus/*
!corpus/fuzz_read/
!corpus/fuzz_write/

View file

@ -10,7 +10,9 @@ cargo-fuzz = true
[dependencies]
libfuzzer-sys = "0.4"
arbitrary = { version = "1.3.0", features = ["derive"] }
arbitrary = { version = "1.3.2", features = ["derive"] }
replace_with = "0.1.7"
tikv-jemallocator = "0.5.4"
[dependencies.zip]
path = ".."

Some files were not shown because too many files have changed in this diff Show more