зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1772965 - Update geckodriver to zip 0.6. r=webdriver-reviewers,whimboo
Differential Revision: https://phabricator.services.mozilla.com/D148465
This commit is contained in:
Родитель
57c2d48e4f
Коммит
f2afd39ca6
|
@ -3455,16 +3455,6 @@ dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "msdos_time"
|
|
||||||
version = "0.1.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "aad9dfe950c057b1bfe9c1f2aa51583a8468ef2a5baba2ebbe06d775efeb7729"
|
|
||||||
dependencies = [
|
|
||||||
"time 0.1.43",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "murmurhash3"
|
name = "murmurhash3"
|
||||||
version = "0.0.5"
|
version = "0.0.5"
|
||||||
|
@ -4046,12 +4036,6 @@ dependencies = [
|
||||||
"xml-rs",
|
"xml-rs",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "podio"
|
|
||||||
version = "0.1.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b18befed8bc2b61abc79a457295e7e838417326da1586050b919414073977f19"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
|
@ -6125,12 +6109,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zip"
|
name = "zip"
|
||||||
version = "0.4.2"
|
version = "0.6.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "36b9e08fb518a65cf7e08a1e482573eb87a2f4f8c6619316612a3c1f162fe822"
|
checksum = "bf225bcf73bb52cbb496e70475c7bd7a3f769df699c0020f6c7bd9a96dcf0b8d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"crc32fast",
|
||||||
|
"crossbeam-utils 0.8.8",
|
||||||
"flate2",
|
"flate2",
|
||||||
"msdos_time",
|
|
||||||
"podio",
|
|
||||||
"time 0.1.43",
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -30,7 +30,7 @@ tempfile = "3"
|
||||||
url = "2.0"
|
url = "2.0"
|
||||||
uuid = { version = "0.8", features = ["v4"] }
|
uuid = { version = "0.8", features = ["v4"] }
|
||||||
webdriver = { path = "../webdriver", version="0.45.0" }
|
webdriver = { path = "../webdriver", version="0.45.0" }
|
||||||
zip = { version = "0.4", default-features = false, features = ["deflate"] }
|
zip = { version = "0.6", default-features = false, features = ["deflate"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
{"files":{"Cargo.toml":"3faa730a95b1a21d63ca11b375e9e86793565a573db649df01562bb24a0b1b43","LICENSE-APACHE":"c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08","LICENSE-MIT":"0241727cb9f7e4ab1ad206ff89ec1dc31034a69b146f076237e8c1b1534b6fe0","README.md":"11cdfba96992fcee089bdcad6682ba5357d8161304d35618ee76abad298ffba7","appveyor.yml":"266ca65d48f02c6a3ce1ba1c0772ef9afece737da03b3176e000b7c32a9ea748","script/doc-upload.cfg":"8cae598ef8592842fa8319b18d515e7a322296490cbdf909b29f5e042a95419e","src/lib.rs":"282e6beaef56932a3737e1b60e71ddfdf620d35a9cafdfaa1cc59b54f7d43009"},"package":"aad9dfe950c057b1bfe9c1f2aa51583a8468ef2a5baba2ebbe06d775efeb7729"}
|
|
|
@ -1,28 +0,0 @@
|
||||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
|
||||||
#
|
|
||||||
# When uploading crates to the registry Cargo will automatically
|
|
||||||
# "normalize" Cargo.toml files for maximal compatibility
|
|
||||||
# with all versions of Cargo and also rewrite `path` dependencies
|
|
||||||
# to registry (e.g. crates.io) dependencies
|
|
||||||
#
|
|
||||||
# If you believe there's an error in this file please file an
|
|
||||||
# issue against the rust-lang/cargo repository. If you're
|
|
||||||
# editing this file be aware that the upstream Cargo.toml
|
|
||||||
# will likely look very different (and much more reasonable)
|
|
||||||
|
|
||||||
[package]
|
|
||||||
name = "msdos_time"
|
|
||||||
version = "0.1.6"
|
|
||||||
authors = ["Mathijs van de Nes <git@mathijs.vd-nes.nl>"]
|
|
||||||
description = "Converts an MsDosDateTime (FAT time) to a Tm value\n"
|
|
||||||
documentation = "https://mvdnes.github.io/rust-docs/msdos_time/msdos_time/"
|
|
||||||
license = "MIT OR Apache-2.0"
|
|
||||||
repository = "https://github.com/mvdnes/msdos_time"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "msdos_time"
|
|
||||||
[dependencies.time]
|
|
||||||
version = "0.1"
|
|
||||||
[target."cfg(windows)".dependencies.winapi]
|
|
||||||
version = "0.3"
|
|
||||||
features = ["winbase", "timezoneapi"]
|
|
|
@ -1,202 +0,0 @@
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright {yyyy} {name of copyright owner}
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
Copyright (c) 2015 The msdos_time Developers
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
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.
|
|
|
@ -1,25 +0,0 @@
|
||||||
# msdos_time
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/mvdnes/msdos_time.svg?branch=master)](https://travis-ci.org/mvdnes/msdos_time)
|
|
||||||
[![Build status](https://ci.appveyor.com/api/projects/status/tf4jgk6wr66n17xa/branch/master?svg=true)](https://ci.appveyor.com/project/mvdnes/msdos-time/branch/master)
|
|
||||||
[![Crates.io](https://img.shields.io/crates/v/msdos_time.svg)](https://crates.io/crates/msdos_time)
|
|
||||||
|
|
||||||
[Documentation](https://mvdnes.github.io/rust-docs/msdos_time/msdos_time/)
|
|
||||||
|
|
||||||
Library to convert Fat time to a Tm struct in Rust.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Licensed under either of
|
|
||||||
|
|
||||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
|
||||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
at your option.
|
|
||||||
|
|
||||||
### Contribution
|
|
||||||
|
|
||||||
Unless you explicitly state otherwise, any contribution intentionally
|
|
||||||
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
|
||||||
license, shall be dual licensed as above, without any additional terms or
|
|
||||||
conditions.
|
|
|
@ -1,39 +0,0 @@
|
||||||
environment:
|
|
||||||
matrix:
|
|
||||||
- RUST_INSTALL_TRIPLE: i686-pc-windows-gnu
|
|
||||||
RUST_CHANNEL: stable
|
|
||||||
- RUST_INSTALL_TRIPLE: x86_64-pc-windows-gnu
|
|
||||||
RUST_CHANNEL: stable
|
|
||||||
- RUST_INSTALL_TRIPLE: x86_64-pc-windows-msvc
|
|
||||||
RUST_CHANNEL: stable
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
allow_failures:
|
|
||||||
- RUST_CHANNEL: nightly
|
|
||||||
|
|
||||||
install:
|
|
||||||
- ps: Start-FileDownload "http://static.rust-lang.org/dist/channel-rust-${env:RUST_CHANNEL}"
|
|
||||||
- ps: $env:RUST_INSTALLER = Select-String "${env:RUST_INSTALL_TRIPLE}.*exe" "channel-rust-${env:RUST_CHANNEL}" | select -exp line
|
|
||||||
- ps: Start-FileDownload "http://static.rust-lang.org/dist/${env:RUST_INSTALLER}"
|
|
||||||
- '%RUST_INSTALLER% /VERYSILENT /NORESTART /DIR="C:\Rust"'
|
|
||||||
- call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" amd64
|
|
||||||
- ps: $env:path = "C:\Rust\bin;" + $env:path
|
|
||||||
- ps: |
|
|
||||||
if ($env:RUST_INSTALL_TRIPLE -like "x86_64-*-gnu") {
|
|
||||||
Start-FileDownload "http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/5.1.0/threads-win32/seh/x86_64-5.1.0-release-win32-seh-rt_v4-rev0.7z/download" -FileName mingw64.7z
|
|
||||||
7z x -oC:\ mingw64.7z > $null
|
|
||||||
$env:path = "C:\mingw64\bin;" + $env:path
|
|
||||||
gcc --version
|
|
||||||
}
|
|
||||||
elseif ($env:RUST_INSTALL_TRIPLE -like "i686-*-gnu") {
|
|
||||||
$env:path = "C:\mingw\bin;" + $env:path
|
|
||||||
gcc --version
|
|
||||||
}
|
|
||||||
- rustc --version --verbose
|
|
||||||
- cargo --version
|
|
||||||
|
|
||||||
build: false
|
|
||||||
|
|
||||||
test_script:
|
|
||||||
- cargo build
|
|
||||||
- cargo test
|
|
|
@ -1,3 +0,0 @@
|
||||||
PROJECT_NAME=msdos_time
|
|
||||||
DOCS_REPO=mvdnes/rust-docs.git
|
|
||||||
DOC_RUST_VERSION=stable
|
|
|
@ -1,225 +0,0 @@
|
||||||
#![warn(missing_docs)]
|
|
||||||
|
|
||||||
//! This crate converts a `Tm` struct to an `MsDosDateTime` and vice-versa
|
|
||||||
//!
|
|
||||||
//! MsDosDateTime is based on a FAT datetime and is a compact representation of a date.
|
|
||||||
//! It is currently mostly used in zip files.
|
|
||||||
|
|
||||||
extern crate time;
|
|
||||||
#[cfg(windows)] extern crate winapi;
|
|
||||||
|
|
||||||
use std::io;
|
|
||||||
use time::Tm;
|
|
||||||
|
|
||||||
/// Struct representing the date and time part of an MsDos datetime
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
pub struct MsDosDateTime {
|
|
||||||
/// Part representing the date
|
|
||||||
pub datepart: u16,
|
|
||||||
/// Part representing the time
|
|
||||||
pub timepart: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MsDosDateTime {
|
|
||||||
/// Constructor of an MsDos datetime, from the raw representation
|
|
||||||
pub fn new(time: u16, date: u16) -> MsDosDateTime {
|
|
||||||
MsDosDateTime {
|
|
||||||
datepart: date,
|
|
||||||
timepart: time,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Trait to convert a time representation to and from a MsDosDateTime
|
|
||||||
pub trait TmMsDosExt : Sized {
|
|
||||||
/// Convert a value to MsDosDateTime
|
|
||||||
fn to_msdos(&self) -> Result<MsDosDateTime, io::Error>;
|
|
||||||
/// Construct a value from an MsDosDateTime
|
|
||||||
fn from_msdos(MsDosDateTime) -> Result<Self, io::Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TmMsDosExt for Tm {
|
|
||||||
fn to_msdos(&self) -> Result<MsDosDateTime, io::Error> {
|
|
||||||
sys::tm_to_msdos(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_msdos(ms: MsDosDateTime) -> Result<Self, io::Error> {
|
|
||||||
sys::msdos_to_tm(ms)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
mod sys {
|
|
||||||
use super::MsDosDateTime;
|
|
||||||
use time::{self, Tm};
|
|
||||||
use std::io;
|
|
||||||
|
|
||||||
pub fn msdos_to_tm(ms: MsDosDateTime) -> Result<Tm, io::Error> {
|
|
||||||
let seconds = (ms.timepart & 0b0000000000011111) << 1;
|
|
||||||
let minutes = (ms.timepart & 0b0000011111100000) >> 5;
|
|
||||||
let hours = (ms.timepart & 0b1111100000000000) >> 11;
|
|
||||||
let days = (ms.datepart & 0b0000000000011111) >> 0;
|
|
||||||
let months = (ms.datepart & 0b0000000111100000) >> 5;
|
|
||||||
let years = (ms.datepart & 0b1111111000000000) >> 9;
|
|
||||||
|
|
||||||
// Month range: Dos [1..12], Tm [0..11]
|
|
||||||
// Year zero: Dos 1980, Tm 1900
|
|
||||||
|
|
||||||
let tm = Tm {
|
|
||||||
tm_sec: seconds as i32,
|
|
||||||
tm_min: minutes as i32,
|
|
||||||
tm_hour: hours as i32,
|
|
||||||
tm_mday: days as i32,
|
|
||||||
tm_mon: months as i32 - 1,
|
|
||||||
tm_year: years as i32 + 80,
|
|
||||||
..time::empty_tm()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Re-parse the possibly incorrect timestamp to get a correct one.
|
|
||||||
// This ensures every value will be in range
|
|
||||||
// TODO: Check if this will only panic on Windows, or also other platforms
|
|
||||||
Ok(time::at_utc(tm.to_timespec()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tm_to_msdos(tm: &Tm) -> Result<MsDosDateTime, io::Error> {
|
|
||||||
let timepart = ((tm.tm_sec >> 1) | (tm.tm_min << 5) | (tm.tm_hour << 11)) as u16;
|
|
||||||
let datepart = (tm.tm_mday | ((tm.tm_mon + 1) << 5) | ((tm.tm_year - 80) << 9)) as u16;
|
|
||||||
Ok(MsDosDateTime { datepart: datepart, timepart: timepart })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
mod sys {
|
|
||||||
use super::MsDosDateTime;
|
|
||||||
use time::{self, Tm};
|
|
||||||
use winapi::shared::minwindef::{WORD, FILETIME};
|
|
||||||
use winapi::um::minwinbase::SYSTEMTIME;
|
|
||||||
use winapi::um::timezoneapi::{FileTimeToSystemTime, SystemTimeToFileTime};
|
|
||||||
use winapi::um::winbase::{DosDateTimeToFileTime, FileTimeToDosDateTime};
|
|
||||||
use std::io;
|
|
||||||
|
|
||||||
pub fn msdos_to_tm(ms: MsDosDateTime) -> Result<Tm, io::Error> {
|
|
||||||
let datepart: WORD = ms.datepart;
|
|
||||||
let timepart: WORD = ms.timepart;
|
|
||||||
let mut filetime: FILETIME = unsafe { ::std::mem::zeroed() };
|
|
||||||
let mut systemtime: SYSTEMTIME = unsafe { ::std::mem::zeroed() };
|
|
||||||
|
|
||||||
if unsafe { DosDateTimeToFileTime(datepart, timepart, &mut filetime) } == 0 {
|
|
||||||
return Err(io::Error::last_os_error());
|
|
||||||
}
|
|
||||||
|
|
||||||
if unsafe { FileTimeToSystemTime(&filetime, &mut systemtime) } == 0 {
|
|
||||||
return Err(io::Error::last_os_error());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Tm {
|
|
||||||
tm_sec: systemtime.wSecond as i32,
|
|
||||||
tm_min: systemtime.wMinute as i32,
|
|
||||||
tm_hour: systemtime.wHour as i32,
|
|
||||||
tm_mday: systemtime.wDay as i32,
|
|
||||||
tm_wday: systemtime.wDayOfWeek as i32,
|
|
||||||
tm_mon: (systemtime.wMonth - 1) as i32,
|
|
||||||
tm_year: (systemtime.wYear - 1900) as i32,
|
|
||||||
..time::empty_tm()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tm_to_msdos(tm: &Tm) -> Result<MsDosDateTime, io::Error> {
|
|
||||||
let systemtime = SYSTEMTIME {
|
|
||||||
wYear: (tm.tm_year + 1900) as WORD,
|
|
||||||
wMonth: (tm.tm_mon + 1) as WORD,
|
|
||||||
wDayOfWeek: tm.tm_wday as WORD,
|
|
||||||
wDay: tm.tm_mday as WORD,
|
|
||||||
wHour: tm.tm_hour as WORD,
|
|
||||||
wMinute: tm.tm_min as WORD,
|
|
||||||
wSecond: tm.tm_sec as WORD,
|
|
||||||
wMilliseconds: 0,
|
|
||||||
};
|
|
||||||
let mut filetime = unsafe { ::std::mem::zeroed() };
|
|
||||||
let mut datepart : WORD = 0;
|
|
||||||
let mut timepart : WORD = 0;
|
|
||||||
|
|
||||||
if unsafe { SystemTimeToFileTime(&systemtime, &mut filetime) } == 0 {
|
|
||||||
return Err(io::Error::last_os_error());
|
|
||||||
}
|
|
||||||
|
|
||||||
if unsafe { FileTimeToDosDateTime(&filetime, &mut datepart, &mut timepart) } == 0 {
|
|
||||||
return Err(io::Error::last_os_error());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(MsDosDateTime { datepart: datepart, timepart: timepart })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
use time::{self, Tm};
|
|
||||||
|
|
||||||
fn check_date(input: Tm, day: i32, month: i32, year: i32) {
|
|
||||||
assert_eq!(input.tm_mday, day);
|
|
||||||
assert_eq!(input.tm_mon + 1, month);
|
|
||||||
assert_eq!(input.tm_year + 1900, year);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_time(input: Tm, hour: i32, minute: i32, second: i32) {
|
|
||||||
assert_eq!(input.tm_hour, hour);
|
|
||||||
assert_eq!(input.tm_min, minute);
|
|
||||||
assert_eq!(input.tm_sec, second);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn dos_zero() {
|
|
||||||
// The 0 date is not a correct msdos date
|
|
||||||
// We assert here that it does not panic. What it will return is undefined.
|
|
||||||
let _ = Tm::from_msdos(MsDosDateTime::new(0, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn dos_smallest() {
|
|
||||||
// This is the actual smallest date possible
|
|
||||||
let tm = Tm::from_msdos(MsDosDateTime::new(0, 0b100001)).unwrap();
|
|
||||||
check_date(tm, 1, 1, 1980);
|
|
||||||
check_time(tm, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn dos_today() {
|
|
||||||
let tm = Tm::from_msdos(MsDosDateTime::new(0b01001_100000_10101, 0b0100011_0110_11110)).unwrap();
|
|
||||||
check_date(tm, 30, 6, 2015);
|
|
||||||
check_time(tm, 9, 32, 42);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn zero_dos() {
|
|
||||||
let tm = Tm {
|
|
||||||
tm_year: 80,
|
|
||||||
tm_mon: 0,
|
|
||||||
tm_mday: 1,
|
|
||||||
tm_hour: 0,
|
|
||||||
tm_min: 0,
|
|
||||||
tm_sec: 0,
|
|
||||||
..time::empty_tm()
|
|
||||||
};
|
|
||||||
let ms = tm.to_msdos().unwrap();
|
|
||||||
assert_eq!(ms.datepart, 0b100001);
|
|
||||||
assert_eq!(ms.timepart, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn today_dos() {
|
|
||||||
let tm = Tm {
|
|
||||||
tm_year: 115,
|
|
||||||
tm_mon: 5,
|
|
||||||
tm_mday: 30,
|
|
||||||
tm_hour: 9,
|
|
||||||
tm_min: 32,
|
|
||||||
tm_sec: 42,
|
|
||||||
..time::empty_tm()
|
|
||||||
};
|
|
||||||
let ms = tm.to_msdos().unwrap();
|
|
||||||
assert_eq!(ms.datepart, 0b0100011_0110_11110);
|
|
||||||
assert_eq!(ms.timepart, 0b01001_100000_10101);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
{"files":{"Cargo.toml":"864dd81db21e85db5b74242b5f486b75bab2f5b9f666e6ee63e41d02520783f7","LICENSE-APACHE":"c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08","LICENSE-MIT":"892ad0db8a971b86db947bae64542aa67d91cfae768b3ed3ea281c54844a4ae9","README.md":"4b66e3517754d9aabe44c17b224f1d1e72a6fb50507d49536e6825a3e4d0b347","appveyor.yml":"5940accb4ac2c4904459a7a600da6b8dca015653ba273eab7e87894df464d379","benches/benchmark.rs":"0adade342f2046c8d6f669097209f23b4d8a35c6b5dc2c3687addf2f4444aa40","script/doc-upload.cfg":"273e9501d95820583705e9f4c4f84afefba4fd227223fc3d165dfb1aba91c5d1","src/lib.rs":"32efe5d0a248a707c68ca7084d3855c6d918fc12d7f49b1fec56002116ecb735","tests/bytes.rs":"1cb1979745f35086a785df207f3b0f660cf6720e123c3e0042fb924d5795aa37","tests/io.rs":"19e4699082d0a0432dfa23bda2a54ed426d23bf7f07a579ba4305a1926d952ec"},"package":"b18befed8bc2b61abc79a457295e7e838417326da1586050b919414073977f19"}
|
|
|
@ -1,21 +0,0 @@
|
||||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
|
||||||
#
|
|
||||||
# When uploading crates to the registry Cargo will automatically
|
|
||||||
# "normalize" Cargo.toml files for maximal compatibility
|
|
||||||
# with all versions of Cargo and also rewrite `path` dependencies
|
|
||||||
# to registry (e.g., crates.io) dependencies
|
|
||||||
#
|
|
||||||
# If you believe there's an error in this file please file an
|
|
||||||
# issue against the rust-lang/cargo repository. If you're
|
|
||||||
# editing this file be aware that the upstream Cargo.toml
|
|
||||||
# will likely look very different (and much more reasonable)
|
|
||||||
|
|
||||||
[package]
|
|
||||||
name = "podio"
|
|
||||||
version = "0.1.7"
|
|
||||||
authors = ["Mathijs van de Nes <git@mathijs.vd-nes.nl>"]
|
|
||||||
description = "Additional trait for Read and Write to read and write Plain Old Data\n"
|
|
||||||
documentation = "http://mvdnes.github.io/rust-docs/podio/podio/index.html"
|
|
||||||
keywords = ["byte", "byteorder", "io"]
|
|
||||||
license = "MIT OR Apache-2.0"
|
|
||||||
repository = "https://github.com/mvdnes/podio.git"
|
|
|
@ -1,202 +0,0 @@
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright {yyyy} {name of copyright owner}
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
Copyright (c) 2015 The podio Developers
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
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.
|
|
|
@ -1,56 +0,0 @@
|
||||||
podio
|
|
||||||
=====
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/mvdnes/podio.svg?branch=master)](https://travis-ci.org/mvdnes/podio)
|
|
||||||
[![Build status](https://ci.appveyor.com/api/projects/status/hjc3icfjob7mocc9/branch/master?svg=true)](https://ci.appveyor.com/project/mvdnes/podio/branch/master)
|
|
||||||
[![Crates.io version](https://img.shields.io/crates/v/podio.svg)](https://crates.io/crates/podio)
|
|
||||||
|
|
||||||
[Documentation](http://mvdnes.github.io/rust-docs/podio/podio/index.html)
|
|
||||||
|
|
||||||
Implementation for reading and writing POD (plain old data) values in Rust. The name stands for POD I/O.
|
|
||||||
|
|
||||||
Keywords: byte, be, le, big-endian, little-endian
|
|
||||||
|
|
||||||
Usage
|
|
||||||
-----
|
|
||||||
|
|
||||||
Include the following code:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[dependencies]
|
|
||||||
podio = "0.1"
|
|
||||||
```
|
|
||||||
|
|
||||||
Example
|
|
||||||
-------
|
|
||||||
|
|
||||||
```rust
|
|
||||||
extern crate podio;
|
|
||||||
|
|
||||||
use podio::{ReadPodExt, BigEndian};
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let slice: &[u8] = &[0x10, 0x20, 0x30, 0x40];
|
|
||||||
let mut reader = std::io::Cursor::new(slice);
|
|
||||||
|
|
||||||
let value = reader.read_u32::<BigEndian>().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(value, 0x10203040);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Licensed under either of
|
|
||||||
|
|
||||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
|
||||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
at your option.
|
|
||||||
|
|
||||||
### Contribution
|
|
||||||
|
|
||||||
Unless you explicitly state otherwise, any contribution intentionally
|
|
||||||
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
|
||||||
license, shall be dual licensed as above, without any additional terms or
|
|
||||||
conditions.
|
|
|
@ -1,43 +0,0 @@
|
||||||
# Appveyor configuration template for Rust using rustup for Rust installation
|
|
||||||
# https://github.com/starkat99/appveyor-rust
|
|
||||||
|
|
||||||
os: Visual Studio 2015
|
|
||||||
|
|
||||||
environment:
|
|
||||||
matrix:
|
|
||||||
|
|
||||||
# Stable 64-bit MSVC
|
|
||||||
- channel: stable
|
|
||||||
target: x86_64-pc-windows-msvc
|
|
||||||
# Stable 32-bit MSVC
|
|
||||||
- channel: stable
|
|
||||||
target: i686-pc-windows-msvc
|
|
||||||
# Stable 64-bit GNU
|
|
||||||
- channel: stable
|
|
||||||
target: x86_64-pc-windows-gnu
|
|
||||||
# Stable 32-bit GNU
|
|
||||||
- channel: stable
|
|
||||||
target: i686-pc-windows-gnu
|
|
||||||
|
|
||||||
install:
|
|
||||||
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
|
|
||||||
- rustup-init -yv --default-toolchain %channel% --default-host %target%
|
|
||||||
- set PATH=%PATH%;%USERPROFILE%\.cargo\bin
|
|
||||||
- ps: |
|
|
||||||
if ($env:target -like "*-gnu" -And $env:target -like "x86_64-*") {
|
|
||||||
Start-FileDownload "http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/5.1.0/threads-win32/seh/x86_64-5.1.0-release-win32-seh-rt_v4-rev0.7z/download" -FileName mingw64.7z
|
|
||||||
7z x -oC:\ mingw64.7z > $null
|
|
||||||
$env:path = "C:\mingw64\bin;" + $env:path
|
|
||||||
gcc --version
|
|
||||||
}
|
|
||||||
elseif ($env:target -like "*-gnu") {
|
|
||||||
$env:path = "C:\mingw\bin;" + $env:path
|
|
||||||
gcc --version
|
|
||||||
}
|
|
||||||
- rustc -vV
|
|
||||||
- cargo -vV
|
|
||||||
|
|
||||||
build: false
|
|
||||||
|
|
||||||
test_script:
|
|
||||||
- cargo test
|
|
|
@ -1,49 +0,0 @@
|
||||||
// bench.rs
|
|
||||||
#![feature(test)]
|
|
||||||
|
|
||||||
extern crate podio;
|
|
||||||
extern crate test;
|
|
||||||
|
|
||||||
use test::Bencher;
|
|
||||||
use podio::{Endianness, BigEndian, LittleEndian, WritePodExt, ReadPodExt};
|
|
||||||
|
|
||||||
// bench: find the `BENCH_SIZE` first terms of the fibonacci sequence
|
|
||||||
const BENCH_SIZE: usize = 2000;
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn write_u64_be(b: &mut Bencher) {
|
|
||||||
write_u64::<BigEndian>(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn write_u64_le(b: &mut Bencher) {
|
|
||||||
write_u64::<LittleEndian>(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_u64<T: Endianness>(b: &mut Bencher) {
|
|
||||||
b.iter(|| {
|
|
||||||
let mut writer : &mut[u8] = &mut [0; BENCH_SIZE * 8];
|
|
||||||
for _ in 0..BENCH_SIZE {
|
|
||||||
writer.write_u64::<T>(0x012345678u64).unwrap();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn read_u64_be(b: &mut Bencher) {
|
|
||||||
read_u64::<BigEndian>(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn read_u64_le(b: &mut Bencher) {
|
|
||||||
read_u64::<LittleEndian>(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_u64<T: Endianness>(b: &mut Bencher) {
|
|
||||||
b.iter(|| {
|
|
||||||
let mut reader : &[u8] = &[0; BENCH_SIZE * 8];
|
|
||||||
for _ in 0..BENCH_SIZE {
|
|
||||||
reader.read_u64::<T>().unwrap();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
PROJECT_NAME=podio
|
|
||||||
DOCS_REPO=mvdnes/rust-docs.git
|
|
||||||
DOC_RUST_VERSION=stable
|
|
|
@ -1,364 +0,0 @@
|
||||||
//! Additional methods for Read and Write
|
|
||||||
//!
|
|
||||||
//! The additional methods implemented allow reading and writing integers and floats
|
|
||||||
//! in the specified endianness.
|
|
||||||
//!
|
|
||||||
//! # Usage
|
|
||||||
//!
|
|
||||||
//! Basically, you need to `use` the trait WritePodExt or ReadPodExt.
|
|
||||||
//!
|
|
||||||
//! # Examples
|
|
||||||
//!
|
|
||||||
//! ## Reading
|
|
||||||
//!
|
|
||||||
//! To read some value from a reader, import ReadPodExt and the needed endianness.
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! use podio::{ReadPodExt, BigEndian};
|
|
||||||
//!
|
|
||||||
//! let slice: &[u8] = &[0x10, 0x20, 0x30, 0x40];
|
|
||||||
//! let mut reader = std::io::Cursor::new(slice);
|
|
||||||
//!
|
|
||||||
//! let value = reader.read_u32::<BigEndian>().unwrap();
|
|
||||||
//!
|
|
||||||
//! assert_eq!(value, 0x10203040);
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! ## Writing
|
|
||||||
//!
|
|
||||||
//! For writing, you need to import the trait WritePodExt.
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! use podio::{WritePodExt, LittleEndian};
|
|
||||||
//!
|
|
||||||
//! let slice: &mut [u8] = &mut [0; 2];
|
|
||||||
//! let mut writer = std::io::Cursor::new(slice);
|
|
||||||
//!
|
|
||||||
//! writer.write_u16::<LittleEndian>(0x8802).unwrap();
|
|
||||||
//!
|
|
||||||
//! assert_eq!(writer.into_inner(), &[0x02, 0x88]);
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! ## Read exact
|
|
||||||
//!
|
|
||||||
//! One additional method, not really dealing with POD, is `read_exact`.
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! use podio::ReadPodExt;
|
|
||||||
//!
|
|
||||||
//! let slice: &[u8] = &[0, 1, 2, 3];
|
|
||||||
//! let mut reader = std::io::Cursor::new(slice);
|
|
||||||
//!
|
|
||||||
//! assert_eq!(reader.read_exact(1).unwrap(), [0]);
|
|
||||||
//! assert_eq!(reader.read_exact(2).unwrap(), [1,2]);
|
|
||||||
//! assert_eq!(reader.read_exact(0).unwrap(), []);
|
|
||||||
//! assert_eq!(reader.read_exact(1).unwrap(), [3]);
|
|
||||||
//! assert!(reader.read_exact(1).is_err());
|
|
||||||
|
|
||||||
#![warn(missing_docs)]
|
|
||||||
|
|
||||||
use std::io;
|
|
||||||
use std::io::prelude::*;
|
|
||||||
|
|
||||||
/// Little endian. The number `0xABCD` is stored `[0xCD, 0xAB]`
|
|
||||||
pub enum LittleEndian {}
|
|
||||||
/// Big endian. The number `0xABCD` is stored `[0xAB, 0xCD]`
|
|
||||||
pub enum BigEndian {}
|
|
||||||
|
|
||||||
/// Trait implementing conversion methods for a specific endianness
|
|
||||||
pub trait Endianness {
|
|
||||||
/// Converts a value from the platform type to the specified endianness
|
|
||||||
fn int_to_target<T: EndianConvert>(val: T) -> T;
|
|
||||||
/// Converts a value from the sepcified endianness to the platform type
|
|
||||||
fn int_from_target<T: EndianConvert>(val: T) -> T;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generic trait for endian conversions on integers
|
|
||||||
pub trait EndianConvert {
|
|
||||||
/// Convert self to a big-endian value
|
|
||||||
fn to_be(self) -> Self;
|
|
||||||
/// Convert self to a little-endian value
|
|
||||||
fn to_le(self) -> Self;
|
|
||||||
/// Convert a big-endian value to the target endianness
|
|
||||||
fn from_be(x: Self) -> Self;
|
|
||||||
/// Convert a little-endian value to the target endiannes
|
|
||||||
fn from_le(x: Self) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Additional write methods for a io::Write
|
|
||||||
pub trait WritePodExt {
|
|
||||||
/// Write a u64
|
|
||||||
fn write_u64<T: Endianness>(&mut self, u64) -> io::Result<()>;
|
|
||||||
/// Write a u32
|
|
||||||
fn write_u32<T: Endianness>(&mut self, u32) -> io::Result<()>;
|
|
||||||
/// Write a u16
|
|
||||||
fn write_u16<T: Endianness>(&mut self, u16) -> io::Result<()>;
|
|
||||||
/// Write a u8
|
|
||||||
fn write_u8(&mut self, u8) -> io::Result<()>;
|
|
||||||
/// Write a i64
|
|
||||||
fn write_i64<T: Endianness>(&mut self, i64) -> io::Result<()>;
|
|
||||||
/// Write a i32
|
|
||||||
fn write_i32<T: Endianness>(&mut self, i32) -> io::Result<()>;
|
|
||||||
/// Write a i16
|
|
||||||
fn write_i16<T: Endianness>(&mut self, i16) -> io::Result<()>;
|
|
||||||
/// Write a i8
|
|
||||||
fn write_i8(&mut self, i8) -> io::Result<()>;
|
|
||||||
/// Write a f32
|
|
||||||
fn write_f32<T: Endianness>(&mut self, f32) -> io::Result<()>;
|
|
||||||
/// Write a f64
|
|
||||||
fn write_f64<T: Endianness>(&mut self, f64) -> io::Result<()>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Additional read methods for a io::Read
|
|
||||||
pub trait ReadPodExt {
|
|
||||||
/// Read a u64
|
|
||||||
fn read_u64<T: Endianness>(&mut self) -> io::Result<u64>;
|
|
||||||
/// Read a u32
|
|
||||||
fn read_u32<T: Endianness>(&mut self) -> io::Result<u32>;
|
|
||||||
/// Read a u16
|
|
||||||
fn read_u16<T: Endianness>(&mut self) -> io::Result<u16>;
|
|
||||||
/// Read a u8
|
|
||||||
fn read_u8(&mut self) -> io::Result<u8>;
|
|
||||||
/// Read a i64
|
|
||||||
fn read_i64<T: Endianness>(&mut self) -> io::Result<i64>;
|
|
||||||
/// Read a i32
|
|
||||||
fn read_i32<T: Endianness>(&mut self) -> io::Result<i32>;
|
|
||||||
/// Read a i16
|
|
||||||
fn read_i16<T: Endianness>(&mut self) -> io::Result<i16>;
|
|
||||||
/// Read a i8
|
|
||||||
fn read_i8(&mut self) -> io::Result<i8>;
|
|
||||||
/// Read a f32
|
|
||||||
fn read_f32<T: Endianness>(&mut self) -> io::Result<f32>;
|
|
||||||
/// Read a f64
|
|
||||||
fn read_f64<T: Endianness>(&mut self) -> io::Result<f64>;
|
|
||||||
/// Read a specific number of bytes
|
|
||||||
fn read_exact(&mut self, usize) -> io::Result<Vec<u8>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Endianness for LittleEndian {
|
|
||||||
#[inline]
|
|
||||||
fn int_to_target<T: EndianConvert>(val: T) -> T {
|
|
||||||
val.to_le()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn int_from_target<T: EndianConvert>(val: T) -> T {
|
|
||||||
<T as EndianConvert>::from_le(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Endianness for BigEndian {
|
|
||||||
#[inline]
|
|
||||||
fn int_to_target<T: EndianConvert>(val: T) -> T {
|
|
||||||
val.to_be()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn int_from_target<T: EndianConvert>(val: T) -> T {
|
|
||||||
<T as EndianConvert>::from_be(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_platform_convert {
|
|
||||||
($T:ty) => {
|
|
||||||
impl EndianConvert for $T {
|
|
||||||
#[inline]
|
|
||||||
fn to_be(self) -> $T {
|
|
||||||
self.to_be()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_le(self) -> $T {
|
|
||||||
self.to_le()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn from_be(x: $T) -> $T {
|
|
||||||
if cfg!(target_endian = "big") { x } else { x.swap_bytes() }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn from_le(x: $T) -> $T {
|
|
||||||
if cfg!(target_endian = "little") { x } else { x.swap_bytes() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_platform_convert!(u8);
|
|
||||||
impl_platform_convert!(u16);
|
|
||||||
impl_platform_convert!(u32);
|
|
||||||
impl_platform_convert!(u64);
|
|
||||||
|
|
||||||
#[cfg(target_endian = "little")]
|
|
||||||
macro_rules! val_to_buf {
|
|
||||||
($val:ident, $T:expr) => {
|
|
||||||
{
|
|
||||||
let mut buf = [0; $T];
|
|
||||||
for i in 0..buf.len() {
|
|
||||||
buf[i] = ($val >> (i * 8)) as u8;
|
|
||||||
}
|
|
||||||
buf
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_endian = "big")]
|
|
||||||
macro_rules! val_to_buf {
|
|
||||||
($val:ident, $T:expr) => {
|
|
||||||
{
|
|
||||||
let mut buf = [0; $T];
|
|
||||||
for i in 0..buf.len() {
|
|
||||||
buf[buf.len() - 1 - i] = ($val >> (i * 8)) as u8;
|
|
||||||
}
|
|
||||||
buf
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<W: Write> WritePodExt for W {
|
|
||||||
fn write_u64<T: Endianness>(&mut self, val: u64) -> io::Result<()> {
|
|
||||||
let tval = <T as Endianness>::int_to_target(val);
|
|
||||||
let buf = val_to_buf!(tval, 8);
|
|
||||||
self.write_all(&buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_u32<T: Endianness>(&mut self, val: u32) -> io::Result<()> {
|
|
||||||
let tval = <T as Endianness>::int_to_target(val);
|
|
||||||
let buf = val_to_buf!(tval, 4);
|
|
||||||
self.write_all(&buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_u16<T: Endianness>(&mut self, val: u16) -> io::Result<()> {
|
|
||||||
let tval = <T as Endianness>::int_to_target(val);
|
|
||||||
let buf = val_to_buf!(tval, 2);
|
|
||||||
self.write_all(&buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_u8(&mut self, val: u8) -> io::Result<()> {
|
|
||||||
self.write_all(&[val])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_i64<T: Endianness>(&mut self, val: i64) -> io::Result<()> {
|
|
||||||
self.write_u64::<T>(val as u64)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_i32<T: Endianness>(&mut self, val: i32) -> io::Result<()> {
|
|
||||||
self.write_u32::<T>(val as u32)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_i16<T: Endianness>(&mut self, val: i16) -> io::Result<()> {
|
|
||||||
self.write_u16::<T>(val as u16)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_i8(&mut self, val: i8) -> io::Result<()> {
|
|
||||||
self.write_u8(val as u8)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_f32<T: Endianness>(&mut self, val: f32) -> io::Result<()> {
|
|
||||||
let tval: u32 = val.to_bits();
|
|
||||||
self.write_u32::<T>(tval)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_f64<T: Endianness>(&mut self, val: f64) -> io::Result<()> {
|
|
||||||
let tval: u64 = val.to_bits();
|
|
||||||
self.write_u64::<T>(tval)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn fill_buf<R: Read>(reader: &mut R, buf: &mut [u8]) -> io::Result<()> {
|
|
||||||
let mut idx = 0;
|
|
||||||
while idx != buf.len() {
|
|
||||||
match reader.read(&mut buf[idx..]) {
|
|
||||||
Ok(0) => return Err(io::Error::new(io::ErrorKind::Other, "Could not read enough bytes")),
|
|
||||||
Ok(v) => { idx += v; }
|
|
||||||
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_endian = "little")]
|
|
||||||
macro_rules! buf_to_val {
|
|
||||||
($buf:ident, $T:ty) => {
|
|
||||||
{
|
|
||||||
let mut val: $T = 0;
|
|
||||||
for i in 0..$buf.len() {
|
|
||||||
val |= ($buf[i] as $T) << (i * 8);
|
|
||||||
}
|
|
||||||
val
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_endian = "big")]
|
|
||||||
macro_rules! buf_to_val {
|
|
||||||
($buf:ident, $T:ty) => {
|
|
||||||
{
|
|
||||||
let mut val: $T = 0;
|
|
||||||
for i in 0..$buf.len() {
|
|
||||||
val |= ($buf[$buf.len() - 1 - i] as $T) << (i * 8);
|
|
||||||
}
|
|
||||||
val
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: Read> ReadPodExt for R {
|
|
||||||
fn read_u64<T: Endianness>(&mut self) -> io::Result<u64> {
|
|
||||||
let buf = &mut [0u8; 8];
|
|
||||||
try!(fill_buf(self, buf));
|
|
||||||
let tval = buf_to_val!(buf, u64);
|
|
||||||
Ok(<T as Endianness>::int_from_target(tval))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_u32<T: Endianness>(&mut self) -> io::Result<u32> {
|
|
||||||
let buf = &mut [0u8; 4];
|
|
||||||
try!(fill_buf(self, buf));
|
|
||||||
let tval = buf_to_val!(buf, u32);
|
|
||||||
Ok(<T as Endianness>::int_from_target(tval))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_u16<T: Endianness>(&mut self) -> io::Result<u16> {
|
|
||||||
let buf = &mut [0u8; 2];
|
|
||||||
try!(fill_buf(self, buf));
|
|
||||||
let tval = buf_to_val!(buf, u16);
|
|
||||||
Ok(<T as Endianness>::int_from_target(tval))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_u8(&mut self) -> io::Result<u8> {
|
|
||||||
let buf = &mut [0u8; 1];
|
|
||||||
try!(fill_buf(self, buf));
|
|
||||||
Ok(buf[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_i64<T: Endianness>(&mut self) -> io::Result<i64> {
|
|
||||||
self.read_u64::<T>().map(|v| v as i64)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_i32<T: Endianness>(&mut self) -> io::Result<i32> {
|
|
||||||
self.read_u32::<T>().map(|v| v as i32)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_i16<T: Endianness>(&mut self) -> io::Result<i16> {
|
|
||||||
self.read_u16::<T>().map(|v| v as i16)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_i8(&mut self) -> io::Result<i8> {
|
|
||||||
self.read_u8().map(|v| v as i8)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_f64<T: Endianness>(&mut self) -> io::Result<f64> {
|
|
||||||
self.read_u64::<T>().map(|v| f64::from_bits(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_f32<T: Endianness>(&mut self) -> io::Result<f32> {
|
|
||||||
self.read_u32::<T>().map(|v| f32::from_bits(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_exact(&mut self, len: usize) -> io::Result<Vec<u8>> {
|
|
||||||
let mut res = vec![0; len];
|
|
||||||
try!(fill_buf(self, &mut res));
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,139 +0,0 @@
|
||||||
extern crate podio;
|
|
||||||
|
|
||||||
use std::io;
|
|
||||||
use podio::{LittleEndian, BigEndian};
|
|
||||||
use podio::{ReadPodExt, WritePodExt};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn write_be() {
|
|
||||||
let buf: &mut [u8] = &mut [0u8; 8];
|
|
||||||
let mut writer = io::Cursor::new(buf);
|
|
||||||
|
|
||||||
writer.set_position(0);
|
|
||||||
writer.write_u64::<BigEndian>(0x01_23_45_67_89_ab_cd_ef).unwrap();
|
|
||||||
assert_eq!(&writer.get_ref()[0..8], &[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]);
|
|
||||||
|
|
||||||
writer.set_position(0);
|
|
||||||
writer.write_u32::<BigEndian>(0x01_23_45_67).unwrap();
|
|
||||||
assert_eq!(&writer.get_ref()[0..4], &[0x01, 0x23, 0x45, 0x67]);
|
|
||||||
|
|
||||||
writer.set_position(0);
|
|
||||||
writer.write_u16::<BigEndian>(0x01_23).unwrap();
|
|
||||||
assert_eq!(&writer.get_ref()[0..2], &[0x01, 0x23]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn write_le() {
|
|
||||||
let buf: &mut [u8] = &mut [0u8; 8];
|
|
||||||
let mut writer = io::Cursor::new(buf);
|
|
||||||
|
|
||||||
writer.set_position(0);
|
|
||||||
writer.write_u64::<LittleEndian>(0x01_23_45_67_89_ab_cd_ef).unwrap();
|
|
||||||
assert_eq!(&writer.get_ref()[0..8], &[0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01]);
|
|
||||||
|
|
||||||
writer.set_position(0);
|
|
||||||
writer.write_u32::<LittleEndian>(0x01_23_45_67).unwrap();
|
|
||||||
assert_eq!(&writer.get_ref()[0..4], &[0x67, 0x45, 0x23, 0x01]);
|
|
||||||
|
|
||||||
writer.set_position(0);
|
|
||||||
writer.write_u16::<LittleEndian>(0x01_23).unwrap();
|
|
||||||
assert_eq!(&writer.get_ref()[0..2], &[0x23, 0x01]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn write_octet() {
|
|
||||||
let buf: &mut [u8] = &mut [0u8; 8];
|
|
||||||
let mut writer = io::Cursor::new(buf);
|
|
||||||
|
|
||||||
writer.set_position(0);
|
|
||||||
writer.write_u8(0x01).unwrap();
|
|
||||||
assert_eq!(&writer.get_ref()[0..1], &[0x01]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn write_float() {
|
|
||||||
let buf: &mut [u8] = &mut [0u8; 8];
|
|
||||||
let mut writer = io::Cursor::new(buf);
|
|
||||||
|
|
||||||
writer.set_position(0);
|
|
||||||
writer.write_f32::<LittleEndian>(10.12f32).unwrap();
|
|
||||||
assert_eq!(&writer.get_ref()[0..4], &[0x85, 0xEB, 0x21, 0x41]);
|
|
||||||
|
|
||||||
writer.set_position(0);
|
|
||||||
writer.write_f32::<BigEndian>(10.12f32).unwrap();
|
|
||||||
assert_eq!(&writer.get_ref()[0..4], &[0x41, 0x21, 0xEB, 0x85]);
|
|
||||||
|
|
||||||
writer.set_position(0);
|
|
||||||
writer.write_f64::<LittleEndian>(10.12f64).unwrap();
|
|
||||||
assert_eq!(&writer.get_ref()[0..8], &[0x3D, 0x0A, 0xD7, 0xA3, 0x70, 0x3D, 0x24, 0x40]);
|
|
||||||
|
|
||||||
writer.set_position(0);
|
|
||||||
writer.write_f64::<BigEndian>(10.12f64).unwrap();
|
|
||||||
assert_eq!(&writer.get_ref()[0..8], &[0x40, 0x24, 0x3D, 0x70, 0xA3, 0xD7, 0x0A, 0x3D]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_be() {
|
|
||||||
let buf: &[u8] = &[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
|
|
||||||
let mut reader = io::Cursor::new(buf);
|
|
||||||
|
|
||||||
reader.set_position(0);
|
|
||||||
assert_eq!(reader.read_u64::<BigEndian>().unwrap(), 0x0123456789abcdef);
|
|
||||||
|
|
||||||
reader.set_position(0);
|
|
||||||
assert_eq!(reader.read_u32::<BigEndian>().unwrap(), 0x01234567);
|
|
||||||
|
|
||||||
reader.set_position(0);
|
|
||||||
assert_eq!(reader.read_u16::<BigEndian>().unwrap(), 0x0123);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_le() {
|
|
||||||
let buf: &[u8] = &[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
|
|
||||||
let mut reader = io::Cursor::new(buf);
|
|
||||||
|
|
||||||
reader.set_position(0);
|
|
||||||
assert_eq!(reader.read_u64::<LittleEndian>().unwrap(), 0xefcdab8967452301);
|
|
||||||
|
|
||||||
reader.set_position(0);
|
|
||||||
assert_eq!(reader.read_u32::<LittleEndian>().unwrap(), 0x67452301);
|
|
||||||
|
|
||||||
reader.set_position(0);
|
|
||||||
assert_eq!(reader.read_u16::<LittleEndian>().unwrap(), 0x2301);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_octet() {
|
|
||||||
let buf: &[u8] = &[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
|
|
||||||
let mut reader = io::Cursor::new(buf);
|
|
||||||
|
|
||||||
reader.set_position(0);
|
|
||||||
assert_eq!(reader.read_u8().unwrap(), 0x01);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_float() {
|
|
||||||
let mut buf: &[u8] = &[0x41, 0x21, 0xEB, 0x85];
|
|
||||||
assert_eq!(buf.read_f32::<BigEndian>().unwrap(), 10.12f32);
|
|
||||||
|
|
||||||
let mut buf: &[u8] = &[0x85, 0xEB, 0x21, 0x41];
|
|
||||||
assert_eq!(buf.read_f32::<LittleEndian>().unwrap(), 10.12f32);
|
|
||||||
|
|
||||||
let mut buf: &[u8] = &[0x40, 0x24, 0x3D, 0x70, 0xA3, 0xD7, 0x0A, 0x3D];
|
|
||||||
assert_eq!(buf.read_f64::<BigEndian>().unwrap(), 10.12f64);
|
|
||||||
|
|
||||||
let mut buf: &[u8] = &[0x3D, 0x0A, 0xD7, 0xA3, 0x70, 0x3D, 0x24, 0x40];
|
|
||||||
assert_eq!(buf.read_f64::<LittleEndian>().unwrap(), 10.12f64);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_exact() {
|
|
||||||
let mut buf: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8];
|
|
||||||
assert_eq!(buf.read_exact(2).unwrap(), [1,2]);
|
|
||||||
assert_eq!(buf.read_exact(1).unwrap(), [3]);
|
|
||||||
assert_eq!(buf.read_exact(0).unwrap(), []);
|
|
||||||
assert_eq!(buf.read_exact(5).unwrap(), [4,5,6,7,8]);
|
|
||||||
assert_eq!(buf.read_exact(0).unwrap(), []);
|
|
||||||
assert!(buf.read_exact(1).is_err());
|
|
||||||
assert_eq!(buf.read_exact(0).unwrap(), []);
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
extern crate podio;
|
|
||||||
|
|
||||||
use std::io::{self, Read};
|
|
||||||
use podio::ReadPodExt;
|
|
||||||
|
|
||||||
struct TestReader {
|
|
||||||
state: Option<io::Result<usize>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TestReader {
|
|
||||||
fn new(first_return: io::Result<usize>) -> TestReader {
|
|
||||||
TestReader { state: Some(first_return) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get(&mut self) -> io::Result<u32> {
|
|
||||||
self.read_u32::<podio::LittleEndian>()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test(first_return: io::Result<usize>) -> io::Result<u32> {
|
|
||||||
TestReader::new(first_return).get()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Read for TestReader {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
match self.state.take().unwrap_or(Ok(1)) {
|
|
||||||
Ok(n) => {
|
|
||||||
for i in buf.iter_mut().take(n) {
|
|
||||||
*i = 0xA5;
|
|
||||||
}
|
|
||||||
Ok(n)
|
|
||||||
},
|
|
||||||
e @ Err(..) => e,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn interrupted() {
|
|
||||||
// Getting an io::ErrorKind::Interrupted should be retried, and thus succeed
|
|
||||||
let first_read = Err(io::Error::new(io::ErrorKind::Interrupted, "Interrupted"));
|
|
||||||
assert_eq!(TestReader::test(first_read).unwrap(), 0xA5A5A5A5);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn eof() {
|
|
||||||
// Getting a Ok(0) implies an unexpected EOF
|
|
||||||
let first_read = Ok(0);
|
|
||||||
assert!(TestReader::test(first_read).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn err() {
|
|
||||||
// Getting any other error is still an error
|
|
||||||
let first_read = Err(io::Error::new(io::ErrorKind::Other, "Other"));
|
|
||||||
assert!(TestReader::test(first_read).is_err());
|
|
||||||
}
|
|
|
@ -1 +1 @@
|
||||||
{"files":{"Cargo.toml":"00f64858860f669e881bf7e797c7f75bcdeb11e299bc9c7344fe3082ccb8b628","LICENSE":"6ac8711fb340c62ce0a4ecd463342d3fa0e8e70de697c863a2e1c0c53006003c","README.md":"face441ad06a7ef87477d5e96f3c456bef8f16662b03b5084d6fed7705fc4e99","appveyor.yml":"5c2aabf9d1fd01ca3dc33a14241bc0ce4a7327d2cbc3c3af350334bf5d107315","examples/extract.rs":"53b555617192330bd2f95235b82749905ad52617a5a3ba2648dbafbfff8a49a0","examples/extract_lorem.rs":"4e4e70927d2928a411ce4abe37ef4924c7156c4fd2697527c01fbd1797a76969","examples/stdin_info.rs":"54a9ce97b75a83c81de56c46cfe477dbd34dd98f739eb767f329b0e6dcb7490f","examples/write_dir.rs":"1a39da7b5d31997bf353a56d009ecdcf31da765f9ab191b20792e1904a594183","examples/write_sample.rs":"72a7c2db8074d20651ba04d0a49a7d6fb213ca674c63ab48db506deb69cba64c","script/doc-upload.cfg":"10684c8f5ef7e7388cfc4742ab9a060fe8f7fc1c51a63ede3c2443260ecc2d5e","src/compression.rs":"64ea510cfac9c9e5fed605a56fa44a7407df5cc51d4f63180bb2d4f2b307fdb2","src/cp437.rs":"7954c5b7df6f72fe3fe2c377c5de09973c2fa704c7ae0274a5490594e4b69517","src/crc32.rs":"3b503ee23047a4950e79f6956d0615f58c3cd8ded7f1fcd3f64e46dca37871da","src/lib.rs":"0afaf385ce7c7102016d2ba4c66c9cc7384ea710892477ed66bd1f560d83a732","src/read.rs":"d40a0b60c3b2dff73d6161e6d798a0e4aa0546a5f825bcddf6cf1bf52bf5366c","src/result.rs":"1006e46206a5b1491ed1d4fde0955a391df78cd42aa9582784b19170332a3783","src/spec.rs":"39b5e115745d962978e55ffb63ae2d62d0176c4c5ab457eea907a7aa33bf4e18","src/types.rs":"e2d7d3757013ca3ab149ee58fb5813fbeb7834e168870b28474816b47b869e61","src/write.rs":"59e3aff9339c9a16597101a5761391e93cc42eb99fb8c9057a6ee1e4e134da86","tests/data/invalid_offset.zip":"c5534a1803145f6344b04c437d436bd583852c78dd3937f4a73a2a39aa2d76b2","tests/data/mimetype.zip":"4bc6fd161d3e844b3e2b7f10fd0f9077ff565bb39fd2bfa34e7f8e9cd62db06d","tests/data/zip64_demo.zip":"223072a480f60d6a700e427b294e575ae2d6265f3e9881b647927f31d96c7e01","tests/invalid_date.rs":"e78b63b469d1f768bb452fefb84b013d3f9a4bf71dba2f9e12ff9e9c6ff8fd25"},"package":"36b9e08fb518a65cf7e08a1e482573eb87a2f4f8c6619316612a3c1f162fe822"}
|
{"files":{"CODE_OF_CONDUCT.md":"77930b2129928632cae79e10d0863885b34729e4f144f3ae5443cff65997196a","Cargo.lock":"93c43c455cb7331312fd980b3aabc02e68e67f9019b6502ba06ed93ee59553b5","Cargo.toml":"ca4e935d9ee6817239787ae2856dce0a25cae84affd469281691d4d37d9977fe","LICENSE":"6ac8711fb340c62ce0a4ecd463342d3fa0e8e70de697c863a2e1c0c53006003c","README.md":"96dd07ada4821b66dfffee4c8a3689d8001f844ee7f19b70741a87cfa6195db4","benches/read_entry.rs":"700235ee560887f33da56895d2a4da901c070b2a6a59aef90331046af4ddadad","examples/extract.rs":"c6e16e42391ce294093866822dc2ee8cf45231d50101d619a0caffa1be633ae6","examples/extract_lorem.rs":"a5b33a31e7a2865e8ed7af52bab538268a8ba07cc4c3d8aa85811f80c86cb1fb","examples/file_info.rs":"e091923fc8c492255609efdd3c5fb656ad177601a73f4011005deab8096ca425","examples/stdin_info.rs":"f0bfed4016abbdb3922426f118bc990e6a1335bab878e1e146cb057f4e33d3cf","examples/write_dir.rs":"6f0b62022946765b6ef5f8ecac054a742d823ab0473152748d637da1568a9475","examples/write_sample.rs":"f57f463a9dea4eca7054d10d35bd8c65e1b6e0530bfbebf758006acea1e4ca0d","src/aes.rs":"c1b19bdf15c75cc27b5657d1dc574b5c5ebcdc7ca2573e9909cc6ba1ffe7f6d8","src/aes_ctr.rs":"6a7a07c9538daf53624818f82084c6655db3862ca00751f3679c8b0bc346b70d","src/compression.rs":"20c2f0c1857a3dce154505b5a73b6a22da867e16d676f0617a0e453f13cad175","src/cp437.rs":"e3a044ddefc947f6d9c26ba50ebd438955d90b8971ad8b15e5dd1194495f3b93","src/crc32.rs":"6c5a48834dfa1ab33590fcc63f4121ee6d3f8fbcc5018e905c4d8570096874cf","src/lib.rs":"7c2f6bc3dde9575eeeaef9569139f28351333c29362f74569e627ad7be5a10b2","src/read.rs":"8df99dbebdf0b856d4291b98ed99ac87b29069b60ed41fafab3acf0765f6a5e2","src/result.rs":"cfa7a545a56c6ce807c04675d70f9aed42d1aa19ffb717f4f94b8901a9f3728d","src/spec.rs":"e3b3429c814bb78efe14a8a48d53c30793f017b5d090889d72a2dc20b94c549a","src/types.rs":"b2979cdba63c9f5e9360578036541a80780300ca4febaece5730f670ab1b0a74","src/write.rs":"9e44a9093e83d41d798aa9ae652d4c7fabd54182a51fd22d34bdb68c3311f31c","src/zipcrypto.rs":"4fe362af2a8e419d9cff6acc165acdc0dccaacc53baec7269b5f55dfcd396f5c","tests/aes_encryption.rs":"3733df1f5dcab4e9f9b9ec25cc7660f23c917759670e9b2e6fe77cfcd771faa9","tests/data/aes_archive.zip":"4abb3f304d1ab669453b7c4eae80db6ce8aff4ab91c8ab9a6edf90bbfede12f4","tests/data/comment_garbage.zip":"4a908af18691e50fc7773b34669a8ab6cb60875bee4d60fb7cd66d90eed55d2b","tests/data/files_and_dirs.zip":"e54cd8d084739f8d0b119d769c1263507b4da915cc38e4e2d3e4ef17c718f317","tests/data/invalid_offset.zip":"c5534a1803145f6344b04c437d436bd583852c78dd3937f4a73a2a39aa2d76b2","tests/data/invalid_offset2.zip":"0f0f3d654f303eaf90f599c0b0266be476ce34857cff932070a86e9853dcb4f9","tests/data/mimetype.zip":"aad4289745bd89016181a35653c9a483c6b9b632e06d002736c7040d61c562a2","tests/data/zip64_demo.zip":"223072a480f60d6a700e427b294e575ae2d6265f3e9881b647927f31d96c7e01","tests/end_to_end.rs":"a39d88edcea901bba2bbdb277f073724a4aadd5c8e692256e9c57a09fb2fd357","tests/invalid_date.rs":"72d5425ffe2887e05646ba56f5116ac760cbeeb7889da5c9be612fe57477bc94","tests/issue_234.rs":"1a3d68d79108140100709c188841c6bf54af8500c819072a440ec72df5d8ab26","tests/zip64_large.rs":"083f070ae566e0a3b317bd94d51186b37f2ff88f225deae721b0b56be838da1c","tests/zip_comment_garbage.rs":"ab0e2e3ace1ca5459977a092ee96836f58c7f7a7cfc8de258ab448d8d24f1cbb","tests/zip_crypto.rs":"7f4f9670e5d971ac4234c7ab2cafecf43801baf0be0cfca6e34c651f6fc6a80d"},"package":"bf225bcf73bb52cbb496e70475c7bd7a3f769df699c0020f6c7bd9a96dcf0b8d"}
|
|
@ -0,0 +1,76 @@
|
||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
In the interest of fostering an open and welcoming environment, we as
|
||||||
|
contributors and maintainers pledge to make participation in our project and
|
||||||
|
our community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||||
|
level of experience, education, socio-economic status, nationality, personal
|
||||||
|
appearance, race, religion, or sexual identity and orientation.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to creating a positive environment
|
||||||
|
include:
|
||||||
|
|
||||||
|
* Using welcoming and inclusive language
|
||||||
|
* Being respectful of differing viewpoints and experiences
|
||||||
|
* Gracefully accepting constructive criticism
|
||||||
|
* Focusing on what is best for the community
|
||||||
|
* Showing empathy towards other community members
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||||
|
advances
|
||||||
|
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or electronic
|
||||||
|
address, without explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Our Responsibilities
|
||||||
|
|
||||||
|
Project maintainers are responsible for clarifying the standards of acceptable
|
||||||
|
behavior and are expected to take appropriate and fair corrective action in
|
||||||
|
response to any instances of unacceptable behavior.
|
||||||
|
|
||||||
|
Project maintainers have the right and responsibility to remove, edit, or
|
||||||
|
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||||
|
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||||
|
permanently any contributor for other behaviors that they deem inappropriate,
|
||||||
|
threatening, offensive, or harmful.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all project spaces, and it also applies when
|
||||||
|
an individual is representing the project or its community in public spaces.
|
||||||
|
Examples of representing a project or community include using an official
|
||||||
|
project e-mail address, posting via an official social media account, or acting
|
||||||
|
as an appointed representative at an online or offline event. Representation of
|
||||||
|
a project may be further defined and clarified by project maintainers.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported by contacting the project team at ryan.levick@gmail.com. All
|
||||||
|
complaints will be reviewed and investigated and will result in a response that
|
||||||
|
is deemed necessary and appropriate to the circumstances. The project team is
|
||||||
|
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||||
|
Further details of specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
|
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||||
|
faith may face temporary or permanent repercussions as determined by other
|
||||||
|
members of the project's leadership.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||||
|
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see
|
||||||
|
https://www.contributor-covenant.org/faq
|
|
@ -0,0 +1,466 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aes"
|
||||||
|
version = "0.7.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cipher",
|
||||||
|
"cpufeatures",
|
||||||
|
"opaque-debug",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64ct"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bencher"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7dfdb4953a096c551ce9ace855a604d702e6e62d77fac690575ae347571717f5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bzip2"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0"
|
||||||
|
dependencies = [
|
||||||
|
"bzip2-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bzip2-sys"
|
||||||
|
version = "0.1.11+1.0.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.73"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||||
|
dependencies = [
|
||||||
|
"jobserver",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cipher"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "constant_time_eq"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpufeatures"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc32fast"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"crypto-common",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flate2"
|
||||||
|
version = "1.0.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"crc32fast",
|
||||||
|
"libc",
|
||||||
|
"libz-sys",
|
||||||
|
"miniz_oxide",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hmac"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jobserver"
|
||||||
|
version = "0.1.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.121"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libz-sys"
|
||||||
|
version = "1.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f35facd4a5673cb5a48822be2be1d4236c1c99cb4113cab7061ac720d5bf859"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
|
||||||
|
dependencies = [
|
||||||
|
"adler",
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_threads"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aba1801fb138d8e85e11d0fc70baf4fe1cdfffda7c6cd34a854905df588e5ed0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "opaque-debug"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "password-hash"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d791538a6dcc1e7cb7fe6f6b58aca40e7f79403c45b2bc274008b5e647af1d8"
|
||||||
|
dependencies = [
|
||||||
|
"base64ct",
|
||||||
|
"rand_core",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pbkdf2"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
"hmac",
|
||||||
|
"password-hash",
|
||||||
|
"sha2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "same-file"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha1"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c77f4e7f65455545c2153c1253d25056825e77ee2533f0e41deb65a93a34852f"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha2"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"libc",
|
||||||
|
"num_threads",
|
||||||
|
"time-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time-macros"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcpkg"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "walkdir"
|
||||||
|
version = "2.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
|
||||||
|
dependencies = [
|
||||||
|
"same-file",
|
||||||
|
"winapi",
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.10.2+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zip"
|
||||||
|
version = "0.6.2"
|
||||||
|
dependencies = [
|
||||||
|
"aes",
|
||||||
|
"bencher",
|
||||||
|
"byteorder",
|
||||||
|
"bzip2",
|
||||||
|
"constant_time_eq",
|
||||||
|
"crc32fast",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"flate2",
|
||||||
|
"getrandom",
|
||||||
|
"hmac",
|
||||||
|
"pbkdf2",
|
||||||
|
"sha1",
|
||||||
|
"time",
|
||||||
|
"walkdir",
|
||||||
|
"zstd",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd"
|
||||||
|
version = "0.10.0+zstd.1.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b1365becbe415f3f0fcd024e2f7b45bacfb5bdd055f0dc113571394114e7bdd"
|
||||||
|
dependencies = [
|
||||||
|
"zstd-safe",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd-safe"
|
||||||
|
version = "4.1.4+zstd.1.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2f7cd17c9af1a4d6c24beb1cc54b17e2ef7b593dc92f19e9d9acad8b182bbaee"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"zstd-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd-sys"
|
||||||
|
version = "1.6.3+zstd.1.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fc49afa5c8d634e75761feda8c592051e7eeb4683ba827211eb0d731d3402ea8"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
]
|
|
@ -3,44 +3,84 @@
|
||||||
# When uploading crates to the registry Cargo will automatically
|
# When uploading crates to the registry Cargo will automatically
|
||||||
# "normalize" Cargo.toml files for maximal compatibility
|
# "normalize" Cargo.toml files for maximal compatibility
|
||||||
# with all versions of Cargo and also rewrite `path` dependencies
|
# with all versions of Cargo and also rewrite `path` dependencies
|
||||||
# to registry (e.g. crates.io) dependencies
|
# to registry (e.g., crates.io) dependencies.
|
||||||
#
|
#
|
||||||
# If you believe there's an error in this file please file an
|
# If you are reading this file be aware that the original Cargo.toml
|
||||||
# issue against the rust-lang/cargo repository. If you're
|
# will likely look very different (and much more reasonable).
|
||||||
# editing this file be aware that the upstream Cargo.toml
|
# See Cargo.toml.orig for the original contents.
|
||||||
# will likely look very different (and much more reasonable)
|
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
|
edition = "2018"
|
||||||
name = "zip"
|
name = "zip"
|
||||||
version = "0.4.2"
|
version = "0.6.2"
|
||||||
authors = ["Mathijs van de Nes <git@mathijs.vd-nes.nl>"]
|
authors = ["Mathijs van de Nes <git@mathijs.vd-nes.nl>", "Marli Frost <marli@frost.red>", "Ryan Levick <ryan.levick@gmail.com>"]
|
||||||
description = "Library to support the reading and writing of zip files.\n"
|
description = "Library to support the reading and writing of zip files.\n"
|
||||||
documentation = "http://mvdnes.github.io/rust-docs/zip-rs/zip/index.html"
|
|
||||||
keywords = ["zip", "archive"]
|
keywords = ["zip", "archive"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/mvdnes/zip-rs.git"
|
repository = "https://github.com/zip-rs/zip.git"
|
||||||
[dependencies.bzip2]
|
|
||||||
version = "0.3"
|
[[bench]]
|
||||||
|
name = "read_entry"
|
||||||
|
harness = false
|
||||||
|
[dependencies.aes]
|
||||||
|
version = "0.7.5"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
|
[dependencies.byteorder]
|
||||||
|
version = "1.4.3"
|
||||||
|
|
||||||
|
[dependencies.bzip2]
|
||||||
|
version = "0.4.3"
|
||||||
|
optional = true
|
||||||
|
|
||||||
|
[dependencies.constant_time_eq]
|
||||||
|
version = "0.1.5"
|
||||||
|
optional = true
|
||||||
|
|
||||||
|
[dependencies.crc32fast]
|
||||||
|
version = "1.3.2"
|
||||||
|
|
||||||
[dependencies.flate2]
|
[dependencies.flate2]
|
||||||
version = "1.0"
|
version = "1.0.22"
|
||||||
optional = true
|
optional = true
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
[dependencies.msdos_time]
|
[dependencies.hmac]
|
||||||
version = "0.1"
|
version = "0.12.1"
|
||||||
|
features = ["reset"]
|
||||||
|
optional = true
|
||||||
|
|
||||||
[dependencies.podio]
|
[dependencies.pbkdf2]
|
||||||
version = "0.1"
|
version = "0.10.1"
|
||||||
|
optional = true
|
||||||
|
|
||||||
|
[dependencies.sha1]
|
||||||
|
version = "0.10.1"
|
||||||
|
optional = true
|
||||||
|
|
||||||
[dependencies.time]
|
[dependencies.time]
|
||||||
version = "0.1"
|
version = "0.3.7"
|
||||||
|
features = ["formatting", "macros"]
|
||||||
|
optional = true
|
||||||
|
|
||||||
|
[dependencies.zstd]
|
||||||
|
version = "0.10.0"
|
||||||
|
optional = true
|
||||||
|
[dev-dependencies.bencher]
|
||||||
|
version = "0.1.5"
|
||||||
|
|
||||||
|
[dev-dependencies.getrandom]
|
||||||
|
version = "0.2.5"
|
||||||
|
|
||||||
[dev-dependencies.walkdir]
|
[dev-dependencies.walkdir]
|
||||||
version = "1.0"
|
version = "2.3.2"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["bzip2", "deflate"]
|
aes-crypto = ["aes", "constant_time_eq", "hmac", "pbkdf2", "sha1"]
|
||||||
deflate = ["flate2", "flate2/rust_backend"]
|
default = ["aes-crypto", "bzip2", "deflate", "time", "zstd"]
|
||||||
deflate-miniz = ["flate2", "flate2/miniz-sys"]
|
deflate = ["flate2/rust_backend"]
|
||||||
deflate-zlib = ["flate2", "flate2/zlib"]
|
deflate-miniz = ["flate2/default"]
|
||||||
|
deflate-zlib = ["flate2/zlib"]
|
||||||
|
unreserved = []
|
||||||
|
[target."cfg(any(all(target_arch = \"arm\", target_pointer_width = \"32\"), target_arch = \"mips\", target_arch = \"powerpc\"))".dependencies.crossbeam-utils]
|
||||||
|
version = "0.8.8"
|
||||||
|
|
|
@ -1,30 +1,33 @@
|
||||||
zip-rs
|
zip-rs
|
||||||
======
|
======
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/mvdnes/zip-rs.svg?branch=master)](https://travis-ci.org/mvdnes/zip-rs)
|
[![Build Status](https://img.shields.io/github/workflow/status/zip-rs/zip/CI)](https://github.com/zip-rs/zip/actions?query=branch%3Amaster+workflow%3ACI)
|
||||||
[![Build status](https://ci.appveyor.com/api/projects/status/gsnpqcodg19iu253/branch/master?svg=true)](https://ci.appveyor.com/project/mvdnes/zip-rs/branch/master)
|
|
||||||
[![Crates.io version](https://img.shields.io/crates/v/zip.svg)](https://crates.io/crates/zip)
|
[![Crates.io version](https://img.shields.io/crates/v/zip.svg)](https://crates.io/crates/zip)
|
||||||
|
[![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/rQ7H9cSsF4)
|
||||||
|
|
||||||
[Documentation](http://mvdnes.github.io/rust-docs/zip-rs/zip/index.html)
|
[Documentation](https://docs.rs/zip/0.6.2/zip/)
|
||||||
|
|
||||||
|
> PSA: This version of the ZIP crate will not gain any new features,
|
||||||
|
> and will only be updated if major security issues are found.
|
||||||
|
|
||||||
Info
|
Info
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
||||||
A zip library for rust which supports reading and writing of simple ZIP files.
|
A zip library for rust which supports reading and writing of simple ZIP files.
|
||||||
|
|
||||||
Supported compression formats:
|
Supported compression formats:
|
||||||
|
|
||||||
* stored (i.e. none)
|
* stored (i.e. none)
|
||||||
* deflate
|
* deflate
|
||||||
* bzip2 (optional, enabled by default)
|
* bzip2
|
||||||
|
* zstd
|
||||||
|
|
||||||
Currently unsupported zip extensions:
|
Currently unsupported zip extensions:
|
||||||
|
|
||||||
* Encryption
|
* Encryption
|
||||||
* Multi-disk
|
* Multi-disk
|
||||||
|
|
||||||
We aim to support rust versions 1.20+.
|
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
@ -32,34 +35,43 @@ With all default features:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
zip = "0.4"
|
zip = "0.6.2"
|
||||||
```
|
```
|
||||||
|
|
||||||
Without the default features:
|
Without the default features:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
zip = { version = "0.4", default-features = false }
|
zip = { version = "0.6.2", default-features = false }
|
||||||
```
|
```
|
||||||
|
|
||||||
You can further control the backend of `deflate` compression method with these features:
|
The features available are:
|
||||||
* `deflate` (enabled by default) uses [miniz_oxide](https://github.com/Frommi/miniz_oxide)
|
|
||||||
* `deflate-miniz` uses [miniz](https://github.com/richgel999/miniz)
|
|
||||||
* `deflate-zlib` uses zlib
|
|
||||||
|
|
||||||
For example:
|
* `aes-crypto`: Enables decryption of files which were encrypted with AES. Supports AE-1 and AE-2 methods.
|
||||||
|
* `deflate`: Enables the deflate compression algorithm, which is the default for zip files.
|
||||||
|
* `bzip2`: Enables the BZip2 compression algorithm.
|
||||||
|
* `time`: Enables features using the [time](https://github.com/rust-lang-deprecated/time) crate.
|
||||||
|
* `zstd`: Enables the Zstandard compression algorithm.
|
||||||
|
|
||||||
```toml
|
All of these are enabled by default.
|
||||||
[dependencies]
|
|
||||||
zip = { version = "0.4", features = ["deflate-zlib"], default-features = false }
|
MSRV
|
||||||
```
|
----
|
||||||
|
|
||||||
|
Our current Minimum Supported Rust Version is **1.54.0**. When adding features,
|
||||||
|
we will follow these guidelines:
|
||||||
|
|
||||||
|
- We will always support the latest four minor Rust versions. This gives you a 6
|
||||||
|
month window to upgrade your compiler.
|
||||||
|
- Any change to the MSRV will be accompanied with a **minor** version bump
|
||||||
|
- While the crate is pre-1.0, this will be a change to the PATCH version.
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
|
|
||||||
See the [examples directory](examples) for:
|
See the [examples directory](examples) for:
|
||||||
* How to write a file to a zip.
|
* How to write a file to a zip.
|
||||||
* how to write a directory of files to a zip (using [walkdir](https://github.com/BurntSushi/walkdir)).
|
* How to write a directory of files to a zip (using [walkdir](https://github.com/BurntSushi/walkdir)).
|
||||||
* How to extract a zip file.
|
* How to extract a zip file.
|
||||||
* How to extract a single file from a zip.
|
* How to extract a single file from a zip.
|
||||||
* How to read a zip from the standard input.
|
* How to read a zip from the standard input.
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
# Appveyor configuration template for Rust using rustup for Rust installation
|
|
||||||
# https://github.com/starkat99/appveyor-rust
|
|
||||||
|
|
||||||
os: Visual Studio 2015
|
|
||||||
|
|
||||||
environment:
|
|
||||||
matrix:
|
|
||||||
|
|
||||||
# Stable 64-bit MSVC
|
|
||||||
- channel: stable
|
|
||||||
target: x86_64-pc-windows-msvc
|
|
||||||
# Stable 32-bit MSVC
|
|
||||||
- channel: stable
|
|
||||||
target: i686-pc-windows-msvc
|
|
||||||
# Stable 64-bit GNU
|
|
||||||
- channel: stable
|
|
||||||
target: x86_64-pc-windows-gnu
|
|
||||||
# Stable 32-bit GNU
|
|
||||||
- channel: stable
|
|
||||||
target: i686-pc-windows-gnu
|
|
||||||
|
|
||||||
install:
|
|
||||||
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
|
|
||||||
- rustup-init -yv --default-toolchain %channel% --default-host %target%
|
|
||||||
- set PATH=%PATH%;%USERPROFILE%\.cargo\bin
|
|
||||||
- ps: |
|
|
||||||
if ($env:target -like "*-gnu" -And $env:target -like "x86_64-*") {
|
|
||||||
Start-FileDownload "http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/5.1.0/threads-win32/seh/x86_64-5.1.0-release-win32-seh-rt_v4-rev0.7z/download" -FileName mingw64.7z
|
|
||||||
7z x -oC:\ mingw64.7z > $null
|
|
||||||
$env:path = "C:\mingw64\bin;" + $env:path
|
|
||||||
gcc --version
|
|
||||||
}
|
|
||||||
elseif ($env:target -like "*-gnu") {
|
|
||||||
$env:path = "C:\mingw\bin;" + $env:path
|
|
||||||
gcc --version
|
|
||||||
}
|
|
||||||
- rustc -vV
|
|
||||||
- cargo -vV
|
|
||||||
|
|
||||||
build: false
|
|
||||||
|
|
||||||
test_script:
|
|
||||||
- cargo test
|
|
||||||
- cargo test --no-default-features
|
|
||||||
- cargo test --no-default-features --features "deflate-miniz"
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
use bencher::{benchmark_group, benchmark_main};
|
||||||
|
|
||||||
|
use std::io::{Cursor, Read, Write};
|
||||||
|
|
||||||
|
use bencher::Bencher;
|
||||||
|
use getrandom::getrandom;
|
||||||
|
use zip::{ZipArchive, ZipWriter};
|
||||||
|
|
||||||
|
fn generate_random_archive(size: usize) -> Vec<u8> {
|
||||||
|
let data = Vec::new();
|
||||||
|
let mut writer = ZipWriter::new(Cursor::new(data));
|
||||||
|
let options =
|
||||||
|
zip::write::FileOptions::default().compression_method(zip::CompressionMethod::Stored);
|
||||||
|
|
||||||
|
writer.start_file("random.dat", options).unwrap();
|
||||||
|
let mut bytes = vec![0u8; size];
|
||||||
|
getrandom(&mut bytes).unwrap();
|
||||||
|
writer.write_all(&bytes).unwrap();
|
||||||
|
|
||||||
|
writer.finish().unwrap().into_inner()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_entry(bench: &mut Bencher) {
|
||||||
|
let size = 1024 * 1024;
|
||||||
|
let bytes = generate_random_archive(size);
|
||||||
|
let mut archive = ZipArchive::new(Cursor::new(bytes.as_slice())).unwrap();
|
||||||
|
|
||||||
|
bench.iter(|| {
|
||||||
|
let mut file = archive.by_name("random.dat").unwrap();
|
||||||
|
let mut buf = [0u8; 1024];
|
||||||
|
loop {
|
||||||
|
let n = file.read(&mut buf).unwrap();
|
||||||
|
if n == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
bench.bytes = size as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
benchmark_group!(benches, read_entry);
|
||||||
|
benchmark_main!(benches);
|
|
@ -1,7 +1,5 @@
|
||||||
extern crate zip;
|
|
||||||
|
|
||||||
use std::io;
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::io;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
std::process::exit(real_main());
|
std::process::exit(real_main());
|
||||||
|
@ -20,7 +18,10 @@ fn real_main() -> i32 {
|
||||||
|
|
||||||
for i in 0..archive.len() {
|
for i in 0..archive.len() {
|
||||||
let mut file = archive.by_index(i).unwrap();
|
let mut file = archive.by_index(i).unwrap();
|
||||||
let outpath = file.sanitized_name();
|
let outpath = match file.enclosed_name() {
|
||||||
|
Some(path) => path.to_owned(),
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
let comment = file.comment();
|
let comment = file.comment();
|
||||||
|
@ -29,11 +30,16 @@ fn real_main() -> i32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (&*file.name()).ends_with('/') {
|
if (*file.name()).ends_with('/') {
|
||||||
println!("File {} extracted to \"{}\"", i, outpath.as_path().display());
|
println!("File {} extracted to \"{}\"", i, outpath.display());
|
||||||
fs::create_dir_all(&outpath).unwrap();
|
fs::create_dir_all(&outpath).unwrap();
|
||||||
} else {
|
} else {
|
||||||
println!("File {} extracted to \"{}\" ({} bytes)", i, outpath.as_path().display(), file.size());
|
println!(
|
||||||
|
"File {} extracted to \"{}\" ({} bytes)",
|
||||||
|
i,
|
||||||
|
outpath.display(),
|
||||||
|
file.size()
|
||||||
|
);
|
||||||
if let Some(p) = outpath.parent() {
|
if let Some(p) = outpath.parent() {
|
||||||
if !p.exists() {
|
if !p.exists() {
|
||||||
fs::create_dir_all(&p).unwrap();
|
fs::create_dir_all(&p).unwrap();
|
||||||
|
@ -53,5 +59,6 @@ fn real_main() -> i32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
|
0
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
|
|
||||||
extern crate zip;
|
fn main() {
|
||||||
|
|
||||||
fn main()
|
|
||||||
{
|
|
||||||
std::process::exit(real_main());
|
std::process::exit(real_main());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn real_main() -> i32
|
fn real_main() -> i32 {
|
||||||
{
|
|
||||||
let args: Vec<_> = std::env::args().collect();
|
let args: Vec<_> = std::env::args().collect();
|
||||||
if args.len() < 2 {
|
if args.len() < 2 {
|
||||||
println!("Usage: {} <filename>", args[0]);
|
println!("Usage: {} <filename>", args[0]);
|
||||||
|
@ -18,16 +14,18 @@ fn real_main() -> i32
|
||||||
let zipfile = std::fs::File::open(&fname).unwrap();
|
let zipfile = std::fs::File::open(&fname).unwrap();
|
||||||
|
|
||||||
let mut archive = zip::ZipArchive::new(zipfile).unwrap();
|
let mut archive = zip::ZipArchive::new(zipfile).unwrap();
|
||||||
|
|
||||||
let mut file = match archive.by_name("test/lorem_ipsum.txt")
|
let mut file = match archive.by_name("test/lorem_ipsum.txt") {
|
||||||
{
|
|
||||||
Ok(file) => file,
|
Ok(file) => file,
|
||||||
Err(..) => { println!("File test/lorem_ipsum.txt not found"); return 2;}
|
Err(..) => {
|
||||||
|
println!("File test/lorem_ipsum.txt not found");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut contents = String::new();
|
let mut contents = String::new();
|
||||||
file.read_to_string(&mut contents).unwrap();
|
file.read_to_string(&mut contents).unwrap();
|
||||||
println!("{}", contents);
|
println!("{}", contents);
|
||||||
|
|
||||||
return 0;
|
0
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
use std::fs;
|
||||||
|
use std::io::BufReader;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
std::process::exit(real_main());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn real_main() -> i32 {
|
||||||
|
let args: Vec<_> = std::env::args().collect();
|
||||||
|
if args.len() < 2 {
|
||||||
|
println!("Usage: {} <filename>", args[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
let fname = std::path::Path::new(&*args[1]);
|
||||||
|
let file = fs::File::open(&fname).unwrap();
|
||||||
|
let reader = BufReader::new(file);
|
||||||
|
|
||||||
|
let mut archive = zip::ZipArchive::new(reader).unwrap();
|
||||||
|
|
||||||
|
for i in 0..archive.len() {
|
||||||
|
let file = archive.by_index(i).unwrap();
|
||||||
|
let outpath = match file.enclosed_name() {
|
||||||
|
Some(path) => path,
|
||||||
|
None => {
|
||||||
|
println!("Entry {} has a suspicious path", file.name());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
let comment = file.comment();
|
||||||
|
if !comment.is_empty() {
|
||||||
|
println!("Entry {} comment: {}", i, comment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*file.name()).ends_with('/') {
|
||||||
|
println!(
|
||||||
|
"Entry {} is a directory with name \"{}\"",
|
||||||
|
i,
|
||||||
|
outpath.display()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
println!(
|
||||||
|
"Entry {} is a file with name \"{}\" ({} bytes)",
|
||||||
|
i,
|
||||||
|
outpath.display(),
|
||||||
|
file.size()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
|
@ -1,5 +1,3 @@
|
||||||
extern crate zip;
|
|
||||||
|
|
||||||
use std::io::{self, Read};
|
use std::io::{self, Read};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -14,18 +12,24 @@ fn real_main() -> i32 {
|
||||||
loop {
|
loop {
|
||||||
match zip::read::read_zipfile_from_stream(&mut stdin_handle) {
|
match zip::read::read_zipfile_from_stream(&mut stdin_handle) {
|
||||||
Ok(Some(mut file)) => {
|
Ok(Some(mut file)) => {
|
||||||
println!("{}: {} bytes ({} bytes packed)", file.name(), file.size(), file.compressed_size());
|
println!(
|
||||||
|
"{}: {} bytes ({} bytes packed)",
|
||||||
|
file.name(),
|
||||||
|
file.size(),
|
||||||
|
file.compressed_size()
|
||||||
|
);
|
||||||
match file.read(&mut buf) {
|
match file.read(&mut buf) {
|
||||||
Ok(n) => println!("The first {} bytes are: {:?}", n, &buf[0..n]),
|
Ok(n) => println!("The first {} bytes are: {:?}", n, &buf[0..n]),
|
||||||
Err(e) => println!("Could not read the file: {:?}", e),
|
Err(e) => println!("Could not read the file: {:?}", e),
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
Ok(None) => break,
|
Ok(None) => break,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error encountered while reading zip: {:?}", e);
|
println!("Error encountered while reading zip: {:?}", e);
|
||||||
return 1;
|
return 1;
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
|
0
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,56 +1,75 @@
|
||||||
extern crate zip;
|
|
||||||
extern crate walkdir;
|
|
||||||
|
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::io::{Write, Seek};
|
use std::io::{Seek, Write};
|
||||||
use std::iter::Iterator;
|
use std::iter::Iterator;
|
||||||
use zip::write::FileOptions;
|
|
||||||
use zip::result::ZipError;
|
use zip::result::ZipError;
|
||||||
|
use zip::write::FileOptions;
|
||||||
|
|
||||||
use walkdir::{WalkDir, DirEntry};
|
|
||||||
use std::path::Path;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
use std::path::Path;
|
||||||
|
use walkdir::{DirEntry, WalkDir};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
std::process::exit(real_main());
|
std::process::exit(real_main());
|
||||||
}
|
}
|
||||||
|
|
||||||
const METHOD_STORED : Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Stored);
|
const METHOD_STORED: Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Stored);
|
||||||
|
|
||||||
#[cfg(feature = "flate2")]
|
#[cfg(any(
|
||||||
const METHOD_DEFLATED : Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Deflated);
|
feature = "deflate",
|
||||||
#[cfg(not(feature = "flate2"))]
|
feature = "deflate-miniz",
|
||||||
const METHOD_DEFLATED : Option<zip::CompressionMethod> = None;
|
feature = "deflate-zlib"
|
||||||
|
))]
|
||||||
|
const METHOD_DEFLATED: Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Deflated);
|
||||||
|
#[cfg(not(any(
|
||||||
|
feature = "deflate",
|
||||||
|
feature = "deflate-miniz",
|
||||||
|
feature = "deflate-zlib"
|
||||||
|
)))]
|
||||||
|
const METHOD_DEFLATED: Option<zip::CompressionMethod> = None;
|
||||||
|
|
||||||
#[cfg(feature = "bzip2")]
|
#[cfg(feature = "bzip2")]
|
||||||
const METHOD_BZIP2 : Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Bzip2);
|
const METHOD_BZIP2: Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Bzip2);
|
||||||
#[cfg(not(feature = "bzip2"))]
|
#[cfg(not(feature = "bzip2"))]
|
||||||
const METHOD_BZIP2 : Option<zip::CompressionMethod> = None;
|
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 {
|
fn real_main() -> i32 {
|
||||||
let args: Vec<_> = std::env::args().collect();
|
let args: Vec<_> = std::env::args().collect();
|
||||||
if args.len() < 3 {
|
if args.len() < 3 {
|
||||||
println!("Usage: {} <source_directory> <destination_zipfile>",
|
println!(
|
||||||
args[0]);
|
"Usage: {} <source_directory> <destination_zipfile>",
|
||||||
|
args[0]
|
||||||
|
);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let src_dir = &*args[1];
|
let src_dir = &*args[1];
|
||||||
let dst_file = &*args[2];
|
let dst_file = &*args[2];
|
||||||
for &method in [METHOD_STORED, METHOD_DEFLATED, METHOD_BZIP2].iter() {
|
for &method in [METHOD_STORED, METHOD_DEFLATED, METHOD_BZIP2, METHOD_ZSTD].iter() {
|
||||||
if method.is_none() { continue }
|
if method.is_none() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
match doit(src_dir, dst_file, method.unwrap()) {
|
match doit(src_dir, dst_file, method.unwrap()) {
|
||||||
Ok(_) => println!("done: {} written to {}", src_dir, dst_file),
|
Ok(_) => println!("done: {} written to {}", src_dir, dst_file),
|
||||||
Err(e) => println!("Error: {:?}", e),
|
Err(e) => println!("Error: {:?}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn zip_dir<T>(it: &mut Iterator<Item=DirEntry>, prefix: &str, writer: T, method: zip::CompressionMethod)
|
fn zip_dir<T>(
|
||||||
-> zip::result::ZipResult<()>
|
it: &mut dyn Iterator<Item = DirEntry>,
|
||||||
where T: Write+Seek
|
prefix: &str,
|
||||||
|
writer: T,
|
||||||
|
method: zip::CompressionMethod,
|
||||||
|
) -> zip::result::ZipResult<()>
|
||||||
|
where
|
||||||
|
T: Write + Seek,
|
||||||
{
|
{
|
||||||
let mut zip = zip::ZipWriter::new(writer);
|
let mut zip = zip::ZipWriter::new(writer);
|
||||||
let options = FileOptions::default()
|
let options = FileOptions::default()
|
||||||
|
@ -60,26 +79,36 @@ fn zip_dir<T>(it: &mut Iterator<Item=DirEntry>, prefix: &str, writer: T, method:
|
||||||
let mut buffer = Vec::new();
|
let mut buffer = Vec::new();
|
||||||
for entry in it {
|
for entry in it {
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
let name = path.strip_prefix(Path::new(prefix))
|
let name = path.strip_prefix(Path::new(prefix)).unwrap();
|
||||||
.unwrap()
|
|
||||||
.to_str()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
|
// Write file or directory explicitly
|
||||||
|
// Some unzip tools unzip files with directory paths correctly, some do not!
|
||||||
if path.is_file() {
|
if path.is_file() {
|
||||||
println!("adding {:?} as {:?} ...", path, name);
|
println!("adding file {:?} as {:?} ...", path, name);
|
||||||
zip.start_file(name, options)?;
|
#[allow(deprecated)]
|
||||||
|
zip.start_file_from_path(name, options)?;
|
||||||
let mut f = File::open(path)?;
|
let mut f = File::open(path)?;
|
||||||
|
|
||||||
f.read_to_end(&mut buffer)?;
|
f.read_to_end(&mut buffer)?;
|
||||||
zip.write_all(&*buffer)?;
|
zip.write_all(&*buffer)?;
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
|
} else if !name.as_os_str().is_empty() {
|
||||||
|
// Only if not root! Avoids path spec / warning
|
||||||
|
// and mapname conversion failed error on unzip
|
||||||
|
println!("adding dir {:?} as {:?} ...", path, name);
|
||||||
|
#[allow(deprecated)]
|
||||||
|
zip.add_directory_from_path(name, options)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
zip.finish()?;
|
zip.finish()?;
|
||||||
Result::Ok(())
|
Result::Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn doit(src_dir: &str, dst_file: &str, method: zip::CompressionMethod) -> zip::result::ZipResult<()> {
|
fn doit(
|
||||||
|
src_dir: &str,
|
||||||
|
dst_file: &str,
|
||||||
|
method: zip::CompressionMethod,
|
||||||
|
) -> zip::result::ZipResult<()> {
|
||||||
if !Path::new(src_dir).is_dir() {
|
if !Path::new(src_dir).is_dir() {
|
||||||
return Err(ZipError::FileNotFound);
|
return Err(ZipError::FileNotFound);
|
||||||
}
|
}
|
||||||
|
@ -87,10 +116,10 @@ fn doit(src_dir: &str, dst_file: &str, method: zip::CompressionMethod) -> zip::r
|
||||||
let path = Path::new(dst_file);
|
let path = Path::new(dst_file);
|
||||||
let file = File::create(&path).unwrap();
|
let file = File::create(&path).unwrap();
|
||||||
|
|
||||||
let walkdir = WalkDir::new(src_dir.to_string());
|
let walkdir = WalkDir::new(src_dir);
|
||||||
let it = walkdir.into_iter();
|
let it = walkdir.into_iter();
|
||||||
|
|
||||||
zip_dir(&mut it.filter_map(|e| e.ok()), src_dir, file, method)?;
|
zip_dir(&mut it.filter_map(|e| e.ok()), src_dir, file, method)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use zip::write::FileOptions;
|
use zip::write::FileOptions;
|
||||||
|
|
||||||
extern crate zip;
|
fn main() {
|
||||||
|
|
||||||
fn main()
|
|
||||||
{
|
|
||||||
std::process::exit(real_main());
|
std::process::exit(real_main());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn real_main() -> i32
|
fn real_main() -> i32 {
|
||||||
{
|
|
||||||
let args: Vec<_> = std::env::args().collect();
|
let args: Vec<_> = std::env::args().collect();
|
||||||
if args.len() < 2 {
|
if args.len() < 2 {
|
||||||
println!("Usage: {} <filename>", args[0]);
|
println!("Usage: {} <filename>", args[0]);
|
||||||
|
@ -17,36 +13,36 @@ fn real_main() -> i32
|
||||||
}
|
}
|
||||||
|
|
||||||
let filename = &*args[1];
|
let filename = &*args[1];
|
||||||
match doit(filename)
|
match doit(filename) {
|
||||||
{
|
|
||||||
Ok(_) => println!("File written to {}", filename),
|
Ok(_) => println!("File written to {}", filename),
|
||||||
Err(e) => println!("Error: {:?}", e),
|
Err(e) => println!("Error: {:?}", e),
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn doit(filename: &str) -> zip::result::ZipResult<()>
|
fn doit(filename: &str) -> zip::result::ZipResult<()> {
|
||||||
{
|
|
||||||
let path = std::path::Path::new(filename);
|
let path = std::path::Path::new(filename);
|
||||||
let file = std::fs::File::create(&path).unwrap();
|
let file = std::fs::File::create(&path).unwrap();
|
||||||
|
|
||||||
let mut zip = zip::ZipWriter::new(file);
|
let mut zip = zip::ZipWriter::new(file);
|
||||||
|
|
||||||
try!(zip.add_directory("test/", FileOptions::default()));
|
zip.add_directory("test/", Default::default())?;
|
||||||
|
|
||||||
let options = FileOptions::default().compression_method(zip::CompressionMethod::Stored).unix_permissions(0o755);
|
let options = FileOptions::default()
|
||||||
try!(zip.start_file("test/☃.txt", options));
|
.compression_method(zip::CompressionMethod::Stored)
|
||||||
try!(zip.write_all(b"Hello, World!\n"));
|
.unix_permissions(0o755);
|
||||||
|
zip.start_file("test/☃.txt", options)?;
|
||||||
|
zip.write_all(b"Hello, World!\n")?;
|
||||||
|
|
||||||
try!(zip.start_file("test/lorem_ipsum.txt", FileOptions::default()));
|
zip.start_file("test/lorem_ipsum.txt", Default::default())?;
|
||||||
try!(zip.write_all(LOREM_IPSUM));
|
zip.write_all(LOREM_IPSUM)?;
|
||||||
|
|
||||||
try!(zip.finish());
|
zip.finish()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
const LOREM_IPSUM : &'static [u8] = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. In tellus elit, tristique vitae mattis egestas, ultricies vitae risus. Quisque sit amet quam ut urna aliquet
|
const LOREM_IPSUM : &[u8] = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. In tellus elit, tristique vitae mattis egestas, ultricies vitae risus. Quisque sit amet quam ut urna aliquet
|
||||||
molestie. Proin blandit ornare dui, a tempor nisl accumsan in. Praesent a consequat felis. Morbi metus diam, auctor in auctor vel, feugiat id odio. Curabitur ex ex,
|
molestie. Proin blandit ornare dui, a tempor nisl accumsan in. Praesent a consequat felis. Morbi metus diam, auctor in auctor vel, feugiat id odio. Curabitur ex ex,
|
||||||
dictum quis auctor quis, suscipit id lorem. Aliquam vestibulum dolor nec enim vehicula, porta tristique augue tincidunt. Vivamus ut gravida est. Sed pellentesque, dolor
|
dictum quis auctor quis, suscipit id lorem. Aliquam vestibulum dolor nec enim vehicula, porta tristique augue tincidunt. Vivamus ut gravida est. Sed pellentesque, dolor
|
||||||
vitae tristique consectetur, neque lectus pulvinar dui, sed feugiat purus diam id lectus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per
|
vitae tristique consectetur, neque lectus pulvinar dui, sed feugiat purus diam id lectus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
PROJECT_NAME=zip-rs
|
|
||||||
DOCS_REPO=mvdnes/rust-docs.git
|
|
||||||
DOC_RUST_VERSION=stable
|
|
|
@ -0,0 +1,185 @@
|
||||||
|
//! Implementation of the AES decryption for zip files.
|
||||||
|
//!
|
||||||
|
//! This was implemented according to the [WinZip specification](https://www.winzip.com/win/en/aes_info.html).
|
||||||
|
//! Note that using CRC with AES depends on the used encryption specification, AE-1 or AE-2.
|
||||||
|
//! If the file is marked as encrypted with AE-2 the CRC field is ignored, even if it isn't set to 0.
|
||||||
|
|
||||||
|
use crate::aes_ctr;
|
||||||
|
use crate::types::AesMode;
|
||||||
|
use constant_time_eq::constant_time_eq;
|
||||||
|
use hmac::{Hmac, Mac};
|
||||||
|
use sha1::Sha1;
|
||||||
|
use std::io::{self, Read};
|
||||||
|
|
||||||
|
/// The length of the password verifcation value in bytes
|
||||||
|
const PWD_VERIFY_LENGTH: usize = 2;
|
||||||
|
/// The length of the authentication code in bytes
|
||||||
|
const AUTH_CODE_LENGTH: usize = 10;
|
||||||
|
/// The number of iterations used with PBKDF2
|
||||||
|
const ITERATION_COUNT: u32 = 1000;
|
||||||
|
|
||||||
|
/// Create a AesCipher depending on the used `AesMode` and the given `key`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This panics if `key` doesn't have the correct size for the chosen aes mode.
|
||||||
|
fn cipher_from_mode(aes_mode: AesMode, key: &[u8]) -> Box<dyn aes_ctr::AesCipher> {
|
||||||
|
match aes_mode {
|
||||||
|
AesMode::Aes128 => Box::new(aes_ctr::AesCtrZipKeyStream::<aes_ctr::Aes128>::new(key))
|
||||||
|
as Box<dyn aes_ctr::AesCipher>,
|
||||||
|
AesMode::Aes192 => Box::new(aes_ctr::AesCtrZipKeyStream::<aes_ctr::Aes192>::new(key))
|
||||||
|
as Box<dyn aes_ctr::AesCipher>,
|
||||||
|
AesMode::Aes256 => Box::new(aes_ctr::AesCtrZipKeyStream::<aes_ctr::Aes256>::new(key))
|
||||||
|
as Box<dyn aes_ctr::AesCipher>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// An aes encrypted file starts with a salt, whose length depends on the used aes mode
|
||||||
|
// followed by a 2 byte password verification value
|
||||||
|
// then the variable length encrypted data
|
||||||
|
// and lastly a 10 byte authentication code
|
||||||
|
pub struct AesReader<R> {
|
||||||
|
reader: R,
|
||||||
|
aes_mode: AesMode,
|
||||||
|
data_length: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read> AesReader<R> {
|
||||||
|
pub fn new(reader: R, aes_mode: AesMode, compressed_size: u64) -> AesReader<R> {
|
||||||
|
let data_length = compressed_size
|
||||||
|
- (PWD_VERIFY_LENGTH + AUTH_CODE_LENGTH + aes_mode.salt_length()) as u64;
|
||||||
|
|
||||||
|
Self {
|
||||||
|
reader,
|
||||||
|
aes_mode,
|
||||||
|
data_length,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read the AES header bytes and validate the password.
|
||||||
|
///
|
||||||
|
/// Even if the validation succeeds, there is still a 1 in 65536 chance that an incorrect
|
||||||
|
/// password was provided.
|
||||||
|
/// It isn't possible to check the authentication code in this step. This will be done after
|
||||||
|
/// reading and decrypting the file.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// If the password verification failed `Ok(None)` will be returned to match the validate
|
||||||
|
/// method of ZipCryptoReader.
|
||||||
|
pub fn validate(mut self, password: &[u8]) -> io::Result<Option<AesReaderValid<R>>> {
|
||||||
|
let salt_length = self.aes_mode.salt_length();
|
||||||
|
let key_length = self.aes_mode.key_length();
|
||||||
|
|
||||||
|
let mut salt = vec![0; salt_length];
|
||||||
|
self.reader.read_exact(&mut salt)?;
|
||||||
|
|
||||||
|
// next are 2 bytes used for password verification
|
||||||
|
let mut pwd_verification_value = vec![0; PWD_VERIFY_LENGTH];
|
||||||
|
self.reader.read_exact(&mut pwd_verification_value)?;
|
||||||
|
|
||||||
|
// derive a key from the password and salt
|
||||||
|
// the length depends on the aes key length
|
||||||
|
let derived_key_len = 2 * key_length + PWD_VERIFY_LENGTH;
|
||||||
|
let mut derived_key: Vec<u8> = vec![0; derived_key_len];
|
||||||
|
|
||||||
|
// use PBKDF2 with HMAC-Sha1 to derive the key
|
||||||
|
pbkdf2::pbkdf2::<Hmac<Sha1>>(password, &salt, ITERATION_COUNT, &mut derived_key);
|
||||||
|
let decrypt_key = &derived_key[0..key_length];
|
||||||
|
let hmac_key = &derived_key[key_length..key_length * 2];
|
||||||
|
let pwd_verify = &derived_key[derived_key_len - 2..];
|
||||||
|
|
||||||
|
// the last 2 bytes should equal the password verification value
|
||||||
|
if pwd_verification_value != pwd_verify {
|
||||||
|
// wrong password
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let cipher = cipher_from_mode(self.aes_mode, decrypt_key);
|
||||||
|
let hmac = Hmac::<Sha1>::new_from_slice(hmac_key).unwrap();
|
||||||
|
|
||||||
|
Ok(Some(AesReaderValid {
|
||||||
|
reader: self.reader,
|
||||||
|
data_remaining: self.data_length,
|
||||||
|
cipher,
|
||||||
|
hmac,
|
||||||
|
finalized: false,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A reader for aes encrypted files, which has already passed the first password check.
|
||||||
|
///
|
||||||
|
/// There is a 1 in 65536 chance that an invalid password passes that check.
|
||||||
|
/// After the data has been read and decrypted an HMAC will be checked and provide a final means
|
||||||
|
/// to check if either the password is invalid or if the data has been changed.
|
||||||
|
pub struct AesReaderValid<R: Read> {
|
||||||
|
reader: R,
|
||||||
|
data_remaining: u64,
|
||||||
|
cipher: Box<dyn aes_ctr::AesCipher>,
|
||||||
|
hmac: Hmac<Sha1>,
|
||||||
|
finalized: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read> Read for AesReaderValid<R> {
|
||||||
|
/// This implementation does not fulfill all requirements set in the trait documentation.
|
||||||
|
///
|
||||||
|
/// ```txt
|
||||||
|
/// "If an error is returned then it must be guaranteed that no bytes were read."
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Whether this applies to errors that occur while reading the encrypted data depends on the
|
||||||
|
/// underlying reader. If the error occurs while verifying the HMAC, the reader might become
|
||||||
|
/// practically unusable, since its position after the error is not known.
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
if self.data_remaining == 0 {
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the number of bytes to read, compare as u64 to make sure we can read more than
|
||||||
|
// 2^32 bytes even on 32 bit systems.
|
||||||
|
let bytes_to_read = self.data_remaining.min(buf.len() as u64) as usize;
|
||||||
|
let read = self.reader.read(&mut buf[0..bytes_to_read])?;
|
||||||
|
self.data_remaining -= read as u64;
|
||||||
|
|
||||||
|
// Update the hmac with the encrypted data
|
||||||
|
self.hmac.update(&buf[0..read]);
|
||||||
|
|
||||||
|
// decrypt the data
|
||||||
|
self.cipher.crypt_in_place(&mut buf[0..read]);
|
||||||
|
|
||||||
|
// if there is no data left to read, check the integrity of the data
|
||||||
|
if self.data_remaining == 0 {
|
||||||
|
assert!(
|
||||||
|
!self.finalized,
|
||||||
|
"Tried to use an already finalized HMAC. This is a bug!"
|
||||||
|
);
|
||||||
|
self.finalized = true;
|
||||||
|
|
||||||
|
// Zip uses HMAC-Sha1-80, which only uses the first half of the hash
|
||||||
|
// see https://www.winzip.com/win/en/aes_info.html#auth-faq
|
||||||
|
let mut read_auth_code = [0; AUTH_CODE_LENGTH];
|
||||||
|
self.reader.read_exact(&mut read_auth_code)?;
|
||||||
|
let computed_auth_code = &self.hmac.finalize_reset().into_bytes()[0..AUTH_CODE_LENGTH];
|
||||||
|
|
||||||
|
// use constant time comparison to mitigate timing attacks
|
||||||
|
if !constant_time_eq(computed_auth_code, &read_auth_code) {
|
||||||
|
return Err(
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::InvalidData,
|
||||||
|
"Invalid authentication code, this could be due to an invalid password or errors in the data"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(read)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read> AesReaderValid<R> {
|
||||||
|
/// Consumes this decoder, returning the underlying reader.
|
||||||
|
pub fn into_inner(self) -> R {
|
||||||
|
self.reader
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,281 @@
|
||||||
|
//! A counter mode (CTR) for AES to work with the encryption used in zip files.
|
||||||
|
//!
|
||||||
|
//! This was implemented since the zip specification requires the mode to not use a nonce and uses a
|
||||||
|
//! different byte order (little endian) than NIST (big endian).
|
||||||
|
//! See [AesCtrZipKeyStream](./struct.AesCtrZipKeyStream.html) for more information.
|
||||||
|
|
||||||
|
use aes::cipher::generic_array::GenericArray;
|
||||||
|
use aes::{BlockEncrypt, NewBlockCipher};
|
||||||
|
use byteorder::WriteBytesExt;
|
||||||
|
use std::{any, fmt};
|
||||||
|
|
||||||
|
/// Internal block size of an AES cipher.
|
||||||
|
const AES_BLOCK_SIZE: usize = 16;
|
||||||
|
|
||||||
|
/// AES-128.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Aes128;
|
||||||
|
/// AES-192
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Aes192;
|
||||||
|
/// AES-256.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Aes256;
|
||||||
|
|
||||||
|
/// An AES cipher kind.
|
||||||
|
pub trait AesKind {
|
||||||
|
/// Key type.
|
||||||
|
type Key: AsRef<[u8]>;
|
||||||
|
/// Cipher used to decrypt.
|
||||||
|
type Cipher;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AesKind for Aes128 {
|
||||||
|
type Key = [u8; 16];
|
||||||
|
type Cipher = aes::Aes128;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AesKind for Aes192 {
|
||||||
|
type Key = [u8; 24];
|
||||||
|
type Cipher = aes::Aes192;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AesKind for Aes256 {
|
||||||
|
type Key = [u8; 32];
|
||||||
|
type Cipher = aes::Aes256;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An AES-CTR key stream generator.
|
||||||
|
///
|
||||||
|
/// Implements the slightly non-standard AES-CTR variant used by WinZip AES encryption.
|
||||||
|
///
|
||||||
|
/// Typical AES-CTR implementations combine a nonce with a 64 bit counter. WinZIP AES instead uses
|
||||||
|
/// no nonce and also uses a different byte order (little endian) than NIST (big endian).
|
||||||
|
///
|
||||||
|
/// The stream implements the `Read` trait; encryption or decryption is performed by XOR-ing the
|
||||||
|
/// bytes from the key stream with the ciphertext/plaintext.
|
||||||
|
pub struct AesCtrZipKeyStream<C: AesKind> {
|
||||||
|
/// Current AES counter.
|
||||||
|
counter: u128,
|
||||||
|
/// AES cipher instance.
|
||||||
|
cipher: C::Cipher,
|
||||||
|
/// Stores the currently available keystream bytes.
|
||||||
|
buffer: [u8; AES_BLOCK_SIZE],
|
||||||
|
/// Number of bytes already used up from `buffer`.
|
||||||
|
pos: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C> fmt::Debug for AesCtrZipKeyStream<C>
|
||||||
|
where
|
||||||
|
C: AesKind,
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"AesCtrZipKeyStream<{}>(counter: {})",
|
||||||
|
any::type_name::<C>(),
|
||||||
|
self.counter
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C> AesCtrZipKeyStream<C>
|
||||||
|
where
|
||||||
|
C: AesKind,
|
||||||
|
C::Cipher: NewBlockCipher,
|
||||||
|
{
|
||||||
|
/// Creates a new zip variant AES-CTR key stream.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This panics if `key` doesn't have the correct size for cipher `C`.
|
||||||
|
pub fn new(key: &[u8]) -> AesCtrZipKeyStream<C> {
|
||||||
|
AesCtrZipKeyStream {
|
||||||
|
counter: 1,
|
||||||
|
cipher: C::Cipher::new(GenericArray::from_slice(key)),
|
||||||
|
buffer: [0u8; AES_BLOCK_SIZE],
|
||||||
|
pos: AES_BLOCK_SIZE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C> AesCipher for AesCtrZipKeyStream<C>
|
||||||
|
where
|
||||||
|
C: AesKind,
|
||||||
|
C::Cipher: BlockEncrypt,
|
||||||
|
{
|
||||||
|
/// Decrypt or encrypt `target`.
|
||||||
|
#[inline]
|
||||||
|
fn crypt_in_place(&mut self, mut target: &mut [u8]) {
|
||||||
|
while !target.is_empty() {
|
||||||
|
if self.pos == AES_BLOCK_SIZE {
|
||||||
|
// Note: AES block size is always 16 bytes, same as u128.
|
||||||
|
self.buffer
|
||||||
|
.as_mut()
|
||||||
|
.write_u128::<byteorder::LittleEndian>(self.counter)
|
||||||
|
.expect("did not expect u128 le conversion to fail");
|
||||||
|
self.cipher
|
||||||
|
.encrypt_block(GenericArray::from_mut_slice(&mut self.buffer));
|
||||||
|
self.counter += 1;
|
||||||
|
self.pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let target_len = target.len().min(AES_BLOCK_SIZE - self.pos);
|
||||||
|
|
||||||
|
xor(
|
||||||
|
&mut target[0..target_len],
|
||||||
|
&self.buffer[self.pos..(self.pos + target_len)],
|
||||||
|
);
|
||||||
|
target = &mut target[target_len..];
|
||||||
|
self.pos += target_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This trait allows using generic AES ciphers with different key sizes.
|
||||||
|
pub trait AesCipher {
|
||||||
|
fn crypt_in_place(&mut self, target: &mut [u8]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// XORs a slice in place with another slice.
|
||||||
|
#[inline]
|
||||||
|
fn xor(dest: &mut [u8], src: &[u8]) {
|
||||||
|
assert_eq!(dest.len(), src.len());
|
||||||
|
|
||||||
|
for (lhs, rhs) in dest.iter_mut().zip(src.iter()) {
|
||||||
|
*lhs ^= *rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{Aes128, Aes192, Aes256, AesCipher, AesCtrZipKeyStream, AesKind};
|
||||||
|
use aes::{BlockEncrypt, NewBlockCipher};
|
||||||
|
|
||||||
|
/// Checks whether `crypt_in_place` produces the correct plaintext after one use and yields the
|
||||||
|
/// cipertext again after applying it again.
|
||||||
|
fn roundtrip<Aes>(key: &[u8], ciphertext: &mut [u8], expected_plaintext: &[u8])
|
||||||
|
where
|
||||||
|
Aes: AesKind,
|
||||||
|
Aes::Cipher: NewBlockCipher + BlockEncrypt,
|
||||||
|
{
|
||||||
|
let mut key_stream = AesCtrZipKeyStream::<Aes>::new(key);
|
||||||
|
|
||||||
|
let mut plaintext: Vec<u8> = ciphertext.to_vec();
|
||||||
|
key_stream.crypt_in_place(plaintext.as_mut_slice());
|
||||||
|
assert_eq!(plaintext, expected_plaintext.to_vec());
|
||||||
|
|
||||||
|
// Round-tripping should yield the ciphertext again.
|
||||||
|
let mut key_stream = AesCtrZipKeyStream::<Aes>::new(key);
|
||||||
|
key_stream.crypt_in_place(&mut plaintext);
|
||||||
|
assert_eq!(plaintext, ciphertext.to_vec());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn new_with_wrong_key_size() {
|
||||||
|
AesCtrZipKeyStream::<Aes128>::new(&[1, 2, 3, 4, 5]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The data used in these tests was generated with p7zip without any compression.
|
||||||
|
// It's not possible to recreate the exact same data, since a random salt is used for encryption.
|
||||||
|
// `7z a -phelloworld -mem=AES256 -mx=0 aes256_40byte.zip 40byte_data.txt`
|
||||||
|
#[test]
|
||||||
|
fn crypt_aes_256_0_byte() {
|
||||||
|
let mut ciphertext = [];
|
||||||
|
let expected_plaintext = &[];
|
||||||
|
let key = [
|
||||||
|
0x0b, 0xec, 0x2e, 0xf2, 0x46, 0xf0, 0x7e, 0x35, 0x16, 0x54, 0xe0, 0x98, 0x10, 0xb3,
|
||||||
|
0x18, 0x55, 0x24, 0xa3, 0x9e, 0x0e, 0x40, 0xe7, 0x92, 0xad, 0xb2, 0x8a, 0x48, 0xf4,
|
||||||
|
0x5c, 0xd0, 0xc0, 0x54,
|
||||||
|
];
|
||||||
|
|
||||||
|
roundtrip::<Aes256>(&key, &mut ciphertext, expected_plaintext);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn crypt_aes_128_5_byte() {
|
||||||
|
let mut ciphertext = [0x98, 0xa9, 0x8c, 0x26, 0x0e];
|
||||||
|
let expected_plaintext = b"asdf\n";
|
||||||
|
let key = [
|
||||||
|
0xe0, 0x25, 0x7b, 0x57, 0x97, 0x6a, 0xa4, 0x23, 0xab, 0x94, 0xaa, 0x44, 0xfd, 0x47,
|
||||||
|
0x4f, 0xa5,
|
||||||
|
];
|
||||||
|
|
||||||
|
roundtrip::<Aes128>(&key, &mut ciphertext, expected_plaintext);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn crypt_aes_192_5_byte() {
|
||||||
|
let mut ciphertext = [0x36, 0x55, 0x5c, 0x61, 0x3c];
|
||||||
|
let expected_plaintext = b"asdf\n";
|
||||||
|
let key = [
|
||||||
|
0xe4, 0x4a, 0x88, 0x52, 0x8f, 0xf7, 0x0b, 0x81, 0x7b, 0x75, 0xf1, 0x74, 0x21, 0x37,
|
||||||
|
0x8c, 0x90, 0xad, 0xbe, 0x4a, 0x65, 0xa8, 0x96, 0x0e, 0xcc,
|
||||||
|
];
|
||||||
|
|
||||||
|
roundtrip::<Aes192>(&key, &mut ciphertext, expected_plaintext);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn crypt_aes_256_5_byte() {
|
||||||
|
let mut ciphertext = [0xc2, 0x47, 0xc0, 0xdc, 0x56];
|
||||||
|
let expected_plaintext = b"asdf\n";
|
||||||
|
let key = [
|
||||||
|
0x79, 0x5e, 0x17, 0xf2, 0xc6, 0x3d, 0x28, 0x9b, 0x4b, 0x4b, 0xbb, 0xa9, 0xba, 0xc9,
|
||||||
|
0xa5, 0xee, 0x3a, 0x4f, 0x0f, 0x4b, 0x29, 0xbd, 0xe9, 0xb8, 0x41, 0x9c, 0x41, 0xa5,
|
||||||
|
0x15, 0xb2, 0x86, 0xab,
|
||||||
|
];
|
||||||
|
|
||||||
|
roundtrip::<Aes256>(&key, &mut ciphertext, expected_plaintext);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn crypt_aes_128_40_byte() {
|
||||||
|
let mut ciphertext = [
|
||||||
|
0xcf, 0x72, 0x6b, 0xa1, 0xb2, 0x0f, 0xdf, 0xaa, 0x10, 0xad, 0x9c, 0x7f, 0x6d, 0x1c,
|
||||||
|
0x8d, 0xb5, 0x16, 0x7e, 0xbb, 0x11, 0x69, 0x52, 0x8c, 0x89, 0x80, 0x32, 0xaa, 0x76,
|
||||||
|
0xa6, 0x18, 0x31, 0x98, 0xee, 0xdd, 0x22, 0x68, 0xb7, 0xe6, 0x77, 0xd2,
|
||||||
|
];
|
||||||
|
let expected_plaintext = b"Lorem ipsum dolor sit amet, consectetur\n";
|
||||||
|
let key = [
|
||||||
|
0x43, 0x2b, 0x6d, 0xbe, 0x05, 0x76, 0x6c, 0x9e, 0xde, 0xca, 0x3b, 0xf8, 0xaf, 0x5d,
|
||||||
|
0x81, 0xb6,
|
||||||
|
];
|
||||||
|
|
||||||
|
roundtrip::<Aes128>(&key, &mut ciphertext, expected_plaintext);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn crypt_aes_192_40_byte() {
|
||||||
|
let mut ciphertext = [
|
||||||
|
0xa6, 0xfc, 0x52, 0x79, 0x2c, 0x6c, 0xfe, 0x68, 0xb1, 0xa8, 0xb3, 0x07, 0x52, 0x8b,
|
||||||
|
0x82, 0xa6, 0x87, 0x9c, 0x72, 0x42, 0x3a, 0xf8, 0xc6, 0xa9, 0xc9, 0xfb, 0x61, 0x19,
|
||||||
|
0x37, 0xb9, 0x56, 0x62, 0xf4, 0xfc, 0x5e, 0x7a, 0xdd, 0x55, 0x0a, 0x48,
|
||||||
|
];
|
||||||
|
let expected_plaintext = b"Lorem ipsum dolor sit amet, consectetur\n";
|
||||||
|
let key = [
|
||||||
|
0xac, 0x92, 0x41, 0xba, 0xde, 0xd9, 0x02, 0xfe, 0x40, 0x92, 0x20, 0xf6, 0x56, 0x03,
|
||||||
|
0xfe, 0xae, 0x1b, 0xba, 0x01, 0x97, 0x97, 0x79, 0xbb, 0xa6,
|
||||||
|
];
|
||||||
|
|
||||||
|
roundtrip::<Aes192>(&key, &mut ciphertext, expected_plaintext);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn crypt_aes_256_40_byte() {
|
||||||
|
let mut ciphertext = [
|
||||||
|
0xa9, 0x99, 0xbd, 0xea, 0x82, 0x9b, 0x8f, 0x2f, 0xb7, 0x52, 0x2f, 0x6b, 0xd8, 0xf6,
|
||||||
|
0xab, 0x0e, 0x24, 0x51, 0x9e, 0x18, 0x0f, 0xc0, 0x8f, 0x54, 0x15, 0x80, 0xae, 0xbc,
|
||||||
|
0xa0, 0x5c, 0x8a, 0x11, 0x8d, 0x14, 0x7e, 0xc5, 0xb4, 0xae, 0xd3, 0x37,
|
||||||
|
];
|
||||||
|
let expected_plaintext = b"Lorem ipsum dolor sit amet, consectetur\n";
|
||||||
|
let key = [
|
||||||
|
0x64, 0x7c, 0x7a, 0xde, 0xf0, 0xf2, 0x61, 0x49, 0x1c, 0xf1, 0xf1, 0xe3, 0x37, 0xfc,
|
||||||
|
0xe1, 0x4d, 0x4a, 0x77, 0xd4, 0xeb, 0x9e, 0x3d, 0x75, 0xce, 0x9a, 0x3e, 0x10, 0x50,
|
||||||
|
0xc2, 0x07, 0x36, 0xb6,
|
||||||
|
];
|
||||||
|
|
||||||
|
roundtrip::<Aes256>(&key, &mut ciphertext, expected_plaintext);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,99 +2,192 @@
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
/// Compression methods for the contents of a ZIP file.
|
#[allow(deprecated)]
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
/// Identifies the storage format used to compress a file within a ZIP archive.
|
||||||
pub enum CompressionMethod
|
///
|
||||||
{
|
/// Each file's compression method is stored alongside it, allowing the
|
||||||
/// The file is stored (no compression)
|
/// contents to be read without context.
|
||||||
|
///
|
||||||
|
/// When creating ZIP files, you may choose the method to use with
|
||||||
|
/// [`crate::write::FileOptions::compression_method`]
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum CompressionMethod {
|
||||||
|
/// Store the file as is
|
||||||
Stored,
|
Stored,
|
||||||
/// The file is Deflated
|
/// Compress the file using Deflate
|
||||||
#[cfg(feature = "flate2")]
|
#[cfg(any(
|
||||||
|
feature = "deflate",
|
||||||
|
feature = "deflate-miniz",
|
||||||
|
feature = "deflate-zlib"
|
||||||
|
))]
|
||||||
Deflated,
|
Deflated,
|
||||||
/// File is compressed using BZIP2 algorithm
|
/// Compress the file using BZIP2
|
||||||
#[cfg(feature = "bzip2")]
|
#[cfg(feature = "bzip2")]
|
||||||
Bzip2,
|
Bzip2,
|
||||||
|
/// Encrypted using AES.
|
||||||
|
///
|
||||||
|
/// The actual compression method has to be taken from the AES extra data field
|
||||||
|
/// or from `ZipFileData`.
|
||||||
|
#[cfg(feature = "aes-crypto")]
|
||||||
|
Aes,
|
||||||
|
/// Compress the file using ZStandard
|
||||||
|
#[cfg(feature = "zstd")]
|
||||||
|
Zstd,
|
||||||
/// Unsupported compression method
|
/// Unsupported compression method
|
||||||
|
#[deprecated(since = "0.5.7", note = "use the constants instead")]
|
||||||
Unsupported(u16),
|
Unsupported(u16),
|
||||||
}
|
}
|
||||||
|
#[allow(deprecated, missing_docs)]
|
||||||
|
/// All compression methods defined for the ZIP format
|
||||||
|
impl CompressionMethod {
|
||||||
|
pub const STORE: Self = CompressionMethod::Stored;
|
||||||
|
pub const SHRINK: Self = CompressionMethod::Unsupported(1);
|
||||||
|
pub const REDUCE_1: Self = CompressionMethod::Unsupported(2);
|
||||||
|
pub const REDUCE_2: Self = CompressionMethod::Unsupported(3);
|
||||||
|
pub const REDUCE_3: Self = CompressionMethod::Unsupported(4);
|
||||||
|
pub const REDUCE_4: Self = CompressionMethod::Unsupported(5);
|
||||||
|
pub const IMPLODE: Self = CompressionMethod::Unsupported(6);
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "deflate",
|
||||||
|
feature = "deflate-miniz",
|
||||||
|
feature = "deflate-zlib"
|
||||||
|
))]
|
||||||
|
pub const DEFLATE: Self = CompressionMethod::Deflated;
|
||||||
|
#[cfg(not(any(
|
||||||
|
feature = "deflate",
|
||||||
|
feature = "deflate-miniz",
|
||||||
|
feature = "deflate-zlib"
|
||||||
|
)))]
|
||||||
|
pub const DEFLATE: Self = CompressionMethod::Unsupported(8);
|
||||||
|
pub const DEFLATE64: Self = CompressionMethod::Unsupported(9);
|
||||||
|
pub const PKWARE_IMPLODE: Self = CompressionMethod::Unsupported(10);
|
||||||
|
#[cfg(feature = "bzip2")]
|
||||||
|
pub const BZIP2: Self = CompressionMethod::Bzip2;
|
||||||
|
#[cfg(not(feature = "bzip2"))]
|
||||||
|
pub const BZIP2: Self = CompressionMethod::Unsupported(12);
|
||||||
|
pub const LZMA: Self = CompressionMethod::Unsupported(14);
|
||||||
|
pub const IBM_ZOS_CMPSC: Self = CompressionMethod::Unsupported(16);
|
||||||
|
pub const IBM_TERSE: Self = CompressionMethod::Unsupported(18);
|
||||||
|
pub const ZSTD_DEPRECATED: Self = CompressionMethod::Unsupported(20);
|
||||||
|
#[cfg(feature = "zstd")]
|
||||||
|
pub const ZSTD: Self = CompressionMethod::Zstd;
|
||||||
|
#[cfg(not(feature = "zstd"))]
|
||||||
|
pub const ZSTD: Self = CompressionMethod::Unsupported(93);
|
||||||
|
pub const MP3: Self = CompressionMethod::Unsupported(94);
|
||||||
|
pub const XZ: Self = CompressionMethod::Unsupported(95);
|
||||||
|
pub const JPEG: Self = CompressionMethod::Unsupported(96);
|
||||||
|
pub const WAVPACK: Self = CompressionMethod::Unsupported(97);
|
||||||
|
pub const PPMD: Self = CompressionMethod::Unsupported(98);
|
||||||
|
#[cfg(feature = "aes-crypto")]
|
||||||
|
pub const AES: Self = CompressionMethod::Aes;
|
||||||
|
#[cfg(not(feature = "aes-crypto"))]
|
||||||
|
pub const AES: Self = CompressionMethod::Unsupported(99);
|
||||||
|
}
|
||||||
impl CompressionMethod {
|
impl CompressionMethod {
|
||||||
/// Converts an u16 to its corresponding CompressionMethod
|
/// Converts an u16 to its corresponding CompressionMethod
|
||||||
|
#[deprecated(
|
||||||
|
since = "0.5.7",
|
||||||
|
note = "use a constant to construct a compression method"
|
||||||
|
)]
|
||||||
pub fn from_u16(val: u16) -> CompressionMethod {
|
pub fn from_u16(val: u16) -> CompressionMethod {
|
||||||
|
#[allow(deprecated)]
|
||||||
match val {
|
match val {
|
||||||
0 => CompressionMethod::Stored,
|
0 => CompressionMethod::Stored,
|
||||||
#[cfg(feature = "flate2")]
|
#[cfg(any(
|
||||||
|
feature = "deflate",
|
||||||
|
feature = "deflate-miniz",
|
||||||
|
feature = "deflate-zlib"
|
||||||
|
))]
|
||||||
8 => CompressionMethod::Deflated,
|
8 => CompressionMethod::Deflated,
|
||||||
#[cfg(feature = "bzip2")]
|
#[cfg(feature = "bzip2")]
|
||||||
12 => CompressionMethod::Bzip2,
|
12 => CompressionMethod::Bzip2,
|
||||||
|
#[cfg(feature = "zstd")]
|
||||||
|
93 => CompressionMethod::Zstd,
|
||||||
|
#[cfg(feature = "aes-crypto")]
|
||||||
|
99 => CompressionMethod::Aes,
|
||||||
|
|
||||||
v => CompressionMethod::Unsupported(v),
|
v => CompressionMethod::Unsupported(v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a CompressionMethod to a u16
|
/// Converts a CompressionMethod to a u16
|
||||||
|
#[deprecated(
|
||||||
|
since = "0.5.7",
|
||||||
|
note = "to match on other compression methods, use a constant"
|
||||||
|
)]
|
||||||
pub fn to_u16(self) -> u16 {
|
pub fn to_u16(self) -> u16 {
|
||||||
|
#[allow(deprecated)]
|
||||||
match self {
|
match self {
|
||||||
CompressionMethod::Stored => 0,
|
CompressionMethod::Stored => 0,
|
||||||
#[cfg(feature = "flate2")]
|
#[cfg(any(
|
||||||
|
feature = "deflate",
|
||||||
|
feature = "deflate-miniz",
|
||||||
|
feature = "deflate-zlib"
|
||||||
|
))]
|
||||||
CompressionMethod::Deflated => 8,
|
CompressionMethod::Deflated => 8,
|
||||||
#[cfg(feature = "bzip2")]
|
#[cfg(feature = "bzip2")]
|
||||||
CompressionMethod::Bzip2 => 12,
|
CompressionMethod::Bzip2 => 12,
|
||||||
|
#[cfg(feature = "aes-crypto")]
|
||||||
|
CompressionMethod::Aes => 99,
|
||||||
|
#[cfg(feature = "zstd")]
|
||||||
|
CompressionMethod::Zstd => 93,
|
||||||
|
|
||||||
CompressionMethod::Unsupported(v) => v,
|
CompressionMethod::Unsupported(v) => v,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for CompressionMethod {
|
impl fmt::Display for CompressionMethod {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
// Just duplicate what the Debug format looks like, i.e, the enum key:
|
// Just duplicate what the Debug format looks like, i.e, the enum key:
|
||||||
write!(f, "{:?}", self)
|
write!(f, "{:?}", self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The compression methods which have been implemented.
|
||||||
|
pub const SUPPORTED_COMPRESSION_METHODS: &[CompressionMethod] = &[
|
||||||
|
CompressionMethod::Stored,
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "deflate",
|
||||||
|
feature = "deflate-miniz",
|
||||||
|
feature = "deflate-zlib"
|
||||||
|
))]
|
||||||
|
CompressionMethod::Deflated,
|
||||||
|
#[cfg(feature = "bzip2")]
|
||||||
|
CompressionMethod::Bzip2,
|
||||||
|
#[cfg(feature = "zstd")]
|
||||||
|
CompressionMethod::Zstd,
|
||||||
|
];
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::CompressionMethod;
|
use super::{CompressionMethod, SUPPORTED_COMPRESSION_METHODS};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_eq_to() {
|
fn from_eq_to() {
|
||||||
for v in 0..(::std::u16::MAX as u32 + 1)
|
for v in 0..(u16::MAX as u32 + 1) {
|
||||||
{
|
#[allow(deprecated)]
|
||||||
let from = CompressionMethod::from_u16(v as u16);
|
let from = CompressionMethod::from_u16(v as u16);
|
||||||
|
#[allow(deprecated)]
|
||||||
let to = from.to_u16() as u32;
|
let to = from.to_u16() as u32;
|
||||||
assert_eq!(v, to);
|
assert_eq!(v, to);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(not(feature = "bzip2"), feature = "flate2"))]
|
|
||||||
fn methods() -> Vec<CompressionMethod> {
|
|
||||||
vec![CompressionMethod::Stored, CompressionMethod::Deflated]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(not(feature = "flate2"), feature = "bzip2"))]
|
|
||||||
fn methods() -> Vec<CompressionMethod> {
|
|
||||||
vec![CompressionMethod::Stored, CompressionMethod::Bzip2]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(feature = "bzip2", feature = "flate2"))]
|
|
||||||
fn methods() -> Vec<CompressionMethod> {
|
|
||||||
vec![CompressionMethod::Stored, CompressionMethod::Deflated, CompressionMethod::Bzip2]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(not(feature = "bzip2"), not(feature = "flate2")))]
|
|
||||||
fn methods() -> Vec<CompressionMethod> {
|
|
||||||
vec![CompressionMethod::Stored]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn to_eq_from() {
|
fn to_eq_from() {
|
||||||
fn check_match(method: CompressionMethod) {
|
fn check_match(method: CompressionMethod) {
|
||||||
|
#[allow(deprecated)]
|
||||||
let to = method.to_u16();
|
let to = method.to_u16();
|
||||||
|
#[allow(deprecated)]
|
||||||
let from = CompressionMethod::from_u16(to);
|
let from = CompressionMethod::from_u16(to);
|
||||||
|
#[allow(deprecated)]
|
||||||
let back = from.to_u16();
|
let back = from.to_u16();
|
||||||
assert_eq!(to, back);
|
assert_eq!(to, back);
|
||||||
}
|
}
|
||||||
|
|
||||||
for method in methods() {
|
for &method in SUPPORTED_COMPRESSION_METHODS {
|
||||||
check_match(method);
|
check_match(method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,7 +200,7 @@ mod test {
|
||||||
assert_eq!(debug_str, display_str);
|
assert_eq!(debug_str, display_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
for method in methods() {
|
for &method in SUPPORTED_COMPRESSION_METHODS {
|
||||||
check_match(method);
|
check_match(method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,19 +6,18 @@ pub trait FromCp437 {
|
||||||
type Target;
|
type Target;
|
||||||
|
|
||||||
/// Function that does the conversion from cp437.
|
/// Function that does the conversion from cp437.
|
||||||
/// Gennerally allocations will be avoided if all data falls into the ASCII range.
|
/// Generally allocations will be avoided if all data falls into the ASCII range.
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
fn from_cp437(self) -> Self::Target;
|
fn from_cp437(self) -> Self::Target;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FromCp437 for &'a [u8] {
|
impl<'a> FromCp437 for &'a [u8] {
|
||||||
type Target = ::std::borrow::Cow<'a, str>;
|
type Target = ::std::borrow::Cow<'a, str>;
|
||||||
|
|
||||||
fn from_cp437(self) -> Self::Target
|
fn from_cp437(self) -> Self::Target {
|
||||||
{
|
|
||||||
if self.iter().all(|c| *c < 0x80) {
|
if self.iter().all(|c| *c < 0x80) {
|
||||||
::std::str::from_utf8(self).unwrap().into()
|
::std::str::from_utf8(self).unwrap().into()
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
self.iter().map(|c| to_char(*c)).collect::<String>().into()
|
self.iter().map(|c| to_char(*c)).collect::<String>().into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,18 +29,15 @@ impl FromCp437 for Vec<u8> {
|
||||||
fn from_cp437(self) -> Self::Target {
|
fn from_cp437(self) -> Self::Target {
|
||||||
if self.iter().all(|c| *c < 0x80) {
|
if self.iter().all(|c| *c < 0x80) {
|
||||||
String::from_utf8(self).unwrap()
|
String::from_utf8(self).unwrap()
|
||||||
}
|
} else {
|
||||||
else {
|
self.into_iter().map(to_char).collect()
|
||||||
self.into_iter().map(|c| to_char(c)).collect()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_char(input: u8) -> char
|
fn to_char(input: u8) -> char {
|
||||||
{
|
let output = match input {
|
||||||
let output = match input
|
0x00..=0x7f => input as u32,
|
||||||
{
|
|
||||||
0x00 ... 0x7f => input as u32,
|
|
||||||
0x80 => 0x00c7,
|
0x80 => 0x00c7,
|
||||||
0x81 => 0x00fc,
|
0x81 => 0x00fc,
|
||||||
0x82 => 0x00e9,
|
0x82 => 0x00e9,
|
||||||
|
@ -170,26 +166,22 @@ fn to_char(input: u8) -> char
|
||||||
0xfd => 0x00b2,
|
0xfd => 0x00b2,
|
||||||
0xfe => 0x25a0,
|
0xfe => 0x25a0,
|
||||||
0xff => 0x00a0,
|
0xff => 0x00a0,
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
};
|
||||||
::std::char::from_u32(output).unwrap()
|
::std::char::from_u32(output).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test
|
mod test {
|
||||||
{
|
|
||||||
#[test]
|
#[test]
|
||||||
fn to_char_valid()
|
fn to_char_valid() {
|
||||||
{
|
for i in 0x00_u32..0x100 {
|
||||||
for i in 0x00_u32 .. 0x100
|
|
||||||
{
|
|
||||||
super::to_char(i as u8);
|
super::to_char(i as u8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ascii() {
|
fn ascii() {
|
||||||
for i in 0x00 .. 0x80 {
|
for i in 0x00..0x80 {
|
||||||
assert_eq!(super::to_char(i), i as char);
|
assert_eq!(super::to_char(i), i as char);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,89 +3,32 @@
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
|
|
||||||
const CRC32_TABLE : [u32; 256] = [
|
use crc32fast::Hasher;
|
||||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
|
|
||||||
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
|
||||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
|
|
||||||
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
|
||||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
|
|
||||||
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
|
||||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
|
|
||||||
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
|
||||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
|
|
||||||
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
|
||||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
|
|
||||||
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
|
||||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
|
|
||||||
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
|
||||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
|
|
||||||
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
|
||||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
|
|
||||||
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
|
||||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
|
|
||||||
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
|
||||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
|
|
||||||
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
|
||||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
|
|
||||||
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
|
||||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
|
|
||||||
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
|
||||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
|
|
||||||
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
|
||||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
|
|
||||||
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
|
||||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
|
|
||||||
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
|
||||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
|
|
||||||
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
|
||||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
|
||||||
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
|
||||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
|
|
||||||
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
|
||||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
|
|
||||||
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
|
||||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
|
|
||||||
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
|
||||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
|
||||||
];
|
|
||||||
|
|
||||||
/// Update the checksum prev based upon the contents of buf.
|
|
||||||
pub fn update(prev: u32, buf: &[u8]) -> u32
|
|
||||||
{
|
|
||||||
let mut crc = !prev;
|
|
||||||
|
|
||||||
for &byte in buf.iter()
|
|
||||||
{
|
|
||||||
crc = CRC32_TABLE[((crc as u8) ^ byte) as usize] ^ (crc >> 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
!crc
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reader that validates the CRC32 when it reaches the EOF.
|
/// Reader that validates the CRC32 when it reaches the EOF.
|
||||||
pub struct Crc32Reader<R>
|
pub struct Crc32Reader<R> {
|
||||||
{
|
|
||||||
inner: R,
|
inner: R,
|
||||||
crc: u32,
|
hasher: Hasher,
|
||||||
check: u32,
|
check: u32,
|
||||||
|
/// Signals if `inner` stores aes encrypted data.
|
||||||
|
/// AE-2 encrypted data doesn't use crc and sets the value to 0.
|
||||||
|
ae2_encrypted: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R> Crc32Reader<R>
|
impl<R> Crc32Reader<R> {
|
||||||
{
|
/// Get a new Crc32Reader which checks the inner reader against checksum.
|
||||||
/// Get a new Crc32Reader which check the inner reader against checksum.
|
/// The check is disabled if `ae2_encrypted == true`.
|
||||||
pub fn new(inner: R, checksum: u32) -> Crc32Reader<R>
|
pub(crate) fn new(inner: R, checksum: u32, ae2_encrypted: bool) -> Crc32Reader<R> {
|
||||||
{
|
Crc32Reader {
|
||||||
Crc32Reader
|
inner,
|
||||||
{
|
hasher: Hasher::new(),
|
||||||
inner: inner,
|
|
||||||
crc: 0,
|
|
||||||
check: checksum,
|
check: checksum,
|
||||||
|
ae2_encrypted,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_matches(&self) -> bool
|
fn check_matches(&self) -> bool {
|
||||||
{
|
self.check == self.hasher.clone().finalize()
|
||||||
self.check == self.crc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_inner(self) -> R {
|
pub fn into_inner(self) -> R {
|
||||||
|
@ -93,36 +36,65 @@ impl<R> Crc32Reader<R>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read> Read for Crc32Reader<R>
|
impl<R: Read> Read for Crc32Reader<R> {
|
||||||
{
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>
|
let invalid_check = !buf.is_empty() && !self.check_matches() && !self.ae2_encrypted;
|
||||||
{
|
|
||||||
let count = match self.inner.read(buf)
|
let count = match self.inner.read(buf) {
|
||||||
{
|
Ok(0) if invalid_check => {
|
||||||
Ok(0) if !self.check_matches() => { return Err(io::Error::new(io::ErrorKind::Other, "Invalid checksum")) },
|
return Err(io::Error::new(io::ErrorKind::Other, "Invalid checksum"))
|
||||||
|
}
|
||||||
Ok(n) => n,
|
Ok(n) => n,
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
};
|
};
|
||||||
self.crc = update(self.crc, &buf[0..count]);
|
self.hasher.update(&buf[0..count]);
|
||||||
Ok(count)
|
Ok(count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn samples() {
|
fn test_empty_reader() {
|
||||||
assert_eq!(super::update(0, b""), 0);
|
let data: &[u8] = b"";
|
||||||
|
let mut buf = [0; 1];
|
||||||
|
|
||||||
// test vectors from the iPXE project (input and output are bitwise negated)
|
let mut reader = Crc32Reader::new(data, 0, false);
|
||||||
assert_eq!(super::update(!0x12345678, b""), !0x12345678);
|
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||||
assert_eq!(super::update(!0xffffffff, b"hello world"), !0xf2b5ee7a);
|
|
||||||
assert_eq!(super::update(!0xffffffff, b"hello"), !0xc9ef5979);
|
|
||||||
assert_eq!(super::update(!0xc9ef5979, b" world"), !0xf2b5ee7a);
|
|
||||||
|
|
||||||
// Some vectors found on Rosetta code
|
let mut reader = Crc32Reader::new(data, 1, false);
|
||||||
assert_eq!(super::update(0, b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), 0x190A55AD);
|
assert!(reader
|
||||||
assert_eq!(super::update(0, b"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"), 0xFF6CAB0B);
|
.read(&mut buf)
|
||||||
assert_eq!(super::update(0, b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"), 0x91267E8A);
|
.unwrap_err()
|
||||||
|
.to_string()
|
||||||
|
.contains("Invalid checksum"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_byte_by_byte() {
|
||||||
|
let data: &[u8] = b"1234";
|
||||||
|
let mut buf = [0; 1];
|
||||||
|
|
||||||
|
let mut reader = Crc32Reader::new(data, 0x9be3e0a3, false);
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 1);
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 1);
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 1);
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 1);
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||||
|
// Can keep reading 0 bytes after the end
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_zero_read() {
|
||||||
|
let data: &[u8] = b"1234";
|
||||||
|
let mut buf = [0; 5];
|
||||||
|
|
||||||
|
let mut reader = Crc32Reader::new(data, 0x9be3e0a3, false);
|
||||||
|
assert_eq!(reader.read(&mut buf[..0]).unwrap(), 0);
|
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,44 @@
|
||||||
//! A basic ZipReader/Writer crate
|
//! A library for reading and writing ZIP archives.
|
||||||
|
//! ZIP is a format designed for cross-platform file "archiving".
|
||||||
|
//! That is, storing a collection of files in a single datastream
|
||||||
|
//! to make them easier to share between computers.
|
||||||
|
//! Additionally, ZIP is able to compress and encrypt files in its
|
||||||
|
//! archives.
|
||||||
|
//!
|
||||||
|
//! The current implementation is based on [PKWARE's APPNOTE.TXT v6.3.9](https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT)
|
||||||
|
//!
|
||||||
|
//! ---
|
||||||
|
//!
|
||||||
|
//! [`zip`](`crate`) has support for the most common ZIP archives found in common use.
|
||||||
|
//! However, in special cases,
|
||||||
|
//! there are some zip archives that are difficult to read or write.
|
||||||
|
//!
|
||||||
|
//! This is a list of supported features:
|
||||||
|
//!
|
||||||
|
//! | | Reading | Writing |
|
||||||
|
//! | ------- | ------ | ------- |
|
||||||
|
//! | Deflate | ✅ [->](`crate::ZipArchive::by_name`) | ✅ [->](`crate::write::FileOptions::compression_method`) |
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
#[cfg(feature = "bzip2")]
|
pub use crate::compression::{CompressionMethod, SUPPORTED_COMPRESSION_METHODS};
|
||||||
extern crate bzip2;
|
pub use crate::read::ZipArchive;
|
||||||
#[cfg(feature = "flate2")]
|
pub use crate::types::DateTime;
|
||||||
extern crate flate2;
|
pub use crate::write::ZipWriter;
|
||||||
extern crate msdos_time;
|
|
||||||
extern crate podio;
|
|
||||||
extern crate time;
|
|
||||||
|
|
||||||
pub use read::ZipArchive;
|
#[cfg(feature = "aes-crypto")]
|
||||||
pub use write::ZipWriter;
|
mod aes;
|
||||||
pub use compression::CompressionMethod;
|
#[cfg(feature = "aes-crypto")]
|
||||||
|
mod aes_ctr;
|
||||||
mod spec;
|
|
||||||
mod crc32;
|
|
||||||
mod types;
|
|
||||||
pub mod read;
|
|
||||||
mod compression;
|
mod compression;
|
||||||
pub mod write;
|
|
||||||
mod cp437;
|
mod cp437;
|
||||||
|
mod crc32;
|
||||||
|
pub mod read;
|
||||||
pub mod result;
|
pub mod result;
|
||||||
|
mod spec;
|
||||||
|
mod types;
|
||||||
|
pub mod write;
|
||||||
|
mod zipcrypto;
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,17 +1,27 @@
|
||||||
//! Error types that can be emitted from this library
|
//! Error types that can be emitted from this library
|
||||||
|
|
||||||
use std::convert;
|
use std::error::Error;
|
||||||
use std::error;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
/// Generic result type with ZipError as its error variant
|
/// Generic result type with ZipError as its error variant
|
||||||
pub type ZipResult<T> = Result<T, ZipError>;
|
pub type ZipResult<T> = Result<T, ZipError>;
|
||||||
|
|
||||||
|
/// The given password is wrong
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct InvalidPassword;
|
||||||
|
|
||||||
|
impl fmt::Display for InvalidPassword {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(fmt, "invalid password for file in archive")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for InvalidPassword {}
|
||||||
|
|
||||||
/// Error type for Zip
|
/// Error type for Zip
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ZipError
|
pub enum ZipError {
|
||||||
{
|
|
||||||
/// An Error caused by I/O
|
/// An Error caused by I/O
|
||||||
Io(io::Error),
|
Io(io::Error),
|
||||||
|
|
||||||
|
@ -25,70 +35,49 @@ pub enum ZipError
|
||||||
FileNotFound,
|
FileNotFound,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ZipError
|
impl From<io::Error> for ZipError {
|
||||||
{
|
fn from(err: io::Error) -> ZipError {
|
||||||
fn detail(&self) -> ::std::borrow::Cow<str>
|
|
||||||
{
|
|
||||||
use std::error::Error;
|
|
||||||
|
|
||||||
match *self
|
|
||||||
{
|
|
||||||
ZipError::Io(ref io_err) => {
|
|
||||||
("Io Error: ".to_string() + (io_err as &error::Error).description()).into()
|
|
||||||
},
|
|
||||||
ZipError::InvalidArchive(msg) | ZipError::UnsupportedArchive(msg) => {
|
|
||||||
(self.description().to_string() + ": " + msg).into()
|
|
||||||
},
|
|
||||||
ZipError::FileNotFound => {
|
|
||||||
self.description().into()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl convert::From<io::Error> for ZipError
|
|
||||||
{
|
|
||||||
fn from(err: io::Error) -> ZipError
|
|
||||||
{
|
|
||||||
ZipError::Io(err)
|
ZipError::Io(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl convert::From<ZipError> for io::Error
|
impl fmt::Display for ZipError {
|
||||||
{
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
fn from(err: ZipError) -> io::Error
|
match self {
|
||||||
{
|
ZipError::Io(err) => write!(fmt, "{}", err),
|
||||||
io::Error::new(io::ErrorKind::Other, err)
|
ZipError::InvalidArchive(err) => write!(fmt, "invalid Zip archive: {}", err),
|
||||||
}
|
ZipError::UnsupportedArchive(err) => write!(fmt, "unsupported Zip archive: {}", err),
|
||||||
}
|
ZipError::FileNotFound => write!(fmt, "specified file not found in archive"),
|
||||||
|
|
||||||
impl fmt::Display for ZipError
|
|
||||||
{
|
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>
|
|
||||||
{
|
|
||||||
fmt.write_str(&*self.detail())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl error::Error for ZipError
|
|
||||||
{
|
|
||||||
fn description(&self) -> &str
|
|
||||||
{
|
|
||||||
match *self
|
|
||||||
{
|
|
||||||
ZipError::Io(ref io_err) => (io_err as &error::Error).description(),
|
|
||||||
ZipError::InvalidArchive(..) => "Invalid Zip archive",
|
|
||||||
ZipError::UnsupportedArchive(..) => "Unsupported Zip archive",
|
|
||||||
ZipError::FileNotFound => "Specified file not found in archive",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn cause(&self) -> Option<&error::Error>
|
impl Error for ZipError {
|
||||||
{
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
match *self
|
match self {
|
||||||
{
|
ZipError::Io(err) => Some(err),
|
||||||
ZipError::Io(ref io_err) => Some(io_err as &error::Error),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ZipError {
|
||||||
|
/// The text used as an error when a password is required and not supplied
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// # use zip::result::ZipError;
|
||||||
|
/// # let mut archive = zip::ZipArchive::new(std::io::Cursor::new(&[])).unwrap();
|
||||||
|
/// match archive.by_index(1) {
|
||||||
|
/// Err(ZipError::UnsupportedArchive(ZipError::PASSWORD_REQUIRED)) => eprintln!("a password is needed to unzip this file"),
|
||||||
|
/// _ => (),
|
||||||
|
/// }
|
||||||
|
/// # ()
|
||||||
|
/// ```
|
||||||
|
pub const PASSWORD_REQUIRED: &'static str = "Password required to decrypt file";
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ZipError> for io::Error {
|
||||||
|
fn from(err: ZipError) -> io::Error {
|
||||||
|
io::Error::new(io::ErrorKind::Other, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
|
use crate::result::{ZipError, ZipResult};
|
||||||
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use result::{ZipResult, ZipError};
|
|
||||||
use podio::{ReadPodExt, WritePodExt, LittleEndian};
|
|
||||||
|
|
||||||
pub const LOCAL_FILE_HEADER_SIGNATURE : u32 = 0x04034b50;
|
pub const LOCAL_FILE_HEADER_SIGNATURE: u32 = 0x04034b50;
|
||||||
pub const CENTRAL_DIRECTORY_HEADER_SIGNATURE : u32 = 0x02014b50;
|
pub const CENTRAL_DIRECTORY_HEADER_SIGNATURE: u32 = 0x02014b50;
|
||||||
const CENTRAL_DIRECTORY_END_SIGNATURE : u32 = 0x06054b50;
|
const CENTRAL_DIRECTORY_END_SIGNATURE: u32 = 0x06054b50;
|
||||||
pub const ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE : u32 = 0x06064b50;
|
pub const ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE: u32 = 0x06064b50;
|
||||||
const ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE : u32 = 0x07064b50;
|
const ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE: u32 = 0x07064b50;
|
||||||
|
|
||||||
pub struct CentralDirectoryEnd
|
pub const ZIP64_BYTES_THR: u64 = u32::MAX as u64;
|
||||||
{
|
pub const ZIP64_ENTRY_THR: usize = u16::MAX as usize;
|
||||||
|
|
||||||
|
pub struct CentralDirectoryEnd {
|
||||||
pub disk_number: u16,
|
pub disk_number: u16,
|
||||||
pub disk_with_central_directory: u16,
|
pub disk_with_central_directory: u16,
|
||||||
pub number_of_files_on_this_disk: u16,
|
pub number_of_files_on_this_disk: u16,
|
||||||
|
@ -20,116 +22,115 @@ pub struct CentralDirectoryEnd
|
||||||
pub zip_file_comment: Vec<u8>,
|
pub zip_file_comment: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CentralDirectoryEnd
|
impl CentralDirectoryEnd {
|
||||||
{
|
pub fn parse<T: Read>(reader: &mut T) -> ZipResult<CentralDirectoryEnd> {
|
||||||
pub fn parse<T: Read>(reader: &mut T) -> ZipResult<CentralDirectoryEnd>
|
let magic = reader.read_u32::<LittleEndian>()?;
|
||||||
{
|
if magic != CENTRAL_DIRECTORY_END_SIGNATURE {
|
||||||
let magic = try!(reader.read_u32::<LittleEndian>());
|
return Err(ZipError::InvalidArchive("Invalid digital signature header"));
|
||||||
if magic != CENTRAL_DIRECTORY_END_SIGNATURE
|
|
||||||
{
|
|
||||||
return Err(ZipError::InvalidArchive("Invalid digital signature header"))
|
|
||||||
}
|
}
|
||||||
let disk_number = try!(reader.read_u16::<LittleEndian>());
|
let disk_number = reader.read_u16::<LittleEndian>()?;
|
||||||
let disk_with_central_directory = try!(reader.read_u16::<LittleEndian>());
|
let disk_with_central_directory = reader.read_u16::<LittleEndian>()?;
|
||||||
let number_of_files_on_this_disk = try!(reader.read_u16::<LittleEndian>());
|
let number_of_files_on_this_disk = reader.read_u16::<LittleEndian>()?;
|
||||||
let number_of_files = try!(reader.read_u16::<LittleEndian>());
|
let number_of_files = reader.read_u16::<LittleEndian>()?;
|
||||||
let central_directory_size = try!(reader.read_u32::<LittleEndian>());
|
let central_directory_size = reader.read_u32::<LittleEndian>()?;
|
||||||
let central_directory_offset = try!(reader.read_u32::<LittleEndian>());
|
let central_directory_offset = reader.read_u32::<LittleEndian>()?;
|
||||||
let zip_file_comment_length = try!(reader.read_u16::<LittleEndian>()) as usize;
|
let zip_file_comment_length = reader.read_u16::<LittleEndian>()? as usize;
|
||||||
let zip_file_comment = try!(ReadPodExt::read_exact(reader, zip_file_comment_length));
|
let mut zip_file_comment = vec![0; zip_file_comment_length];
|
||||||
|
reader.read_exact(&mut zip_file_comment)?;
|
||||||
|
|
||||||
Ok(CentralDirectoryEnd
|
Ok(CentralDirectoryEnd {
|
||||||
{
|
disk_number,
|
||||||
disk_number: disk_number,
|
disk_with_central_directory,
|
||||||
disk_with_central_directory: disk_with_central_directory,
|
number_of_files_on_this_disk,
|
||||||
number_of_files_on_this_disk: number_of_files_on_this_disk,
|
number_of_files,
|
||||||
number_of_files: number_of_files,
|
central_directory_size,
|
||||||
central_directory_size: central_directory_size,
|
central_directory_offset,
|
||||||
central_directory_offset: central_directory_offset,
|
zip_file_comment,
|
||||||
zip_file_comment: zip_file_comment,
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_and_parse<T: Read+io::Seek>(reader: &mut T) -> ZipResult<(CentralDirectoryEnd, u64)>
|
pub fn find_and_parse<T: Read + io::Seek>(
|
||||||
{
|
reader: &mut T,
|
||||||
|
) -> ZipResult<(CentralDirectoryEnd, u64)> {
|
||||||
const HEADER_SIZE: u64 = 22;
|
const HEADER_SIZE: u64 = 22;
|
||||||
const BYTES_BETWEEN_MAGIC_AND_COMMENT_SIZE: u64 = HEADER_SIZE - 6;
|
const BYTES_BETWEEN_MAGIC_AND_COMMENT_SIZE: u64 = HEADER_SIZE - 6;
|
||||||
let file_length = try!(reader.seek(io::SeekFrom::End(0)));
|
let file_length = reader.seek(io::SeekFrom::End(0))?;
|
||||||
|
|
||||||
let search_upper_bound = file_length.checked_sub(HEADER_SIZE + ::std::u16::MAX as u64).unwrap_or(0);
|
let search_upper_bound = file_length.saturating_sub(HEADER_SIZE + ::std::u16::MAX as u64);
|
||||||
|
|
||||||
if file_length < HEADER_SIZE {
|
if file_length < HEADER_SIZE {
|
||||||
return Err(ZipError::InvalidArchive("Invalid zip header"));
|
return Err(ZipError::InvalidArchive("Invalid zip header"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut pos = file_length - HEADER_SIZE;
|
let mut pos = file_length - HEADER_SIZE;
|
||||||
while pos >= search_upper_bound
|
while pos >= search_upper_bound {
|
||||||
{
|
reader.seek(io::SeekFrom::Start(pos as u64))?;
|
||||||
try!(reader.seek(io::SeekFrom::Start(pos as u64)));
|
if reader.read_u32::<LittleEndian>()? == CENTRAL_DIRECTORY_END_SIGNATURE {
|
||||||
if try!(reader.read_u32::<LittleEndian>()) == CENTRAL_DIRECTORY_END_SIGNATURE
|
reader.seek(io::SeekFrom::Current(
|
||||||
{
|
BYTES_BETWEEN_MAGIC_AND_COMMENT_SIZE as i64,
|
||||||
try!(reader.seek(io::SeekFrom::Current(BYTES_BETWEEN_MAGIC_AND_COMMENT_SIZE as i64)));
|
))?;
|
||||||
let comment_length = try!(reader.read_u16::<LittleEndian>()) as u64;
|
let cde_start_pos = reader.seek(io::SeekFrom::Start(pos as u64))?;
|
||||||
if file_length - pos - HEADER_SIZE == comment_length
|
return CentralDirectoryEnd::parse(reader).map(|cde| (cde, cde_start_pos));
|
||||||
{
|
|
||||||
let cde_start_pos = try!(reader.seek(io::SeekFrom::Start(pos as u64)));
|
|
||||||
return CentralDirectoryEnd::parse(reader).map(|cde| (cde, cde_start_pos));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pos = match pos.checked_sub(1) {
|
pos = match pos.checked_sub(1) {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => break,
|
None => break,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Err(ZipError::InvalidArchive("Could not find central directory end"))
|
Err(ZipError::InvalidArchive(
|
||||||
|
"Could not find central directory end",
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()>
|
pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()> {
|
||||||
{
|
writer.write_u32::<LittleEndian>(CENTRAL_DIRECTORY_END_SIGNATURE)?;
|
||||||
try!(writer.write_u32::<LittleEndian>(CENTRAL_DIRECTORY_END_SIGNATURE));
|
writer.write_u16::<LittleEndian>(self.disk_number)?;
|
||||||
try!(writer.write_u16::<LittleEndian>(self.disk_number));
|
writer.write_u16::<LittleEndian>(self.disk_with_central_directory)?;
|
||||||
try!(writer.write_u16::<LittleEndian>(self.disk_with_central_directory));
|
writer.write_u16::<LittleEndian>(self.number_of_files_on_this_disk)?;
|
||||||
try!(writer.write_u16::<LittleEndian>(self.number_of_files_on_this_disk));
|
writer.write_u16::<LittleEndian>(self.number_of_files)?;
|
||||||
try!(writer.write_u16::<LittleEndian>(self.number_of_files));
|
writer.write_u32::<LittleEndian>(self.central_directory_size)?;
|
||||||
try!(writer.write_u32::<LittleEndian>(self.central_directory_size));
|
writer.write_u32::<LittleEndian>(self.central_directory_offset)?;
|
||||||
try!(writer.write_u32::<LittleEndian>(self.central_directory_offset));
|
writer.write_u16::<LittleEndian>(self.zip_file_comment.len() as u16)?;
|
||||||
try!(writer.write_u16::<LittleEndian>(self.zip_file_comment.len() as u16));
|
writer.write_all(&self.zip_file_comment)?;
|
||||||
try!(writer.write_all(&self.zip_file_comment));
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Zip64CentralDirectoryEndLocator
|
pub struct Zip64CentralDirectoryEndLocator {
|
||||||
{
|
|
||||||
pub disk_with_central_directory: u32,
|
pub disk_with_central_directory: u32,
|
||||||
pub end_of_central_directory_offset: u64,
|
pub end_of_central_directory_offset: u64,
|
||||||
pub number_of_disks: u32,
|
pub number_of_disks: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Zip64CentralDirectoryEndLocator
|
impl Zip64CentralDirectoryEndLocator {
|
||||||
{
|
pub fn parse<T: Read>(reader: &mut T) -> ZipResult<Zip64CentralDirectoryEndLocator> {
|
||||||
pub fn parse<T: Read>(reader: &mut T) -> ZipResult<Zip64CentralDirectoryEndLocator>
|
let magic = reader.read_u32::<LittleEndian>()?;
|
||||||
{
|
if magic != ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE {
|
||||||
let magic = try!(reader.read_u32::<LittleEndian>());
|
return Err(ZipError::InvalidArchive(
|
||||||
if magic != ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE
|
"Invalid zip64 locator digital signature header",
|
||||||
{
|
));
|
||||||
return Err(ZipError::InvalidArchive("Invalid zip64 locator digital signature header"))
|
|
||||||
}
|
}
|
||||||
let disk_with_central_directory = try!(reader.read_u32::<LittleEndian>());
|
let disk_with_central_directory = reader.read_u32::<LittleEndian>()?;
|
||||||
let end_of_central_directory_offset = try!(reader.read_u64::<LittleEndian>());
|
let end_of_central_directory_offset = reader.read_u64::<LittleEndian>()?;
|
||||||
let number_of_disks = try!(reader.read_u32::<LittleEndian>());
|
let number_of_disks = reader.read_u32::<LittleEndian>()?;
|
||||||
|
|
||||||
Ok(Zip64CentralDirectoryEndLocator
|
Ok(Zip64CentralDirectoryEndLocator {
|
||||||
{
|
disk_with_central_directory,
|
||||||
disk_with_central_directory: disk_with_central_directory,
|
end_of_central_directory_offset,
|
||||||
end_of_central_directory_offset: end_of_central_directory_offset,
|
number_of_disks,
|
||||||
number_of_disks: number_of_disks,
|
})
|
||||||
})
|
}
|
||||||
|
|
||||||
|
pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()> {
|
||||||
|
writer.write_u32::<LittleEndian>(ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE)?;
|
||||||
|
writer.write_u32::<LittleEndian>(self.disk_with_central_directory)?;
|
||||||
|
writer.write_u64::<LittleEndian>(self.end_of_central_directory_offset)?;
|
||||||
|
writer.write_u32::<LittleEndian>(self.number_of_disks)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Zip64CentralDirectoryEnd
|
pub struct Zip64CentralDirectoryEnd {
|
||||||
{
|
|
||||||
pub version_made_by: u16,
|
pub version_made_by: u16,
|
||||||
pub version_needed_to_extract: u16,
|
pub version_needed_to_extract: u16,
|
||||||
pub disk_number: u32,
|
pub disk_number: u32,
|
||||||
|
@ -141,50 +142,66 @@ pub struct Zip64CentralDirectoryEnd
|
||||||
//pub extensible_data_sector: Vec<u8>, <-- We don't do anything with this at the moment.
|
//pub extensible_data_sector: Vec<u8>, <-- We don't do anything with this at the moment.
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Zip64CentralDirectoryEnd
|
impl Zip64CentralDirectoryEnd {
|
||||||
{
|
pub fn find_and_parse<T: Read + io::Seek>(
|
||||||
pub fn find_and_parse<T: Read+io::Seek>(reader: &mut T,
|
reader: &mut T,
|
||||||
nominal_offset: u64,
|
nominal_offset: u64,
|
||||||
search_upper_bound: u64) -> ZipResult<(Zip64CentralDirectoryEnd, u64)>
|
search_upper_bound: u64,
|
||||||
{
|
) -> ZipResult<(Zip64CentralDirectoryEnd, u64)> {
|
||||||
let mut pos = nominal_offset;
|
let mut pos = nominal_offset;
|
||||||
|
|
||||||
while pos <= search_upper_bound
|
while pos <= search_upper_bound {
|
||||||
{
|
|
||||||
reader.seek(io::SeekFrom::Start(pos))?;
|
reader.seek(io::SeekFrom::Start(pos))?;
|
||||||
|
|
||||||
if reader.read_u32::<LittleEndian>()? == ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE
|
if reader.read_u32::<LittleEndian>()? == ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE {
|
||||||
{
|
|
||||||
let archive_offset = pos - nominal_offset;
|
let archive_offset = pos - nominal_offset;
|
||||||
|
|
||||||
let _record_size = try!(reader.read_u64::<LittleEndian>());
|
let _record_size = reader.read_u64::<LittleEndian>()?;
|
||||||
// We would use this value if we did anything with the "zip64 extensible data sector".
|
// We would use this value if we did anything with the "zip64 extensible data sector".
|
||||||
|
|
||||||
let version_made_by = try!(reader.read_u16::<LittleEndian>());
|
let version_made_by = reader.read_u16::<LittleEndian>()?;
|
||||||
let version_needed_to_extract = try!(reader.read_u16::<LittleEndian>());
|
let version_needed_to_extract = reader.read_u16::<LittleEndian>()?;
|
||||||
let disk_number = try!(reader.read_u32::<LittleEndian>());
|
let disk_number = reader.read_u32::<LittleEndian>()?;
|
||||||
let disk_with_central_directory = try!(reader.read_u32::<LittleEndian>());
|
let disk_with_central_directory = reader.read_u32::<LittleEndian>()?;
|
||||||
let number_of_files_on_this_disk = try!(reader.read_u64::<LittleEndian>());
|
let number_of_files_on_this_disk = reader.read_u64::<LittleEndian>()?;
|
||||||
let number_of_files = try!(reader.read_u64::<LittleEndian>());
|
let number_of_files = reader.read_u64::<LittleEndian>()?;
|
||||||
let central_directory_size = try!(reader.read_u64::<LittleEndian>());
|
let central_directory_size = reader.read_u64::<LittleEndian>()?;
|
||||||
let central_directory_offset = try!(reader.read_u64::<LittleEndian>());
|
let central_directory_offset = reader.read_u64::<LittleEndian>()?;
|
||||||
|
|
||||||
return Ok((Zip64CentralDirectoryEnd
|
return Ok((
|
||||||
{
|
Zip64CentralDirectoryEnd {
|
||||||
version_made_by: version_made_by,
|
version_made_by,
|
||||||
version_needed_to_extract: version_needed_to_extract,
|
version_needed_to_extract,
|
||||||
disk_number: disk_number,
|
disk_number,
|
||||||
disk_with_central_directory: disk_with_central_directory,
|
disk_with_central_directory,
|
||||||
number_of_files_on_this_disk: number_of_files_on_this_disk,
|
number_of_files_on_this_disk,
|
||||||
number_of_files: number_of_files,
|
number_of_files,
|
||||||
central_directory_size: central_directory_size,
|
central_directory_size,
|
||||||
central_directory_offset: central_directory_offset,
|
central_directory_offset,
|
||||||
}, archive_offset));
|
},
|
||||||
|
archive_offset,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pos += 1;
|
pos += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(ZipError::InvalidArchive("Could not find ZIP64 central directory end"))
|
Err(ZipError::InvalidArchive(
|
||||||
|
"Could not find ZIP64 central directory end",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()> {
|
||||||
|
writer.write_u32::<LittleEndian>(ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE)?;
|
||||||
|
writer.write_u64::<LittleEndian>(44)?; // record size
|
||||||
|
writer.write_u16::<LittleEndian>(self.version_made_by)?;
|
||||||
|
writer.write_u16::<LittleEndian>(self.version_needed_to_extract)?;
|
||||||
|
writer.write_u32::<LittleEndian>(self.disk_number)?;
|
||||||
|
writer.write_u32::<LittleEndian>(self.disk_with_central_directory)?;
|
||||||
|
writer.write_u64::<LittleEndian>(self.number_of_files_on_this_disk)?;
|
||||||
|
writer.write_u64::<LittleEndian>(self.number_of_files)?;
|
||||||
|
writer.write_u64::<LittleEndian>(self.central_directory_size)?;
|
||||||
|
writer.write_u64::<LittleEndian>(self.central_directory_offset)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,58 @@
|
||||||
//! Types that specify what is contained in a ZIP.
|
//! Types that specify what is contained in a ZIP.
|
||||||
|
#[cfg(doc)]
|
||||||
|
use {crate::read::ZipFile, crate::write::FileOptions};
|
||||||
|
|
||||||
use time;
|
#[cfg(not(any(
|
||||||
|
all(target_arch = "arm", target_pointer_width = "32"),
|
||||||
|
target_arch = "mips",
|
||||||
|
target_arch = "powerpc"
|
||||||
|
)))]
|
||||||
|
use std::sync::atomic;
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
all(target_arch = "arm", target_pointer_width = "32"),
|
||||||
|
target_arch = "mips",
|
||||||
|
target_arch = "powerpc"
|
||||||
|
))]
|
||||||
|
mod atomic {
|
||||||
|
use crossbeam_utils::sync::ShardedLock;
|
||||||
|
pub use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct AtomicU64 {
|
||||||
|
value: ShardedLock<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AtomicU64 {
|
||||||
|
pub fn new(v: u64) -> Self {
|
||||||
|
Self {
|
||||||
|
value: ShardedLock::new(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn get_mut(&mut self) -> &mut u64 {
|
||||||
|
self.value.get_mut().unwrap()
|
||||||
|
}
|
||||||
|
pub fn load(&self, _: Ordering) -> u64 {
|
||||||
|
*self.value.read().unwrap()
|
||||||
|
}
|
||||||
|
pub fn store(&self, value: u64, _: Ordering) {
|
||||||
|
*self.value.write().unwrap() = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
use time::{error::ComponentRange, Date, Month, OffsetDateTime, PrimitiveDateTime, Time};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum System
|
pub enum System {
|
||||||
{
|
|
||||||
Dos = 0,
|
Dos = 0,
|
||||||
Unix = 3,
|
Unix = 3,
|
||||||
Unknown,
|
Unknown,
|
||||||
#[doc(hidden)]
|
|
||||||
__Nonexhaustive,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl System {
|
impl System {
|
||||||
pub fn from_u8(system: u8) -> System
|
pub fn from_u8(system: u8) -> System {
|
||||||
{
|
|
||||||
use self::System::*;
|
use self::System::*;
|
||||||
|
|
||||||
match system {
|
match system {
|
||||||
|
@ -25,22 +63,249 @@ impl System {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Representation of a moment in time.
|
||||||
|
///
|
||||||
|
/// Zip files use an old format from DOS to store timestamps,
|
||||||
|
/// with its own set of peculiarities.
|
||||||
|
/// For example, it has a resolution of 2 seconds!
|
||||||
|
///
|
||||||
|
/// A [`DateTime`] can be stored directly in a zipfile with [`FileOptions::last_modified_time`],
|
||||||
|
/// or read from one with [`ZipFile::last_modified`]
|
||||||
|
///
|
||||||
|
/// # Warning
|
||||||
|
///
|
||||||
|
/// Because there is no timezone associated with the [`DateTime`], they should ideally only
|
||||||
|
/// be used for user-facing descriptions. This also means [`DateTime::to_time`] returns an
|
||||||
|
/// [`OffsetDateTime`] (which is the equivalent of chrono's `NaiveDateTime`).
|
||||||
|
///
|
||||||
|
/// Modern zip files store more precise timestamps, which are ignored by [`crate::read::ZipArchive`],
|
||||||
|
/// so keep in mind that these timestamps are unreliable. [We're working on this](https://github.com/zip-rs/zip/issues/156#issuecomment-652981904).
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct DateTime {
|
||||||
|
year: u16,
|
||||||
|
month: u8,
|
||||||
|
day: u8,
|
||||||
|
hour: u8,
|
||||||
|
minute: u8,
|
||||||
|
second: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::default::Default for DateTime {
|
||||||
|
/// Constructs an 'default' datetime of 1980-01-01 00:00:00
|
||||||
|
fn default() -> DateTime {
|
||||||
|
DateTime {
|
||||||
|
year: 1980,
|
||||||
|
month: 1,
|
||||||
|
day: 1,
|
||||||
|
hour: 0,
|
||||||
|
minute: 0,
|
||||||
|
second: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DateTime {
|
||||||
|
/// Converts an msdos (u16, u16) pair to a DateTime object
|
||||||
|
pub fn from_msdos(datepart: u16, timepart: u16) -> DateTime {
|
||||||
|
let seconds = (timepart & 0b0000000000011111) << 1;
|
||||||
|
let minutes = (timepart & 0b0000011111100000) >> 5;
|
||||||
|
let hours = (timepart & 0b1111100000000000) >> 11;
|
||||||
|
let days = datepart & 0b0000000000011111;
|
||||||
|
let months = (datepart & 0b0000000111100000) >> 5;
|
||||||
|
let years = (datepart & 0b1111111000000000) >> 9;
|
||||||
|
|
||||||
|
DateTime {
|
||||||
|
year: (years + 1980) as u16,
|
||||||
|
month: months as u8,
|
||||||
|
day: days as u8,
|
||||||
|
hour: hours as u8,
|
||||||
|
minute: minutes as u8,
|
||||||
|
second: seconds as u8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a DateTime from a specific date and time
|
||||||
|
///
|
||||||
|
/// The bounds are:
|
||||||
|
/// * year: [1980, 2107]
|
||||||
|
/// * month: [1, 12]
|
||||||
|
/// * day: [1, 31]
|
||||||
|
/// * hour: [0, 23]
|
||||||
|
/// * minute: [0, 59]
|
||||||
|
/// * second: [0, 60]
|
||||||
|
#[allow(clippy::result_unit_err)]
|
||||||
|
pub fn from_date_and_time(
|
||||||
|
year: u16,
|
||||||
|
month: u8,
|
||||||
|
day: u8,
|
||||||
|
hour: u8,
|
||||||
|
minute: u8,
|
||||||
|
second: u8,
|
||||||
|
) -> Result<DateTime, ()> {
|
||||||
|
if (1980..=2107).contains(&year)
|
||||||
|
&& month >= 1
|
||||||
|
&& month <= 12
|
||||||
|
&& day >= 1
|
||||||
|
&& day <= 31
|
||||||
|
&& hour <= 23
|
||||||
|
&& minute <= 59
|
||||||
|
&& second <= 60
|
||||||
|
{
|
||||||
|
Ok(DateTime {
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
day,
|
||||||
|
hour,
|
||||||
|
minute,
|
||||||
|
second,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
/// Converts a OffsetDateTime object to a DateTime
|
||||||
|
///
|
||||||
|
/// Returns `Err` when this object is out of bounds
|
||||||
|
#[allow(clippy::result_unit_err)]
|
||||||
|
pub fn from_time(dt: OffsetDateTime) -> Result<DateTime, ()> {
|
||||||
|
if dt.year() >= 1980 && dt.year() <= 2107 {
|
||||||
|
Ok(DateTime {
|
||||||
|
year: (dt.year()) as u16,
|
||||||
|
month: (dt.month()) as u8,
|
||||||
|
day: dt.day() as u8,
|
||||||
|
hour: dt.hour() as u8,
|
||||||
|
minute: dt.minute() as u8,
|
||||||
|
second: dt.second() as u8,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the time portion of this datetime in the msdos representation
|
||||||
|
pub fn timepart(&self) -> u16 {
|
||||||
|
((self.second as u16) >> 1) | ((self.minute as u16) << 5) | ((self.hour as u16) << 11)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the date portion of this datetime in the msdos representation
|
||||||
|
pub fn datepart(&self) -> u16 {
|
||||||
|
(self.day as u16) | ((self.month as u16) << 5) | ((self.year - 1980) << 9)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
/// Converts the DateTime to a OffsetDateTime structure
|
||||||
|
pub fn to_time(&self) -> Result<OffsetDateTime, ComponentRange> {
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
let date =
|
||||||
|
Date::from_calendar_date(self.year as i32, Month::try_from(self.month)?, self.day)?;
|
||||||
|
let time = Time::from_hms(self.hour, self.minute, self.second)?;
|
||||||
|
Ok(PrimitiveDateTime::new(date, time).assume_utc())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the year. There is no epoch, i.e. 2018 will be returned as 2018.
|
||||||
|
pub fn year(&self) -> u16 {
|
||||||
|
self.year
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the month, where 1 = january and 12 = december
|
||||||
|
///
|
||||||
|
/// # Warning
|
||||||
|
///
|
||||||
|
/// When read from a zip file, this may not be a reasonable value
|
||||||
|
pub fn month(&self) -> u8 {
|
||||||
|
self.month
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the day
|
||||||
|
///
|
||||||
|
/// # Warning
|
||||||
|
///
|
||||||
|
/// When read from a zip file, this may not be a reasonable value
|
||||||
|
pub fn day(&self) -> u8 {
|
||||||
|
self.day
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the hour
|
||||||
|
///
|
||||||
|
/// # Warning
|
||||||
|
///
|
||||||
|
/// When read from a zip file, this may not be a reasonable value
|
||||||
|
pub fn hour(&self) -> u8 {
|
||||||
|
self.hour
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the minute
|
||||||
|
///
|
||||||
|
/// # Warning
|
||||||
|
///
|
||||||
|
/// When read from a zip file, this may not be a reasonable value
|
||||||
|
pub fn minute(&self) -> u8 {
|
||||||
|
self.minute
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the second
|
||||||
|
///
|
||||||
|
/// # Warning
|
||||||
|
///
|
||||||
|
/// When read from a zip file, this may not be a reasonable value
|
||||||
|
pub fn second(&self) -> u8 {
|
||||||
|
self.second
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const DEFAULT_VERSION: u8 = 46;
|
pub const DEFAULT_VERSION: u8 = 46;
|
||||||
|
|
||||||
|
/// A type like `AtomicU64` except it implements `Clone` and has predefined
|
||||||
|
/// ordering.
|
||||||
|
///
|
||||||
|
/// It uses `Relaxed` ordering because it is not used for synchronisation.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AtomicU64(atomic::AtomicU64);
|
||||||
|
|
||||||
|
impl AtomicU64 {
|
||||||
|
pub fn new(v: u64) -> Self {
|
||||||
|
Self(atomic::AtomicU64::new(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load(&self) -> u64 {
|
||||||
|
self.0.load(atomic::Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn store(&self, val: u64) {
|
||||||
|
self.0.store(val, atomic::Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mut(&mut self) -> &mut u64 {
|
||||||
|
self.0.get_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for AtomicU64 {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self(atomic::AtomicU64::new(self.load()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Structure representing a ZIP file.
|
/// Structure representing a ZIP file.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ZipFileData
|
pub struct ZipFileData {
|
||||||
{
|
|
||||||
/// Compatibility of the file attribute information
|
/// Compatibility of the file attribute information
|
||||||
pub system: System,
|
pub system: System,
|
||||||
/// Specification version
|
/// Specification version
|
||||||
pub version_made_by: u8,
|
pub version_made_by: u8,
|
||||||
/// True if the file is encrypted.
|
/// True if the file is encrypted.
|
||||||
pub encrypted: bool,
|
pub encrypted: bool,
|
||||||
|
/// True if the file uses a data-descriptor section
|
||||||
|
pub using_data_descriptor: bool,
|
||||||
/// Compression method used to store the file
|
/// Compression method used to store the file
|
||||||
pub compression_method: ::compression::CompressionMethod,
|
pub compression_method: crate::compression::CompressionMethod,
|
||||||
|
/// Compression level to store the file
|
||||||
|
pub compression_level: Option<i32>,
|
||||||
/// Last modified time. This will only have a 2 second precision.
|
/// Last modified time. This will only have a 2 second precision.
|
||||||
pub last_modified_time: time::Tm,
|
pub last_modified_time: DateTime,
|
||||||
/// CRC32 checksum
|
/// CRC32 checksum
|
||||||
pub crc32: u32,
|
pub crc32: u32,
|
||||||
/// Size of the file in the ZIP
|
/// Size of the file in the ZIP
|
||||||
|
@ -51,14 +316,24 @@ pub struct ZipFileData
|
||||||
pub file_name: String,
|
pub file_name: String,
|
||||||
/// Raw file name. To be used when file_name was incorrectly decoded.
|
/// Raw file name. To be used when file_name was incorrectly decoded.
|
||||||
pub file_name_raw: Vec<u8>,
|
pub file_name_raw: Vec<u8>,
|
||||||
|
/// Extra field usually used for storage expansion
|
||||||
|
pub extra_field: Vec<u8>,
|
||||||
/// File comment
|
/// File comment
|
||||||
pub file_comment: String,
|
pub file_comment: String,
|
||||||
/// Specifies where the local header of the file starts
|
/// Specifies where the local header of the file starts
|
||||||
pub header_start: u64,
|
pub header_start: u64,
|
||||||
|
/// Specifies where the central header of the file starts
|
||||||
|
///
|
||||||
|
/// Note that when this is not known, it is set to 0
|
||||||
|
pub central_header_start: u64,
|
||||||
/// Specifies where the compressed data of the file starts
|
/// Specifies where the compressed data of the file starts
|
||||||
pub data_start: u64,
|
pub data_start: AtomicU64,
|
||||||
/// External file attributes
|
/// External file attributes
|
||||||
pub external_attributes: u32,
|
pub external_attributes: u32,
|
||||||
|
/// Reserve local ZIP64 extra field
|
||||||
|
pub large_file: bool,
|
||||||
|
/// AES mode if applicable
|
||||||
|
pub aes_mode: Option<(AesMode, AesVendorVersion)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ZipFileData {
|
impl ZipFileData {
|
||||||
|
@ -66,7 +341,8 @@ impl ZipFileData {
|
||||||
let no_null_filename = match self.file_name.find('\0') {
|
let no_null_filename = match self.file_name.find('\0') {
|
||||||
Some(index) => &self.file_name[0..index],
|
Some(index) => &self.file_name[0..index],
|
||||||
None => &self.file_name,
|
None => &self.file_name,
|
||||||
}.to_string();
|
}
|
||||||
|
.to_string();
|
||||||
|
|
||||||
// zip files can contain both / and \ as separators regardless of the OS
|
// zip files can contain both / and \ as separators regardless of the OS
|
||||||
// and as we want to return a sanitized PathBuf that only supports the
|
// and as we want to return a sanitized PathBuf that only supports the
|
||||||
|
@ -74,32 +350,70 @@ impl ZipFileData {
|
||||||
let separator = ::std::path::MAIN_SEPARATOR;
|
let separator = ::std::path::MAIN_SEPARATOR;
|
||||||
let opposite_separator = match separator {
|
let opposite_separator = match separator {
|
||||||
'/' => '\\',
|
'/' => '\\',
|
||||||
'\\' | _ => '/',
|
_ => '/',
|
||||||
};
|
};
|
||||||
let filename =
|
let filename =
|
||||||
no_null_filename.replace(&opposite_separator.to_string(), &separator.to_string());
|
no_null_filename.replace(&opposite_separator.to_string(), &separator.to_string());
|
||||||
|
|
||||||
::std::path::Path::new(&filename)
|
::std::path::Path::new(&filename)
|
||||||
.components()
|
.components()
|
||||||
.filter(|component| match *component {
|
.filter(|component| matches!(*component, ::std::path::Component::Normal(..)))
|
||||||
::std::path::Component::Normal(..) => true,
|
|
||||||
_ => false,
|
|
||||||
})
|
|
||||||
.fold(::std::path::PathBuf::new(), |mut path, ref cur| {
|
.fold(::std::path::PathBuf::new(), |mut path, ref cur| {
|
||||||
path.push(cur.as_os_str());
|
path.push(cur.as_os_str());
|
||||||
path
|
path
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn zip64_extension(&self) -> bool {
|
||||||
|
self.uncompressed_size > 0xFFFFFFFF
|
||||||
|
|| self.compressed_size > 0xFFFFFFFF
|
||||||
|
|| self.header_start > 0xFFFFFFFF
|
||||||
|
}
|
||||||
|
|
||||||
pub fn version_needed(&self) -> u16 {
|
pub fn version_needed(&self) -> u16 {
|
||||||
match self.compression_method {
|
// higher versions matched first
|
||||||
|
match (self.zip64_extension(), self.compression_method) {
|
||||||
#[cfg(feature = "bzip2")]
|
#[cfg(feature = "bzip2")]
|
||||||
::compression::CompressionMethod::Bzip2 => 46,
|
(_, crate::compression::CompressionMethod::Bzip2) => 46,
|
||||||
|
(true, _) => 45,
|
||||||
_ => 20,
|
_ => 20,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The encryption specification used to encrypt a file with AES.
|
||||||
|
///
|
||||||
|
/// According to the [specification](https://www.winzip.com/win/en/aes_info.html#winzip11) AE-2
|
||||||
|
/// does not make use of the CRC check.
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum AesVendorVersion {
|
||||||
|
Ae1,
|
||||||
|
Ae2,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// AES variant used.
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum AesMode {
|
||||||
|
Aes128,
|
||||||
|
Aes192,
|
||||||
|
Aes256,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "aes-crypto")]
|
||||||
|
impl AesMode {
|
||||||
|
pub fn salt_length(&self) -> usize {
|
||||||
|
self.key_length() / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn key_length(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Self::Aes128 => 16,
|
||||||
|
Self::Aes192 => 24,
|
||||||
|
Self::Aes256 => 32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -119,18 +433,141 @@ mod test {
|
||||||
system: System::Dos,
|
system: System::Dos,
|
||||||
version_made_by: 0,
|
version_made_by: 0,
|
||||||
encrypted: false,
|
encrypted: false,
|
||||||
compression_method: ::compression::CompressionMethod::Stored,
|
using_data_descriptor: false,
|
||||||
last_modified_time: time::empty_tm(),
|
compression_method: crate::compression::CompressionMethod::Stored,
|
||||||
|
compression_level: None,
|
||||||
|
last_modified_time: DateTime::default(),
|
||||||
crc32: 0,
|
crc32: 0,
|
||||||
compressed_size: 0,
|
compressed_size: 0,
|
||||||
uncompressed_size: 0,
|
uncompressed_size: 0,
|
||||||
file_name: file_name.clone(),
|
file_name: file_name.clone(),
|
||||||
file_name_raw: file_name.into_bytes(),
|
file_name_raw: file_name.into_bytes(),
|
||||||
|
extra_field: Vec::new(),
|
||||||
file_comment: String::new(),
|
file_comment: String::new(),
|
||||||
header_start: 0,
|
header_start: 0,
|
||||||
data_start: 0,
|
data_start: AtomicU64::new(0),
|
||||||
|
central_header_start: 0,
|
||||||
external_attributes: 0,
|
external_attributes: 0,
|
||||||
|
large_file: false,
|
||||||
|
aes_mode: None,
|
||||||
};
|
};
|
||||||
assert_eq!(data.file_name_sanitized(), ::std::path::PathBuf::from("path/etc/passwd"));
|
assert_eq!(
|
||||||
|
data.file_name_sanitized(),
|
||||||
|
::std::path::PathBuf::from("path/etc/passwd")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[allow(clippy::unusual_byte_groupings)]
|
||||||
|
fn datetime_default() {
|
||||||
|
use super::DateTime;
|
||||||
|
let dt = DateTime::default();
|
||||||
|
assert_eq!(dt.timepart(), 0);
|
||||||
|
assert_eq!(dt.datepart(), 0b0000000_0001_00001);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[allow(clippy::unusual_byte_groupings)]
|
||||||
|
fn datetime_max() {
|
||||||
|
use super::DateTime;
|
||||||
|
let dt = DateTime::from_date_and_time(2107, 12, 31, 23, 59, 60).unwrap();
|
||||||
|
assert_eq!(dt.timepart(), 0b10111_111011_11110);
|
||||||
|
assert_eq!(dt.datepart(), 0b1111111_1100_11111);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn datetime_bounds() {
|
||||||
|
use super::DateTime;
|
||||||
|
|
||||||
|
assert!(DateTime::from_date_and_time(2000, 1, 1, 23, 59, 60).is_ok());
|
||||||
|
assert!(DateTime::from_date_and_time(2000, 1, 1, 24, 0, 0).is_err());
|
||||||
|
assert!(DateTime::from_date_and_time(2000, 1, 1, 0, 60, 0).is_err());
|
||||||
|
assert!(DateTime::from_date_and_time(2000, 1, 1, 0, 0, 61).is_err());
|
||||||
|
|
||||||
|
assert!(DateTime::from_date_and_time(2107, 12, 31, 0, 0, 0).is_ok());
|
||||||
|
assert!(DateTime::from_date_and_time(1980, 1, 1, 0, 0, 0).is_ok());
|
||||||
|
assert!(DateTime::from_date_and_time(1979, 1, 1, 0, 0, 0).is_err());
|
||||||
|
assert!(DateTime::from_date_and_time(1980, 0, 1, 0, 0, 0).is_err());
|
||||||
|
assert!(DateTime::from_date_and_time(1980, 1, 0, 0, 0, 0).is_err());
|
||||||
|
assert!(DateTime::from_date_and_time(2108, 12, 31, 0, 0, 0).is_err());
|
||||||
|
assert!(DateTime::from_date_and_time(2107, 13, 31, 0, 0, 0).is_err());
|
||||||
|
assert!(DateTime::from_date_and_time(2107, 12, 32, 0, 0, 0).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
use time::{format_description::well_known::Rfc3339, OffsetDateTime};
|
||||||
|
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
#[test]
|
||||||
|
fn datetime_from_time_bounds() {
|
||||||
|
use super::DateTime;
|
||||||
|
use time::macros::datetime;
|
||||||
|
|
||||||
|
// 1979-12-31 23:59:59
|
||||||
|
assert!(DateTime::from_time(datetime!(1979-12-31 23:59:59 UTC)).is_err());
|
||||||
|
|
||||||
|
// 1980-01-01 00:00:00
|
||||||
|
assert!(DateTime::from_time(datetime!(1980-01-01 00:00:00 UTC)).is_ok());
|
||||||
|
|
||||||
|
// 2107-12-31 23:59:59
|
||||||
|
assert!(DateTime::from_time(datetime!(2107-12-31 23:59:59 UTC)).is_ok());
|
||||||
|
|
||||||
|
// 2108-01-01 00:00:00
|
||||||
|
assert!(DateTime::from_time(datetime!(2108-01-01 00:00:00 UTC)).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn time_conversion() {
|
||||||
|
use super::DateTime;
|
||||||
|
let dt = DateTime::from_msdos(0x4D71, 0x54CF);
|
||||||
|
assert_eq!(dt.year(), 2018);
|
||||||
|
assert_eq!(dt.month(), 11);
|
||||||
|
assert_eq!(dt.day(), 17);
|
||||||
|
assert_eq!(dt.hour(), 10);
|
||||||
|
assert_eq!(dt.minute(), 38);
|
||||||
|
assert_eq!(dt.second(), 30);
|
||||||
|
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
assert_eq!(
|
||||||
|
dt.to_time().unwrap().format(&Rfc3339).unwrap(),
|
||||||
|
"2018-11-17T10:38:30Z"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn time_out_of_bounds() {
|
||||||
|
use super::DateTime;
|
||||||
|
let dt = DateTime::from_msdos(0xFFFF, 0xFFFF);
|
||||||
|
assert_eq!(dt.year(), 2107);
|
||||||
|
assert_eq!(dt.month(), 15);
|
||||||
|
assert_eq!(dt.day(), 31);
|
||||||
|
assert_eq!(dt.hour(), 31);
|
||||||
|
assert_eq!(dt.minute(), 63);
|
||||||
|
assert_eq!(dt.second(), 62);
|
||||||
|
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
assert!(dt.to_time().is_err());
|
||||||
|
|
||||||
|
let dt = DateTime::from_msdos(0x0000, 0x0000);
|
||||||
|
assert_eq!(dt.year(), 1980);
|
||||||
|
assert_eq!(dt.month(), 0);
|
||||||
|
assert_eq!(dt.day(), 0);
|
||||||
|
assert_eq!(dt.hour(), 0);
|
||||||
|
assert_eq!(dt.minute(), 0);
|
||||||
|
assert_eq!(dt.second(), 0);
|
||||||
|
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
assert!(dt.to_time().is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
#[test]
|
||||||
|
fn time_at_january() {
|
||||||
|
use super::DateTime;
|
||||||
|
|
||||||
|
// 2020-01-01 00:00:00
|
||||||
|
let clock = OffsetDateTime::from_unix_timestamp(1_577_836_800).unwrap();
|
||||||
|
|
||||||
|
assert!(DateTime::from_time(clock).is_ok());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,184 @@
|
||||||
|
//! Implementation of the ZipCrypto algorithm
|
||||||
|
//!
|
||||||
|
//! The following paper was used to implement the ZipCrypto algorithm:
|
||||||
|
//! [https://courses.cs.ut.ee/MTAT.07.022/2015_fall/uploads/Main/dmitri-report-f15-16.pdf](https://courses.cs.ut.ee/MTAT.07.022/2015_fall/uploads/Main/dmitri-report-f15-16.pdf)
|
||||||
|
|
||||||
|
use std::num::Wrapping;
|
||||||
|
|
||||||
|
/// A container to hold the current key state
|
||||||
|
struct ZipCryptoKeys {
|
||||||
|
key_0: Wrapping<u32>,
|
||||||
|
key_1: Wrapping<u32>,
|
||||||
|
key_2: Wrapping<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ZipCryptoKeys {
|
||||||
|
fn new() -> ZipCryptoKeys {
|
||||||
|
ZipCryptoKeys {
|
||||||
|
key_0: Wrapping(0x12345678),
|
||||||
|
key_1: Wrapping(0x23456789),
|
||||||
|
key_2: Wrapping(0x34567890),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, input: u8) {
|
||||||
|
self.key_0 = ZipCryptoKeys::crc32(self.key_0, input);
|
||||||
|
self.key_1 =
|
||||||
|
(self.key_1 + (self.key_0 & Wrapping(0xff))) * Wrapping(0x08088405) + Wrapping(1);
|
||||||
|
self.key_2 = ZipCryptoKeys::crc32(self.key_2, (self.key_1 >> 24).0 as u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stream_byte(&mut self) -> u8 {
|
||||||
|
let temp: Wrapping<u16> = Wrapping(self.key_2.0 as u16) | Wrapping(3);
|
||||||
|
((temp * (temp ^ Wrapping(1))) >> 8).0 as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decrypt_byte(&mut self, cipher_byte: u8) -> u8 {
|
||||||
|
let plain_byte: u8 = self.stream_byte() ^ cipher_byte;
|
||||||
|
self.update(plain_byte);
|
||||||
|
plain_byte
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn encrypt_byte(&mut self, plain_byte: u8) -> u8 {
|
||||||
|
let cipher_byte: u8 = self.stream_byte() ^ plain_byte;
|
||||||
|
self.update(plain_byte);
|
||||||
|
cipher_byte
|
||||||
|
}
|
||||||
|
|
||||||
|
fn crc32(crc: Wrapping<u32>, input: u8) -> Wrapping<u32> {
|
||||||
|
(crc >> 8) ^ Wrapping(CRCTABLE[((crc & Wrapping(0xff)).0 as u8 ^ input) as usize])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A ZipCrypto reader with unverified password
|
||||||
|
pub struct ZipCryptoReader<R> {
|
||||||
|
file: R,
|
||||||
|
keys: ZipCryptoKeys,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ZipCryptoValidator {
|
||||||
|
PkzipCrc32(u32),
|
||||||
|
InfoZipMsdosTime(u16),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: std::io::Read> ZipCryptoReader<R> {
|
||||||
|
/// Note: The password is `&[u8]` and not `&str` because the
|
||||||
|
/// [zip specification](https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE-6.3.3.TXT)
|
||||||
|
/// does not specify password encoding (see function `update_keys` in the specification).
|
||||||
|
/// Therefore, if `&str` was used, the password would be UTF-8 and it
|
||||||
|
/// would be impossible to decrypt files that were encrypted with a
|
||||||
|
/// password byte sequence that is unrepresentable in UTF-8.
|
||||||
|
pub fn new(file: R, password: &[u8]) -> ZipCryptoReader<R> {
|
||||||
|
let mut result = ZipCryptoReader {
|
||||||
|
file,
|
||||||
|
keys: ZipCryptoKeys::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Key the cipher by updating the keys with the password.
|
||||||
|
for byte in password.iter() {
|
||||||
|
result.keys.update(*byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read the ZipCrypto header bytes and validate the password.
|
||||||
|
pub fn validate(
|
||||||
|
mut self,
|
||||||
|
validator: ZipCryptoValidator,
|
||||||
|
) -> Result<Option<ZipCryptoReaderValid<R>>, std::io::Error> {
|
||||||
|
// ZipCrypto prefixes a file with a 12 byte header
|
||||||
|
let mut header_buf = [0u8; 12];
|
||||||
|
self.file.read_exact(&mut header_buf)?;
|
||||||
|
for byte in header_buf.iter_mut() {
|
||||||
|
*byte = self.keys.decrypt_byte(*byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
match validator {
|
||||||
|
ZipCryptoValidator::PkzipCrc32(crc32_plaintext) => {
|
||||||
|
// PKZIP before 2.0 used 2 byte CRC check.
|
||||||
|
// PKZIP 2.0+ used 1 byte CRC check. It's more secure.
|
||||||
|
// We also use 1 byte CRC.
|
||||||
|
|
||||||
|
if (crc32_plaintext >> 24) as u8 != header_buf[11] {
|
||||||
|
return Ok(None); // Wrong password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ZipCryptoValidator::InfoZipMsdosTime(last_mod_time) => {
|
||||||
|
// Info-ZIP modification to ZipCrypto format:
|
||||||
|
// If bit 3 of the general purpose bit flag is set
|
||||||
|
// (indicates that the file uses a data-descriptor section),
|
||||||
|
// it uses high byte of 16-bit File Time.
|
||||||
|
// Info-ZIP code probably writes 2 bytes of File Time.
|
||||||
|
// We check only 1 byte.
|
||||||
|
|
||||||
|
if (last_mod_time >> 8) as u8 != header_buf[11] {
|
||||||
|
return Ok(None); // Wrong password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Some(ZipCryptoReaderValid { reader: self }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A ZipCrypto reader with verified password
|
||||||
|
pub struct ZipCryptoReaderValid<R> {
|
||||||
|
reader: ZipCryptoReader<R>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: std::io::Read> std::io::Read for ZipCryptoReaderValid<R> {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||||
|
// Note: There might be potential for optimization. Inspiration can be found at:
|
||||||
|
// https://github.com/kornelski/7z/blob/master/CPP/7zip/Crypto/ZipCrypto.cpp
|
||||||
|
|
||||||
|
let result = self.reader.file.read(buf);
|
||||||
|
for byte in buf.iter_mut() {
|
||||||
|
*byte = self.reader.keys.decrypt_byte(*byte);
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: std::io::Read> ZipCryptoReaderValid<R> {
|
||||||
|
/// Consumes this decoder, returning the underlying reader.
|
||||||
|
pub fn into_inner(self) -> R {
|
||||||
|
self.reader.file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static CRCTABLE: [u32; 256] = [
|
||||||
|
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
|
||||||
|
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
|
||||||
|
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||||
|
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
|
||||||
|
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
|
||||||
|
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||||
|
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
|
||||||
|
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
|
||||||
|
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||||
|
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
|
||||||
|
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
|
||||||
|
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||||
|
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
|
||||||
|
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
|
||||||
|
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||||
|
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
|
||||||
|
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
|
||||||
|
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||||
|
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
|
||||||
|
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
|
||||||
|
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||||
|
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
|
||||||
|
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
|
||||||
|
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||||
|
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
|
||||||
|
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
|
||||||
|
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||||
|
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
|
||||||
|
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
|
||||||
|
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||||
|
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
|
||||||
|
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
|
||||||
|
];
|
|
@ -0,0 +1,80 @@
|
||||||
|
#![cfg(feature = "aes-crypto")]
|
||||||
|
|
||||||
|
use std::io::{self, Read};
|
||||||
|
use zip::ZipArchive;
|
||||||
|
|
||||||
|
const SECRET_CONTENT: &str = "Lorem ipsum dolor sit amet";
|
||||||
|
|
||||||
|
const PASSWORD: &[u8] = b"helloworld";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn aes256_encrypted_uncompressed_file() {
|
||||||
|
let mut v = Vec::new();
|
||||||
|
v.extend_from_slice(include_bytes!("data/aes_archive.zip"));
|
||||||
|
let mut archive = ZipArchive::new(io::Cursor::new(v)).expect("couldn't open test zip file");
|
||||||
|
|
||||||
|
let mut file = archive
|
||||||
|
.by_name_decrypt("secret_data_256_uncompressed", PASSWORD)
|
||||||
|
.expect("couldn't find file in archive")
|
||||||
|
.expect("invalid password");
|
||||||
|
assert_eq!("secret_data_256_uncompressed", file.name());
|
||||||
|
|
||||||
|
let mut content = String::new();
|
||||||
|
file.read_to_string(&mut content)
|
||||||
|
.expect("couldn't read encrypted file");
|
||||||
|
assert_eq!(SECRET_CONTENT, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn aes256_encrypted_file() {
|
||||||
|
let mut v = Vec::new();
|
||||||
|
v.extend_from_slice(include_bytes!("data/aes_archive.zip"));
|
||||||
|
let mut archive = ZipArchive::new(io::Cursor::new(v)).expect("couldn't open test zip file");
|
||||||
|
|
||||||
|
let mut file = archive
|
||||||
|
.by_name_decrypt("secret_data_256", PASSWORD)
|
||||||
|
.expect("couldn't find file in archive")
|
||||||
|
.expect("invalid password");
|
||||||
|
assert_eq!("secret_data_256", file.name());
|
||||||
|
|
||||||
|
let mut content = String::new();
|
||||||
|
file.read_to_string(&mut content)
|
||||||
|
.expect("couldn't read encrypted and compressed file");
|
||||||
|
assert_eq!(SECRET_CONTENT, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn aes192_encrypted_file() {
|
||||||
|
let mut v = Vec::new();
|
||||||
|
v.extend_from_slice(include_bytes!("data/aes_archive.zip"));
|
||||||
|
let mut archive = ZipArchive::new(io::Cursor::new(v)).expect("couldn't open test zip file");
|
||||||
|
|
||||||
|
let mut file = archive
|
||||||
|
.by_name_decrypt("secret_data_192", PASSWORD)
|
||||||
|
.expect("couldn't find file in archive")
|
||||||
|
.expect("invalid password");
|
||||||
|
assert_eq!("secret_data_192", file.name());
|
||||||
|
|
||||||
|
let mut content = String::new();
|
||||||
|
file.read_to_string(&mut content)
|
||||||
|
.expect("couldn't read encrypted file");
|
||||||
|
assert_eq!(SECRET_CONTENT, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn aes128_encrypted_file() {
|
||||||
|
let mut v = Vec::new();
|
||||||
|
v.extend_from_slice(include_bytes!("data/aes_archive.zip"));
|
||||||
|
let mut archive = ZipArchive::new(io::Cursor::new(v)).expect("couldn't open test zip file");
|
||||||
|
|
||||||
|
let mut file = archive
|
||||||
|
.by_name_decrypt("secret_data_128", PASSWORD)
|
||||||
|
.expect("couldn't find file in archive")
|
||||||
|
.expect("invalid password");
|
||||||
|
assert_eq!("secret_data_128", file.name());
|
||||||
|
|
||||||
|
let mut content = String::new();
|
||||||
|
file.read_to_string(&mut content)
|
||||||
|
.expect("couldn't read encrypted file");
|
||||||
|
assert_eq!(SECRET_CONTENT, content);
|
||||||
|
}
|
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
|
@ -0,0 +1,205 @@
|
||||||
|
use byteorder::{LittleEndian, WriteBytesExt};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
use std::io::{Cursor, Seek};
|
||||||
|
use std::iter::FromIterator;
|
||||||
|
use zip::write::FileOptions;
|
||||||
|
use zip::{CompressionMethod, SUPPORTED_COMPRESSION_METHODS};
|
||||||
|
|
||||||
|
// This test asserts that after creating a zip file, then reading its contents back out,
|
||||||
|
// the extracted data will *always* be exactly the same as the original data.
|
||||||
|
#[test]
|
||||||
|
fn end_to_end() {
|
||||||
|
for &method in SUPPORTED_COMPRESSION_METHODS {
|
||||||
|
let file = &mut Cursor::new(Vec::new());
|
||||||
|
|
||||||
|
println!("Writing file with {} compression", method);
|
||||||
|
write_test_archive(file, method).expect("Couldn't write test zip archive");
|
||||||
|
|
||||||
|
println!("Checking file contents");
|
||||||
|
check_archive_file(file, ENTRY_NAME, Some(method), LOREM_IPSUM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test asserts that after copying a `ZipFile` to a new `ZipWriter`, then reading its
|
||||||
|
// contents back out, the extracted data will *always* be exactly the same as the original data.
|
||||||
|
#[test]
|
||||||
|
fn copy() {
|
||||||
|
for &method in SUPPORTED_COMPRESSION_METHODS {
|
||||||
|
let src_file = &mut Cursor::new(Vec::new());
|
||||||
|
write_test_archive(src_file, method).expect("Couldn't write to test file");
|
||||||
|
|
||||||
|
let mut tgt_file = &mut Cursor::new(Vec::new());
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut src_archive = zip::ZipArchive::new(src_file).unwrap();
|
||||||
|
let mut zip = zip::ZipWriter::new(&mut tgt_file);
|
||||||
|
|
||||||
|
{
|
||||||
|
let file = src_archive
|
||||||
|
.by_name(ENTRY_NAME)
|
||||||
|
.expect("Missing expected file");
|
||||||
|
|
||||||
|
zip.raw_copy_file(file).expect("Couldn't copy file");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let file = src_archive
|
||||||
|
.by_name(ENTRY_NAME)
|
||||||
|
.expect("Missing expected file");
|
||||||
|
|
||||||
|
zip.raw_copy_file_rename(file, COPY_ENTRY_NAME)
|
||||||
|
.expect("Couldn't copy and rename file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tgt_archive = zip::ZipArchive::new(tgt_file).unwrap();
|
||||||
|
|
||||||
|
check_archive_file_contents(&mut tgt_archive, ENTRY_NAME, LOREM_IPSUM);
|
||||||
|
check_archive_file_contents(&mut tgt_archive, COPY_ENTRY_NAME, LOREM_IPSUM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test asserts that after appending to a `ZipWriter`, then reading its contents back out,
|
||||||
|
// both the prior data and the appended data will be exactly the same as their originals.
|
||||||
|
#[test]
|
||||||
|
fn append() {
|
||||||
|
for &method in SUPPORTED_COMPRESSION_METHODS {
|
||||||
|
let mut file = &mut Cursor::new(Vec::new());
|
||||||
|
write_test_archive(file, method).expect("Couldn't write to test file");
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut zip = zip::ZipWriter::new_append(&mut file).unwrap();
|
||||||
|
zip.start_file(
|
||||||
|
COPY_ENTRY_NAME,
|
||||||
|
FileOptions::default().compression_method(method),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
zip.write_all(LOREM_IPSUM).unwrap();
|
||||||
|
zip.finish().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut zip = zip::ZipArchive::new(&mut file).unwrap();
|
||||||
|
check_archive_file_contents(&mut zip, ENTRY_NAME, LOREM_IPSUM);
|
||||||
|
check_archive_file_contents(&mut zip, COPY_ENTRY_NAME, LOREM_IPSUM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a test zip archive to buffer.
|
||||||
|
fn write_test_archive(
|
||||||
|
file: &mut Cursor<Vec<u8>>,
|
||||||
|
method: CompressionMethod,
|
||||||
|
) -> zip::result::ZipResult<()> {
|
||||||
|
let mut zip = zip::ZipWriter::new(file);
|
||||||
|
|
||||||
|
zip.add_directory("test/", Default::default())?;
|
||||||
|
|
||||||
|
let options = FileOptions::default()
|
||||||
|
.compression_method(method)
|
||||||
|
.unix_permissions(0o755);
|
||||||
|
|
||||||
|
zip.start_file("test/☃.txt", options)?;
|
||||||
|
zip.write_all(b"Hello, World!\n")?;
|
||||||
|
|
||||||
|
zip.start_file_with_extra_data("test_with_extra_data/🐢.txt", options)?;
|
||||||
|
zip.write_u16::<LittleEndian>(0xbeef)?;
|
||||||
|
zip.write_u16::<LittleEndian>(EXTRA_DATA.len() as u16)?;
|
||||||
|
zip.write_all(EXTRA_DATA)?;
|
||||||
|
zip.end_extra_data()?;
|
||||||
|
zip.write_all(b"Hello, World! Again.\n")?;
|
||||||
|
|
||||||
|
zip.start_file(ENTRY_NAME, options)?;
|
||||||
|
zip.write_all(LOREM_IPSUM)?;
|
||||||
|
|
||||||
|
zip.finish()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load an archive from buffer and check for test data.
|
||||||
|
fn check_test_archive<R: Read + Seek>(zip_file: R) -> zip::result::ZipResult<zip::ZipArchive<R>> {
|
||||||
|
let mut archive = zip::ZipArchive::new(zip_file).unwrap();
|
||||||
|
|
||||||
|
// Check archive contains expected file names.
|
||||||
|
{
|
||||||
|
let expected_file_names = [
|
||||||
|
"test/",
|
||||||
|
"test/☃.txt",
|
||||||
|
"test_with_extra_data/🐢.txt",
|
||||||
|
ENTRY_NAME,
|
||||||
|
];
|
||||||
|
let expected_file_names = HashSet::from_iter(expected_file_names.iter().copied());
|
||||||
|
let file_names = archive.file_names().collect::<HashSet<_>>();
|
||||||
|
assert_eq!(file_names, expected_file_names);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check an archive file for extra data field contents.
|
||||||
|
{
|
||||||
|
let file_with_extra_data = archive.by_name("test_with_extra_data/🐢.txt")?;
|
||||||
|
let mut extra_data = Vec::new();
|
||||||
|
extra_data.write_u16::<LittleEndian>(0xbeef)?;
|
||||||
|
extra_data.write_u16::<LittleEndian>(EXTRA_DATA.len() as u16)?;
|
||||||
|
extra_data.write_all(EXTRA_DATA)?;
|
||||||
|
assert_eq!(file_with_extra_data.extra_data(), extra_data.as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(archive)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read a file in the archive as a string.
|
||||||
|
fn read_archive_file<R: Read + Seek>(
|
||||||
|
archive: &mut zip::ZipArchive<R>,
|
||||||
|
name: &str,
|
||||||
|
) -> zip::result::ZipResult<String> {
|
||||||
|
let mut file = archive.by_name(name)?;
|
||||||
|
|
||||||
|
let mut contents = String::new();
|
||||||
|
file.read_to_string(&mut contents).unwrap();
|
||||||
|
|
||||||
|
Ok(contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check a file in the archive contains expected data and properties.
|
||||||
|
fn check_archive_file(
|
||||||
|
zip_file: &mut Cursor<Vec<u8>>,
|
||||||
|
name: &str,
|
||||||
|
expected_method: Option<CompressionMethod>,
|
||||||
|
expected_data: &[u8],
|
||||||
|
) {
|
||||||
|
let mut archive = check_test_archive(zip_file).unwrap();
|
||||||
|
|
||||||
|
if let Some(expected_method) = expected_method {
|
||||||
|
// Check the file's compression method.
|
||||||
|
let file = archive.by_name(name).unwrap();
|
||||||
|
let real_method = file.compression();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
expected_method, real_method,
|
||||||
|
"File does not have expected compression method"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
check_archive_file_contents(&mut archive, name, expected_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check a file in the archive contains the given data.
|
||||||
|
fn check_archive_file_contents<R: Read + Seek>(
|
||||||
|
archive: &mut zip::ZipArchive<R>,
|
||||||
|
name: &str,
|
||||||
|
expected: &[u8],
|
||||||
|
) {
|
||||||
|
let file_contents: String = read_archive_file(archive, name).unwrap();
|
||||||
|
assert_eq!(file_contents.as_bytes(), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
const LOREM_IPSUM : &[u8] = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. In tellus elit, tristique vitae mattis egestas, ultricies vitae risus. Quisque sit amet quam ut urna aliquet
|
||||||
|
molestie. Proin blandit ornare dui, a tempor nisl accumsan in. Praesent a consequat felis. Morbi metus diam, auctor in auctor vel, feugiat id odio. Curabitur ex ex,
|
||||||
|
dictum quis auctor quis, suscipit id lorem. Aliquam vestibulum dolor nec enim vehicula, porta tristique augue tincidunt. Vivamus ut gravida est. Sed pellentesque, dolor
|
||||||
|
vitae tristique consectetur, neque lectus pulvinar dui, sed feugiat purus diam id lectus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per
|
||||||
|
inceptos himenaeos. Maecenas feugiat velit in ex ultrices scelerisque id id neque.
|
||||||
|
";
|
||||||
|
|
||||||
|
const EXTRA_DATA: &[u8] = b"Extra Data";
|
||||||
|
|
||||||
|
const ENTRY_NAME: &str = "test/lorem_ipsum.txt";
|
||||||
|
|
||||||
|
const COPY_ENTRY_NAME: &str = "test/lorem_ipsum_renamed.txt";
|
|
@ -1,31 +1,24 @@
|
||||||
extern crate zip;
|
|
||||||
|
|
||||||
use zip::read::ZipArchive;
|
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
use zip::read::ZipArchive;
|
||||||
|
|
||||||
const BUF : &[u8] = &[
|
const BUF: &[u8] = &[
|
||||||
0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x1c, 0x00, 0x69, 0x6e,
|
||||||
0x00, 0x00, 0x12, 0x00, 0x1c, 0x00, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69,
|
0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f,
|
||||||
0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f,
|
0x55, 0x54, 0x09, 0x00, 0x03, 0xf4, 0x5c, 0x88, 0x5a, 0xf4, 0x5c, 0x88, 0x5a, 0x75, 0x78, 0x0b,
|
||||||
0x55, 0x54, 0x09, 0x00, 0x03, 0xf4, 0x5c, 0x88, 0x5a, 0xf4, 0x5c, 0x88,
|
0x00, 0x01, 0x04, 0xe8, 0x03, 0x00, 0x00, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x01, 0x02,
|
||||||
0x5a, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xe8, 0x03, 0x00, 0x00, 0x04,
|
0x1e, 0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x0a, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x01, 0x02, 0x1e, 0x03, 0x0a, 0x00,
|
0x00, // time part: 0 seconds, 0 minutes, 0 hours
|
||||||
0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, // date part: day 0 (invalid), month 0 (invalid), year 0 (1980)
|
||||||
0x00, 0x00, // time part: 0 seconds, 0 minutes, 0 hours
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x18, 0x00,
|
||||||
0x00, 0x00, // date part: day 0 (invalid), month 0 (invalid), year 0 (1980)
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0xed, 0x41, 0x00, 0x00, 0x00, 0x00, 0x69, 0x6e,
|
||||||
0x00, 0x00, 0x00, 0x00,
|
0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x18, 0x00,
|
0x55, 0x54, 0x05, 0x00, 0x03, 0xf4, 0x5c, 0x88, 0x5a, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xe8,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0xed, 0x41, 0x00, 0x00,
|
0x03, 0x00, 0x00, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x74, 0x69,
|
0x01, 0x00, 0x01, 0x00, 0x58, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x55, 0x54, 0x05, 0x00,
|
|
||||||
0x03, 0xf4, 0x5c, 0x88, 0x5a, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xe8,
|
|
||||||
0x03, 0x00, 0x00, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x05, 0x06,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x58, 0x00, 0x00, 0x00,
|
|
||||||
0x4c, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
||||||
];
|
];
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn main() {
|
fn invalid_date() {
|
||||||
let _archive = ZipArchive::new(Cursor::new(BUF)).unwrap();
|
let _archive = ZipArchive::new(Cursor::new(BUF)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
use zip::result::ZipError;
|
||||||
|
|
||||||
|
const BUF: &[u8] = &[
|
||||||
|
0, 80, 75, 1, 2, 127, 120, 0, 3, 3, 75, 80, 232, 3, 0, 0, 0, 0, 0, 0, 3, 0, 1, 0, 7, 0, 0, 0,
|
||||||
|
0, 65, 0, 1, 0, 0, 0, 4, 0, 0, 224, 255, 0, 255, 255, 255, 255, 255, 255, 20, 39, 221, 221,
|
||||||
|
221, 221, 221, 221, 205, 221, 221, 221, 42, 221, 221, 221, 221, 221, 221, 221, 221, 38, 34, 34,
|
||||||
|
219, 80, 75, 5, 6, 0, 0, 0, 0, 5, 96, 0, 1, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 234, 236, 124,
|
||||||
|
221, 221, 37, 221, 221, 221, 221, 221, 129, 4, 0, 0, 221, 221, 80, 75, 1, 2, 127, 120, 0, 4, 0,
|
||||||
|
0, 2, 127, 120, 0, 79, 75, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0,
|
||||||
|
234, 0, 0, 0, 3, 8, 4, 232, 3, 0, 0, 0, 255, 255, 255, 255, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 3, 0,
|
||||||
|
221, 209, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 58, 58, 42, 75, 9, 2, 127,
|
||||||
|
120, 0, 99, 99, 99, 99, 99, 99, 94, 7, 0, 0, 0, 0, 0, 0, 213, 213, 213, 213, 213, 213, 213,
|
||||||
|
213, 213, 7, 0, 0, 211, 211, 211, 211, 124, 236, 99, 99, 99, 94, 7, 0, 0, 0, 0, 0, 0, 213, 213,
|
||||||
|
213, 213, 213, 213, 213, 213, 213, 7, 0, 0, 211, 211, 211, 211, 124, 236, 234, 0, 0, 0, 3, 8,
|
||||||
|
0, 0, 0, 12, 0, 0, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0, 0, 0, 58, 58, 58, 42, 175, 221, 253, 221,
|
||||||
|
221, 221, 221, 221, 80, 75, 9, 2, 127, 120, 0, 99, 99, 99, 99, 99, 99, 94, 7, 0, 0, 0, 0, 0, 0,
|
||||||
|
213, 213, 213, 213, 213, 213, 213, 213, 213, 7, 0, 0, 211, 211, 211, 211, 124, 236, 221, 221,
|
||||||
|
221, 221, 221, 80, 75, 9, 2, 127, 120, 0, 99, 99, 99, 99, 99, 99, 94, 7, 0, 0, 0, 0, 0, 0, 213,
|
||||||
|
213, 213, 213, 213, 213, 213, 213, 213, 7, 0, 0, 211, 211, 211, 211, 124, 236,
|
||||||
|
];
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_header() {
|
||||||
|
let reader = std::io::Cursor::new(&BUF);
|
||||||
|
let archive = zip::ZipArchive::new(reader);
|
||||||
|
match archive {
|
||||||
|
Err(ZipError::InvalidArchive(_)) => {}
|
||||||
|
value => panic!("Unexpected value: {:?}", value),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,211 @@
|
||||||
|
// The following is a hexdump of a zip64 file containing the following files:
|
||||||
|
// zero4400: 4400 MB of zeroes
|
||||||
|
// zero100: 100 MB of zeroes
|
||||||
|
// zero4400_2: 4400 MB of zeroes
|
||||||
|
//
|
||||||
|
// 00000000 50 4b 03 04 2d 00 00 00 00 00 1b 6e 51 4d 66 82 |PK..-......nQMf.|
|
||||||
|
// 00000010 13 da ff ff ff ff ff ff ff ff 08 00 30 00 7a 65 |............0.ze|
|
||||||
|
// 00000020 72 6f 34 34 30 30 55 54 09 00 03 a5 21 c7 5b db |ro4400UT....!.[.|
|
||||||
|
// 00000030 21 c7 5b 75 78 0b 00 01 04 e8 03 00 00 04 e8 03 |!.[ux...........|
|
||||||
|
// 00000040 00 00 01 00 10 00 00 00 00 13 01 00 00 00 00 00 |................|
|
||||||
|
// 00000050 00 13 01 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||||
|
// 00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||||
|
// *
|
||||||
|
// 113000050 00 00 00 00 00 00 50 4b 03 04 0a 00 00 00 00 00 |......PK........|
|
||||||
|
// 113000060 2b 6e 51 4d 98 23 28 4b 00 00 40 06 00 00 40 06 |+nQM.#(K..@...@.|
|
||||||
|
// 113000070 07 00 1c 00 7a 65 72 6f 31 30 30 55 54 09 00 03 |....zero100UT...|
|
||||||
|
// 113000080 c2 21 c7 5b c2 21 c7 5b 75 78 0b 00 01 04 e8 03 |.!.[.!.[ux......|
|
||||||
|
// 113000090 00 00 04 e8 03 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||||
|
// 1130000a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||||
|
// *
|
||||||
|
// 119400090 00 00 00 00 00 00 00 50 4b 03 04 2d 00 00 00 00 |.......PK..-....|
|
||||||
|
// 1194000a0 00 3b 6e 51 4d 66 82 13 da ff ff ff ff ff ff ff |.;nQMf..........|
|
||||||
|
// 1194000b0 ff 0a 00 30 00 7a 65 72 6f 34 34 30 30 5f 32 55 |...0.zero4400_2U|
|
||||||
|
// 1194000c0 54 09 00 03 e2 21 c7 5b db 21 c7 5b 75 78 0b 00 |T....!.[.!.[ux..|
|
||||||
|
// 1194000d0 01 04 e8 03 00 00 04 e8 03 00 00 01 00 10 00 00 |................|
|
||||||
|
// 1194000e0 00 00 13 01 00 00 00 00 00 00 13 01 00 00 00 00 |................|
|
||||||
|
// 1194000f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||||
|
// *
|
||||||
|
// 22c4000e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 50 |...............P|
|
||||||
|
// 22c4000f0 4b 01 02 1e 03 2d 00 00 00 00 00 1b 6e 51 4d 66 |K....-......nQMf|
|
||||||
|
// 22c400100 82 13 da ff ff ff ff ff ff ff ff 08 00 2c 00 00 |.............,..|
|
||||||
|
// 22c400110 00 00 00 00 00 00 00 a4 81 00 00 00 00 7a 65 72 |.............zer|
|
||||||
|
// 22c400120 6f 34 34 30 30 55 54 05 00 03 a5 21 c7 5b 75 78 |o4400UT....!.[ux|
|
||||||
|
// 22c400130 0b 00 01 04 e8 03 00 00 04 e8 03 00 00 01 00 10 |................|
|
||||||
|
// 22c400140 00 00 00 00 13 01 00 00 00 00 00 00 13 01 00 00 |................|
|
||||||
|
// 22c400150 00 50 4b 01 02 1e 03 0a 00 00 00 00 00 2b 6e 51 |.PK..........+nQ|
|
||||||
|
// 22c400160 4d 98 23 28 4b 00 00 40 06 00 00 40 06 07 00 24 |M.#(K..@...@...$|
|
||||||
|
// 22c400170 00 00 00 00 00 00 00 00 00 a4 81 ff ff ff ff 7a |...............z|
|
||||||
|
// 22c400180 65 72 6f 31 30 30 55 54 05 00 03 c2 21 c7 5b 75 |ero100UT....!.[u|
|
||||||
|
// 22c400190 78 0b 00 01 04 e8 03 00 00 04 e8 03 00 00 01 00 |x...............|
|
||||||
|
// 22c4001a0 08 00 56 00 00 13 01 00 00 00 50 4b 01 02 1e 03 |..V.......PK....|
|
||||||
|
// 22c4001b0 2d 00 00 00 00 00 3b 6e 51 4d 66 82 13 da ff ff |-.....;nQMf.....|
|
||||||
|
// 22c4001c0 ff ff ff ff ff ff 0a 00 34 00 00 00 00 00 00 00 |........4.......|
|
||||||
|
// 22c4001d0 00 00 a4 81 ff ff ff ff 7a 65 72 6f 34 34 30 30 |........zero4400|
|
||||||
|
// 22c4001e0 5f 32 55 54 05 00 03 e2 21 c7 5b 75 78 0b 00 01 |_2UT....!.[ux...|
|
||||||
|
// 22c4001f0 04 e8 03 00 00 04 e8 03 00 00 01 00 18 00 00 00 |................|
|
||||||
|
// 22c400200 00 13 01 00 00 00 00 00 00 13 01 00 00 00 97 00 |................|
|
||||||
|
// 22c400210 40 19 01 00 00 00 50 4b 06 06 2c 00 00 00 00 00 |@.....PK..,.....|
|
||||||
|
// 22c400220 00 00 1e 03 2d 00 00 00 00 00 00 00 00 00 03 00 |....-...........|
|
||||||
|
// 22c400230 00 00 00 00 00 00 03 00 00 00 00 00 00 00 27 01 |..............'.|
|
||||||
|
// 22c400240 00 00 00 00 00 00 ef 00 40 2c 02 00 00 00 50 4b |........@,....PK|
|
||||||
|
// 22c400250 06 07 00 00 00 00 16 02 40 2c 02 00 00 00 01 00 |........@,......|
|
||||||
|
// 22c400260 00 00 50 4b 05 06 00 00 00 00 03 00 03 00 27 01 |..PK..........'.|
|
||||||
|
// 22c400270 00 00 ff ff ff ff 00 00 |........|
|
||||||
|
// 22c400278
|
||||||
|
use std::io::{self, Read, Seek, SeekFrom};
|
||||||
|
|
||||||
|
const BLOCK1_LENGTH: u64 = 0x60;
|
||||||
|
const BLOCK1: [u8; BLOCK1_LENGTH as usize] = [
|
||||||
|
0x50, 0x4b, 0x03, 0x04, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x6e, 0x51, 0x4d, 0x66, 0x82,
|
||||||
|
0x13, 0xda, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x30, 0x00, 0x7a, 0x65,
|
||||||
|
0x72, 0x6f, 0x34, 0x34, 0x30, 0x30, 0x55, 0x54, 0x09, 0x00, 0x03, 0xa5, 0x21, 0xc7, 0x5b, 0xdb,
|
||||||
|
0x21, 0xc7, 0x5b, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xe8, 0x03, 0x00, 0x00, 0x04, 0xe8, 0x03,
|
||||||
|
0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x13, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x13, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
];
|
||||||
|
|
||||||
|
const BLOCK2_LENGTH: u64 = 0x50;
|
||||||
|
const BLOCK2: [u8; BLOCK2_LENGTH as usize] = [
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x2b, 0x6e, 0x51, 0x4d, 0x98, 0x23, 0x28, 0x4b, 0x00, 0x00, 0x40, 0x06, 0x00, 0x00, 0x40, 0x06,
|
||||||
|
0x07, 0x00, 0x1c, 0x00, 0x7a, 0x65, 0x72, 0x6f, 0x31, 0x30, 0x30, 0x55, 0x54, 0x09, 0x00, 0x03,
|
||||||
|
0xc2, 0x21, 0xc7, 0x5b, 0xc2, 0x21, 0xc7, 0x5b, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xe8, 0x03,
|
||||||
|
0x00, 0x00, 0x04, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
];
|
||||||
|
|
||||||
|
const BLOCK3_LENGTH: u64 = 0x60;
|
||||||
|
const BLOCK3: [u8; BLOCK3_LENGTH as usize] = [
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x2d, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x3b, 0x6e, 0x51, 0x4d, 0x66, 0x82, 0x13, 0xda, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0x0a, 0x00, 0x30, 0x00, 0x7a, 0x65, 0x72, 0x6f, 0x34, 0x34, 0x30, 0x30, 0x5f, 0x32, 0x55,
|
||||||
|
0x54, 0x09, 0x00, 0x03, 0xe2, 0x21, 0xc7, 0x5b, 0xdb, 0x21, 0xc7, 0x5b, 0x75, 0x78, 0x0b, 0x00,
|
||||||
|
0x01, 0x04, 0xe8, 0x03, 0x00, 0x00, 0x04, 0xe8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x13, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
];
|
||||||
|
|
||||||
|
const BLOCK4_LENGTH: u64 = 0x198;
|
||||||
|
const BLOCK4: [u8; BLOCK4_LENGTH as usize] = [
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50,
|
||||||
|
0x4b, 0x01, 0x02, 0x1e, 0x03, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x6e, 0x51, 0x4d, 0x66,
|
||||||
|
0x82, 0x13, 0xda, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x2c, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x65, 0x72,
|
||||||
|
0x6f, 0x34, 0x34, 0x30, 0x30, 0x55, 0x54, 0x05, 0x00, 0x03, 0xa5, 0x21, 0xc7, 0x5b, 0x75, 0x78,
|
||||||
|
0x0b, 0x00, 0x01, 0x04, 0xe8, 0x03, 0x00, 0x00, 0x04, 0xe8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x10,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x13, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x01, 0x00, 0x00,
|
||||||
|
0x00, 0x50, 0x4b, 0x01, 0x02, 0x1e, 0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x6e, 0x51,
|
||||||
|
0x4d, 0x98, 0x23, 0x28, 0x4b, 0x00, 0x00, 0x40, 0x06, 0x00, 0x00, 0x40, 0x06, 0x07, 0x00, 0x24,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x81, 0xff, 0xff, 0xff, 0xff, 0x7a,
|
||||||
|
0x65, 0x72, 0x6f, 0x31, 0x30, 0x30, 0x55, 0x54, 0x05, 0x00, 0x03, 0xc2, 0x21, 0xc7, 0x5b, 0x75,
|
||||||
|
0x78, 0x0b, 0x00, 0x01, 0x04, 0xe8, 0x03, 0x00, 0x00, 0x04, 0xe8, 0x03, 0x00, 0x00, 0x01, 0x00,
|
||||||
|
0x08, 0x00, 0x56, 0x00, 0x00, 0x13, 0x01, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x01, 0x02, 0x1e, 0x03,
|
||||||
|
0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x6e, 0x51, 0x4d, 0x66, 0x82, 0x13, 0xda, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xa4, 0x81, 0xff, 0xff, 0xff, 0xff, 0x7a, 0x65, 0x72, 0x6f, 0x34, 0x34, 0x30, 0x30,
|
||||||
|
0x5f, 0x32, 0x55, 0x54, 0x05, 0x00, 0x03, 0xe2, 0x21, 0xc7, 0x5b, 0x75, 0x78, 0x0b, 0x00, 0x01,
|
||||||
|
0x04, 0xe8, 0x03, 0x00, 0x00, 0x04, 0xe8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x13, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x01, 0x00, 0x00, 0x00, 0x97, 0x00,
|
||||||
|
0x40, 0x19, 0x01, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x06, 0x06, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x1e, 0x03, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x01,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0x00, 0x40, 0x2c, 0x02, 0x00, 0x00, 0x00, 0x50, 0x4b,
|
||||||
|
0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x16, 0x02, 0x40, 0x2c, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||||
|
0x00, 0x00, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x27, 0x01,
|
||||||
|
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
|
||||||
|
];
|
||||||
|
|
||||||
|
const BLOCK1_START: u64 = 0x000000000;
|
||||||
|
const BLOCK2_START: u64 = 0x113000050;
|
||||||
|
const BLOCK3_START: u64 = 0x119400090;
|
||||||
|
const BLOCK4_START: u64 = 0x22c4000e0;
|
||||||
|
|
||||||
|
const BLOCK1_END: u64 = BLOCK1_START + BLOCK1_LENGTH - 1;
|
||||||
|
const BLOCK2_END: u64 = BLOCK2_START + BLOCK2_LENGTH - 1;
|
||||||
|
const BLOCK3_END: u64 = BLOCK3_START + BLOCK3_LENGTH - 1;
|
||||||
|
const BLOCK4_END: u64 = BLOCK4_START + BLOCK4_LENGTH - 1;
|
||||||
|
|
||||||
|
const TOTAL_LENGTH: u64 = BLOCK4_START + BLOCK4_LENGTH;
|
||||||
|
|
||||||
|
struct Zip64File {
|
||||||
|
pointer: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Zip64File {
|
||||||
|
fn new() -> Self {
|
||||||
|
Zip64File { pointer: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Seek for Zip64File {
|
||||||
|
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||||
|
match pos {
|
||||||
|
SeekFrom::Start(offset) => {
|
||||||
|
self.pointer = offset;
|
||||||
|
}
|
||||||
|
SeekFrom::End(offset) => {
|
||||||
|
if offset > 0 || offset < -(TOTAL_LENGTH as i64) {
|
||||||
|
return Err(io::Error::new(io::ErrorKind::Other, "Invalid seek offset"));
|
||||||
|
}
|
||||||
|
self.pointer = (TOTAL_LENGTH as i64 + offset) as u64;
|
||||||
|
}
|
||||||
|
SeekFrom::Current(offset) => {
|
||||||
|
let seekpos = self.pointer as i64 + offset;
|
||||||
|
if seekpos < 0 || seekpos as u64 > TOTAL_LENGTH {
|
||||||
|
return Err(io::Error::new(io::ErrorKind::Other, "Invalid seek offset"));
|
||||||
|
}
|
||||||
|
self.pointer = seekpos as u64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(self.pointer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for Zip64File {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
if self.pointer >= TOTAL_LENGTH {
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
match self.pointer {
|
||||||
|
BLOCK1_START..=BLOCK1_END => {
|
||||||
|
buf[0] = BLOCK1[(self.pointer - BLOCK1_START) as usize];
|
||||||
|
}
|
||||||
|
BLOCK2_START..=BLOCK2_END => {
|
||||||
|
buf[0] = BLOCK2[(self.pointer - BLOCK2_START) as usize];
|
||||||
|
}
|
||||||
|
BLOCK3_START..=BLOCK3_END => {
|
||||||
|
buf[0] = BLOCK3[(self.pointer - BLOCK3_START) as usize];
|
||||||
|
}
|
||||||
|
BLOCK4_START..=BLOCK4_END => {
|
||||||
|
buf[0] = BLOCK4[(self.pointer - BLOCK4_START) as usize];
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
buf[0] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.pointer += 1;
|
||||||
|
Ok(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn zip64_large() {
|
||||||
|
let zipfile = Zip64File::new();
|
||||||
|
let mut archive = zip::ZipArchive::new(zipfile).unwrap();
|
||||||
|
let mut buf = [0u8; 32];
|
||||||
|
|
||||||
|
for i in 0..archive.len() {
|
||||||
|
let mut file = archive.by_index(i).unwrap();
|
||||||
|
let outpath = file.enclosed_name().unwrap();
|
||||||
|
println!(
|
||||||
|
"Entry {} has name \"{}\" ({} bytes)",
|
||||||
|
i,
|
||||||
|
outpath.display(),
|
||||||
|
file.size()
|
||||||
|
);
|
||||||
|
|
||||||
|
match file.read_exact(&mut buf) {
|
||||||
|
Ok(()) => println!("The first {} bytes are: {:?}", buf.len(), buf),
|
||||||
|
Err(e) => println!("Could not read the file: {:?}", e),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Some zip files can contain garbage after the comment. For example, python zipfile generates
|
||||||
|
// it when opening a zip in 'a' mode:
|
||||||
|
//
|
||||||
|
// >>> from zipfile import ZipFile
|
||||||
|
// >>> with ZipFile('comment_garbage.zip', 'a') as z:
|
||||||
|
// ... z.comment = b'long comment bla bla bla'
|
||||||
|
// ...
|
||||||
|
// >>> with ZipFile('comment_garbage.zip', 'a') as z:
|
||||||
|
// ... z.comment = b'short.'
|
||||||
|
// ...
|
||||||
|
// >>>
|
||||||
|
//
|
||||||
|
// Hexdump:
|
||||||
|
//
|
||||||
|
// 00000000 50 4b 05 06 00 00 00 00 00 00 00 00 00 00 00 00 |PK..............|
|
||||||
|
// 00000010 00 00 00 00 06 00 73 68 6f 72 74 2e 6f 6d 6d 65 |......short.omme|
|
||||||
|
// 00000020 6e 74 20 62 6c 61 20 62 6c 61 20 62 6c 61 |nt bla bla bla|
|
||||||
|
// 0000002e
|
||||||
|
|
||||||
|
use std::io;
|
||||||
|
use zip::ZipArchive;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn correctly_handle_zip_with_garbage_after_comment() {
|
||||||
|
let mut v = Vec::new();
|
||||||
|
v.extend_from_slice(include_bytes!("../tests/data/comment_garbage.zip"));
|
||||||
|
let archive = ZipArchive::new(io::Cursor::new(v)).expect("couldn't open test zip file");
|
||||||
|
|
||||||
|
assert_eq!(archive.comment(), "short.".as_bytes());
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
// The following is a hexdump of a zip file containing the following
|
||||||
|
// ZipCrypto encrypted file:
|
||||||
|
// test.txt: 35 bytes, contents: `abcdefghijklmnopqrstuvwxyz123456789`, password: `test`
|
||||||
|
//
|
||||||
|
// 00000000 50 4b 03 04 14 00 01 00 00 00 54 bd b5 50 2f 20 |PK........T..P/ |
|
||||||
|
// 00000010 79 55 2f 00 00 00 23 00 00 00 08 00 00 00 74 65 |yU/...#.......te|
|
||||||
|
// 00000020 73 74 2e 74 78 74 ca 2d 1d 27 19 19 63 43 77 9a |st.txt.-.'..cCw.|
|
||||||
|
// 00000030 71 76 c9 ec d1 6f d9 f5 22 67 b3 8f 52 b5 41 bc |qv...o.."g..R.A.|
|
||||||
|
// 00000040 5c 36 f2 1d 84 c3 c0 28 3b fd e1 70 c2 cc 0c 11 |\6.....(;..p....|
|
||||||
|
// 00000050 0c c5 95 2f a4 50 4b 01 02 3f 00 14 00 01 00 00 |.../.PK..?......|
|
||||||
|
// 00000060 00 54 bd b5 50 2f 20 79 55 2f 00 00 00 23 00 00 |.T..P/ yU/...#..|
|
||||||
|
// 00000070 00 08 00 24 00 00 00 00 00 00 00 20 00 00 00 00 |...$....... ....|
|
||||||
|
// 00000080 00 00 00 74 65 73 74 2e 74 78 74 0a 00 20 00 00 |...test.txt.. ..|
|
||||||
|
// 00000090 00 00 00 01 00 18 00 31 b2 3b bf b8 2f d6 01 31 |.......1.;../..1|
|
||||||
|
// 000000a0 b2 3b bf b8 2f d6 01 a8 c4 45 bd b8 2f d6 01 50 |.;../....E../..P|
|
||||||
|
// 000000b0 4b 05 06 00 00 00 00 01 00 01 00 5a 00 00 00 55 |K..........Z...U|
|
||||||
|
// 000000c0 00 00 00 00 00 |.....|
|
||||||
|
// 000000c5
|
||||||
|
|
||||||
|
use std::io::Cursor;
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn encrypted_file() {
|
||||||
|
let zip_file_bytes = &mut Cursor::new(vec![
|
||||||
|
0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0x54, 0xbd, 0xb5, 0x50, 0x2f,
|
||||||
|
0x20, 0x79, 0x55, 0x2f, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
|
||||||
|
0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, 0xca, 0x2d, 0x1d, 0x27, 0x19, 0x19, 0x63,
|
||||||
|
0x43, 0x77, 0x9a, 0x71, 0x76, 0xc9, 0xec, 0xd1, 0x6f, 0xd9, 0xf5, 0x22, 0x67, 0xb3, 0x8f,
|
||||||
|
0x52, 0xb5, 0x41, 0xbc, 0x5c, 0x36, 0xf2, 0x1d, 0x84, 0xc3, 0xc0, 0x28, 0x3b, 0xfd, 0xe1,
|
||||||
|
0x70, 0xc2, 0xcc, 0x0c, 0x11, 0x0c, 0xc5, 0x95, 0x2f, 0xa4, 0x50, 0x4b, 0x01, 0x02, 0x3f,
|
||||||
|
0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0x54, 0xbd, 0xb5, 0x50, 0x2f, 0x20, 0x79, 0x55,
|
||||||
|
0x2f, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x08, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74,
|
||||||
|
0x2e, 0x74, 0x78, 0x74, 0x0a, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x18,
|
||||||
|
0x00, 0x31, 0xb2, 0x3b, 0xbf, 0xb8, 0x2f, 0xd6, 0x01, 0x31, 0xb2, 0x3b, 0xbf, 0xb8, 0x2f,
|
||||||
|
0xd6, 0x01, 0xa8, 0xc4, 0x45, 0xbd, 0xb8, 0x2f, 0xd6, 0x01, 0x50, 0x4b, 0x05, 0x06, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let mut archive = zip::ZipArchive::new(zip_file_bytes).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(archive.len(), 1); //Only one file inside archive: `test.txt`
|
||||||
|
|
||||||
|
{
|
||||||
|
// No password
|
||||||
|
let file = archive.by_index(0);
|
||||||
|
match file {
|
||||||
|
Err(zip::result::ZipError::UnsupportedArchive(
|
||||||
|
zip::result::ZipError::PASSWORD_REQUIRED,
|
||||||
|
)) => (),
|
||||||
|
Err(_) => panic!(
|
||||||
|
"Expected PasswordRequired error when opening encrypted file without password"
|
||||||
|
),
|
||||||
|
Ok(_) => panic!("Error: Successfully opened encrypted file without password?!"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Wrong password
|
||||||
|
let file = archive.by_index_decrypt(0, b"wrong password");
|
||||||
|
match file {
|
||||||
|
Ok(Err(zip::result::InvalidPassword)) => (),
|
||||||
|
Err(_) => panic!(
|
||||||
|
"Expected InvalidPassword error when opening encrypted file with wrong password"
|
||||||
|
),
|
||||||
|
Ok(Ok(_)) => panic!("Error: Successfully opened encrypted file with wrong password?!"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Correct password, read contents
|
||||||
|
let mut file = archive
|
||||||
|
.by_index_decrypt(0, "test".as_bytes())
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
let file_name = file.enclosed_name().unwrap();
|
||||||
|
assert_eq!(file_name, std::path::PathBuf::from("test.txt"));
|
||||||
|
|
||||||
|
let mut data = Vec::new();
|
||||||
|
file.read_to_end(&mut data).unwrap();
|
||||||
|
assert_eq!(data, "abcdefghijklmnopqrstuvwxyz123456789".as_bytes());
|
||||||
|
}
|
||||||
|
}
|
Загрузка…
Ссылка в новой задаче