Snap for 10453563 from 3954ea2e3580dc0a244262e6a52d6ce0e1c54c02 to mainline-permission-release

Change-Id: I6657527c84b7532687bcfac4dea0402ef9320550
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index b50dc02..535489c 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
 {
   "git": {
-    "sha1": "4f7609cec700765525a537747c8f340dd1090aa0"
+    "sha1": "f7dcc666b75256e766295589a5ac5dc5a9617c39"
   },
   "path_in_vcs": ""
 }
\ No newline at end of file
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 6f0e4b9..35d4a6e 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -16,7 +16,7 @@
     strategy:
       matrix:
         os: [ubuntu-latest, macOS-latest, windows-latest]
-        rust: [stable, 1.54.0]
+        rust: [stable, 1.59.0]
 
     steps:
     - uses: actions/checkout@master
@@ -74,3 +74,19 @@
 
     - name: Docs
       run: cargo doc
+
+  fuzz:
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions-rs/toolchain@v1
+        with:
+          profile: minimal
+          toolchain: nightly
+          override: true
+
+      - run: cargo install cargo-fuzz
+      - name: compile fuzz
+        run: |
+          cargo fuzz build fuzz_read
diff --git a/Android.bp b/Android.bp
index c0b2349..8ccfd4a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -20,10 +20,11 @@
 
 rust_library {
     name: "libzip",
+    // has rustc warnings
     host_supported: true,
     crate_name: "zip",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.6.2",
+    cargo_pkg_version: "0.6.4",
     srcs: ["src/lib.rs"],
     edition: "2018",
     features: [
@@ -39,6 +40,8 @@
         "//apex_available:platform",
         "com.android.virt",
     ],
+    product_available: true,
+    vendor_available: true,
     arch: {
         arm: {
             rustlibs: ["libcrossbeam_utils"],
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..cd79e39
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,9 @@
+# Changelog
+
+## [0.6.4]
+
+### Changed
+
+ - [#333](https://github.com/zip-rs/zip/pull/333): disabled the default features of the `time` dependency, and also `formatting` and `macros`, as they were enabled by mistake.
+ - Deprecated [`DateTime::from_time`](https://docs.rs/zip/0.6/zip/struct.DateTime.html#method.from_time) in favor of [`DateTime::try_from`](https://docs.rs/zip/0.6/zip/struct.DateTime.html#impl-TryFrom-for-DateTime)
+ 
\ No newline at end of file
diff --git a/Cargo.toml b/Cargo.toml
index fe1a031..7a1b656 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,16 +12,31 @@
 [package]
 edition = "2018"
 name = "zip"
-version = "0.6.2"
-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"
-keywords = ["zip", "archive"]
+version = "0.6.4"
+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.
+"""
+readme = "README.md"
+keywords = [
+    "zip",
+    "archive",
+]
 license = "MIT"
 repository = "https://github.com/zip-rs/zip.git"
 
 [[bench]]
 name = "read_entry"
 harness = false
+
+[[bench]]
+name = "read_metadata"
+harness = false
+
 [dependencies.aes]
 version = "0.7.5"
 optional = true
@@ -41,7 +56,7 @@
 version = "1.3.2"
 
 [dependencies.flate2]
-version = "1.0.22"
+version = "1.0.23"
 optional = true
 default-features = false
 
@@ -51,7 +66,7 @@
 optional = true
 
 [dependencies.pbkdf2]
-version = "0.10.1"
+version = "0.11.0"
 optional = true
 
 [dependencies.sha1]
@@ -60,27 +75,49 @@
 
 [dependencies.time]
 version = "0.3.7"
-features = ["formatting", "macros"]
+features = ["std"]
 optional = true
+default-features = false
 
 [dependencies.zstd]
-version = "0.10.0"
+version = "0.11.2"
 optional = true
+
 [dev-dependencies.bencher]
 version = "0.1.5"
 
 [dev-dependencies.getrandom]
 version = "0.2.5"
 
+[dev-dependencies.time]
+version = "0.3.7"
+features = [
+    "formatting",
+    "macros",
+]
+
 [dev-dependencies.walkdir]
 version = "2.3.2"
 
 [features]
-aes-crypto = ["aes", "constant_time_eq", "hmac", "pbkdf2", "sha1"]
-default = ["aes-crypto", "bzip2", "deflate", "time", "zstd"]
+aes-crypto = [
+    "aes",
+    "constant_time_eq",
+    "hmac",
+    "pbkdf2",
+    "sha1",
+]
+default = [
+    "aes-crypto",
+    "bzip2",
+    "deflate",
+    "time",
+    "zstd",
+]
 deflate = ["flate2/rust_backend"]
 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"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index cc87821..caf6a07 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "zip"
-version = "0.6.2"
+version = "0.6.4"
 authors = ["Mathijs van de Nes <git@mathijs.vd-nes.nl>", "Marli Frost <marli@frost.red>", "Ryan Levick <ryan.levick@gmail.com>"]
 license = "MIT"
 repository = "https://github.com/zip-rs/zip.git"
@@ -16,12 +16,12 @@
 bzip2 = { version = "0.4.3", optional = true }
 constant_time_eq = { version = "0.1.5", optional = true }
 crc32fast = "1.3.2"
-flate2 = { version = "1.0.22", default-features = false, optional = true }
+flate2 = { version = "1.0.23", default-features = false, optional = true }
 hmac = { version = "0.12.1", optional = true, features = ["reset"] }
-pbkdf2 = {version = "0.10.1", optional = true }
+pbkdf2 = {version = "0.11.0", optional = true }
 sha1 = {version = "0.10.1", optional = true }
-time = { version = "0.3.7", features = ["formatting", "macros" ], optional = true }
-zstd = { version = "0.10.0", optional = true }
+time = { version = "0.3.7", optional = true, default-features = false, features = ["std"] }
+zstd = { version = "0.11.2", optional = true }
 
 [target.'cfg(any(all(target_arch = "arm", target_pointer_width = "32"), target_arch = "mips", target_arch = "powerpc"))'.dependencies]
 crossbeam-utils = "0.8.8"
@@ -30,6 +30,7 @@
 bencher = "0.1.5"
 getrandom = "0.2.5"
 walkdir = "2.3.2"
+time = { version = "0.3.7", features = ["formatting", "macros"] }
 
 [features]
 aes-crypto = [ "aes", "constant_time_eq", "hmac", "pbkdf2", "sha1" ]
@@ -42,3 +43,7 @@
 [[bench]]
 name = "read_entry"
 harness = false
+
+[[bench]]
+name = "read_metadata"
+harness = false
diff --git a/METADATA b/METADATA
index c6e83c0..a547795 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,7 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update rust/crates/zip
+# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+
 name: "zip"
 description: "Library to support the reading and writing of zip files."
 third_party {
@@ -7,13 +11,13 @@
   }
   url {
     type: ARCHIVE
-    value: "https://static.crates.io/crates/zip/zip-0.6.2.crate"
+    value: "https://static.crates.io/crates/zip/zip-0.6.4.crate"
   }
-  version: "0.6.2"
+  version: "0.6.4"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2022
-    month: 4
-    day: 13
+    year: 2023
+    month: 2
+    day: 17
   }
 }
diff --git a/README.md b/README.md
index 7db31d4..3754a7c 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
 [![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](https://docs.rs/zip/0.6.2/zip/)
+[Documentation](https://docs.rs/zip/0.6.3/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.
@@ -35,14 +35,14 @@
 
 ```toml
 [dependencies]
-zip = "0.6.2"
+zip = "0.6.4"
 ```
 
 Without the default features:
 
 ```toml
 [dependencies]
-zip = { version = "0.6.2", default-features = false }
+zip = { version = "0.6.4", default-features = false }
 ```
 
 The features available are:
@@ -58,7 +58,7 @@
 MSRV
 ----
 
-Our current Minimum Supported Rust Version is **1.54.0**. When adding features,
+Our current Minimum Supported Rust Version is **1.59.0**. When adding features,
 we will follow these guidelines:
 
 - We will always support the latest four minor Rust versions. This gives you a 6
@@ -75,3 +75,24 @@
    * How to extract a zip file.
    * How to extract a single file from a zip.
    * How to read a zip from the standard input.
+
+Fuzzing
+-------
+
+Fuzzing support is through [cargo fuzz](https://github.com/rust-fuzz/cargo-fuzz). To install cargo fuzz:
+
+```bash
+cargo install cargo-fuzz
+```
+
+To list fuzz targets:
+
+```bash
+cargo +nightly fuzz list
+```
+
+To start fuzzing zip extraction:
+
+```bash
+cargo +nightly fuzz run fuzz_read
+```
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 7325ef4..bbc8a7f 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1,37 +1,29 @@
 // Generated by update_crate_tests.py for tests that depend on this crate.
 {
-  "presubmit": [
+  "imports": [
     {
-      "name": "ZipFuseTest"
+      "path": "packages/modules/Virtualization/apkdmverity"
     },
     {
-      "name": "libapkverify.integration_test"
+      "path": "packages/modules/Virtualization/avmd"
     },
     {
-      "name": "libapkverify.test"
+      "path": "packages/modules/Virtualization/libs/apexutil"
     },
     {
-      "name": "microdroid_manager_test"
+      "path": "packages/modules/Virtualization/libs/apkverify"
     },
     {
-      "name": "virtualizationservice_device_test"
-    }
-  ],
-  "presubmit-rust": [
-    {
-      "name": "ZipFuseTest"
+      "path": "packages/modules/Virtualization/microdroid_manager"
     },
     {
-      "name": "libapkverify.integration_test"
+      "path": "packages/modules/Virtualization/virtualizationmanager"
     },
     {
-      "name": "libapkverify.test"
+      "path": "packages/modules/Virtualization/vm"
     },
     {
-      "name": "microdroid_manager_test"
-    },
-    {
-      "name": "virtualizationservice_device_test"
+      "path": "packages/modules/Virtualization/zipfuse"
     }
   ]
 }
diff --git a/benches/read_metadata.rs b/benches/read_metadata.rs
new file mode 100644
index 0000000..95334b1
--- /dev/null
+++ b/benches/read_metadata.rs
@@ -0,0 +1,38 @@
+use bencher::{benchmark_group, benchmark_main};
+
+use std::io::{Cursor, Write};
+
+use bencher::Bencher;
+use zip::{ZipArchive, ZipWriter};
+
+const FILE_COUNT: usize = 15_000;
+const FILE_SIZE: usize = 1024;
+
+fn generate_random_archive(count_files: usize, file_size: usize) -> Vec<u8> {
+    let data = Vec::new();
+    let mut writer = ZipWriter::new(Cursor::new(data));
+    let options =
+        zip::write::FileOptions::default().compression_method(zip::CompressionMethod::Stored);
+
+    let bytes = vec![0u8; file_size];
+
+    for i in 0..count_files {
+        let name = format!("file_deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef_{i}.dat");
+        writer.start_file(name, options).unwrap();
+        writer.write_all(&bytes).unwrap();
+    }
+
+    writer.finish().unwrap().into_inner()
+}
+
+fn read_metadata(bench: &mut Bencher) {
+    let bytes = generate_random_archive(FILE_COUNT, FILE_SIZE);
+
+    bench.iter(|| {
+        let archive = ZipArchive::new(Cursor::new(bytes.as_slice())).unwrap();
+        archive.len()
+    });
+}
+
+benchmark_group!(benches, read_metadata);
+benchmark_main!(benches);
diff --git a/examples/extract.rs b/examples/extract.rs
index 7b8860c..3080716 100644
--- a/examples/extract.rs
+++ b/examples/extract.rs
@@ -12,7 +12,7 @@
         return 1;
     }
     let fname = std::path::Path::new(&*args[1]);
-    let file = fs::File::open(&fname).unwrap();
+    let file = fs::File::open(fname).unwrap();
 
     let mut archive = zip::ZipArchive::new(file).unwrap();
 
@@ -26,7 +26,7 @@
         {
             let comment = file.comment();
             if !comment.is_empty() {
-                println!("File {} comment: {}", i, comment);
+                println!("File {i} comment: {comment}");
             }
         }
 
@@ -42,7 +42,7 @@
             );
             if let Some(p) = outpath.parent() {
                 if !p.exists() {
-                    fs::create_dir_all(&p).unwrap();
+                    fs::create_dir_all(p).unwrap();
                 }
             }
             let mut outfile = fs::File::create(&outpath).unwrap();
diff --git a/examples/extract_lorem.rs b/examples/extract_lorem.rs
index a34a04f..bc50abe 100644
--- a/examples/extract_lorem.rs
+++ b/examples/extract_lorem.rs
@@ -11,7 +11,7 @@
         return 1;
     }
     let fname = std::path::Path::new(&*args[1]);
-    let zipfile = std::fs::File::open(&fname).unwrap();
+    let zipfile = std::fs::File::open(fname).unwrap();
 
     let mut archive = zip::ZipArchive::new(zipfile).unwrap();
 
@@ -25,7 +25,7 @@
 
     let mut contents = String::new();
     file.read_to_string(&mut contents).unwrap();
-    println!("{}", contents);
+    println!("{contents}");
 
     0
 }
diff --git a/examples/file_info.rs b/examples/file_info.rs
index 64969b6..6a2adc5 100644
--- a/examples/file_info.rs
+++ b/examples/file_info.rs
@@ -12,7 +12,7 @@
         return 1;
     }
     let fname = std::path::Path::new(&*args[1]);
-    let file = fs::File::open(&fname).unwrap();
+    let file = fs::File::open(fname).unwrap();
     let reader = BufReader::new(file);
 
     let mut archive = zip::ZipArchive::new(reader).unwrap();
@@ -30,7 +30,7 @@
         {
             let comment = file.comment();
             if !comment.is_empty() {
-                println!("Entry {} comment: {}", i, comment);
+                println!("Entry {i} comment: {comment}");
             }
         }
 
diff --git a/examples/stdin_info.rs b/examples/stdin_info.rs
index 10d7aa8..a609916 100644
--- a/examples/stdin_info.rs
+++ b/examples/stdin_info.rs
@@ -20,12 +20,12 @@
                 );
                 match file.read(&mut buf) {
                     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,
             Err(e) => {
-                println!("Error encountered while reading zip: {:?}", e);
+                println!("Error encountered while reading zip: {e:?}");
                 return 1;
             }
         }
diff --git a/examples/write_dir.rs b/examples/write_dir.rs
index 8cc561f..3b04352 100644
--- a/examples/write_dir.rs
+++ b/examples/write_dir.rs
@@ -54,8 +54,8 @@
             continue;
         }
         match doit(src_dir, dst_file, method.unwrap()) {
-            Ok(_) => println!("done: {} written to {}", src_dir, dst_file),
-            Err(e) => println!("Error: {:?}", e),
+            Ok(_) => println!("done: {src_dir} written to {dst_file}"),
+            Err(e) => println!("Error: {e:?}"),
         }
     }
 
@@ -84,18 +84,18 @@
         // Write file or directory explicitly
         // Some unzip tools unzip files with directory paths correctly, some do not!
         if path.is_file() {
-            println!("adding file {:?} as {:?} ...", path, name);
+            println!("adding file {path:?} as {name:?} ...");
             #[allow(deprecated)]
             zip.start_file_from_path(name, options)?;
             let mut f = File::open(path)?;
 
             f.read_to_end(&mut buffer)?;
-            zip.write_all(&*buffer)?;
+            zip.write_all(&buffer)?;
             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);
+            println!("adding dir {path:?} as {name:?} ...");
             #[allow(deprecated)]
             zip.add_directory_from_path(name, options)?;
         }
@@ -114,7 +114,7 @@
     }
 
     let path = Path::new(dst_file);
-    let file = File::create(&path).unwrap();
+    let file = File::create(path).unwrap();
 
     let walkdir = WalkDir::new(src_dir);
     let it = walkdir.into_iter();
diff --git a/examples/write_sample.rs b/examples/write_sample.rs
index b574950..2e45cb1 100644
--- a/examples/write_sample.rs
+++ b/examples/write_sample.rs
@@ -14,8 +14,8 @@
 
     let filename = &*args[1];
     match doit(filename) {
-        Ok(_) => println!("File written to {}", filename),
-        Err(e) => println!("Error: {:?}", e),
+        Ok(_) => println!("File written to {filename}"),
+        Err(e) => println!("Error: {e:?}"),
     }
 
     0
@@ -23,7 +23,7 @@
 
 fn doit(filename: &str) -> zip::result::ZipResult<()> {
     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);
 
diff --git a/patches/Android.bp.diff b/patches/Android.bp.diff
index c0037cc..dfeb034 100644
--- a/patches/Android.bp.diff
+++ b/patches/Android.bp.diff
@@ -2,10 +2,12 @@
 index 2373253..c0b2349 100644
 --- a/Android.bp
 +++ b/Android.bp
-@@ -39,4 +39,9 @@ rust_library {
+@@ -39,6 +39,11 @@ rust_library {
          "//apex_available:platform",
          "com.android.virt",
      ],
+     product_available: true,
+     vendor_available: true,
 +    arch: {
 +        arm: {
 +            rustlibs: ["libcrossbeam_utils"],
diff --git a/src/compression.rs b/src/compression.rs
index abd8b53..baec939 100644
--- a/src/compression.rs
+++ b/src/compression.rs
@@ -141,7 +141,7 @@
 impl fmt::Display for CompressionMethod {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         // Just duplicate what the Debug format looks like, i.e, the enum key:
-        write!(f, "{:?}", self)
+        write!(f, "{self:?}")
     }
 }
 
@@ -195,8 +195,8 @@
     #[test]
     fn to_display_fmt() {
         fn check_match(method: CompressionMethod) {
-            let debug_str = format!("{:?}", method);
-            let display_str = format!("{}", method);
+            let debug_str = format!("{method:?}");
+            let display_str = format!("{method}");
             assert_eq!(debug_str, display_str);
         }
 
diff --git a/src/read.rs b/src/read.rs
index c619f24..dad20c2 100644
--- a/src/read.rs
+++ b/src/read.rs
@@ -348,7 +348,9 @@
             Some(locator64) => {
                 // If we got here, this is indeed a ZIP64 file.
 
-                if footer.disk_number as u32 != locator64.disk_with_central_directory {
+                if !footer.record_too_small()
+                    && footer.disk_number as u32 != locator64.disk_with_central_directory
+                {
                     return unsupported_zip_error(
                         "Support for multi-disk files is not implemented",
                     );
@@ -401,15 +403,23 @@
     pub fn new(mut reader: R) -> ZipResult<ZipArchive<R>> {
         let (footer, cde_start_pos) = spec::CentralDirectoryEnd::find_and_parse(&mut reader)?;
 
-        if footer.disk_number != footer.disk_with_central_directory {
+        if !footer.record_too_small() && footer.disk_number != footer.disk_with_central_directory {
             return unsupported_zip_error("Support for multi-disk files is not implemented");
         }
 
         let (archive_offset, directory_start, number_of_files) =
             Self::get_directory_counts(&mut reader, &footer, cde_start_pos)?;
 
-        let mut files = Vec::new();
-        let mut names_map = HashMap::new();
+        // If the parsed number of files is greater than the offset then
+        // something fishy is going on and we shouldn't trust number_of_files.
+        let file_capacity = if number_of_files > cde_start_pos as usize {
+            0
+        } else {
+            number_of_files
+        };
+
+        let mut files = Vec::with_capacity(file_capacity);
+        let mut names_map = HashMap::with_capacity(file_capacity);
 
         if reader.seek(io::SeekFrom::Start(directory_start)).is_err() {
             return Err(ZipError::InvalidArchive(
@@ -453,7 +463,7 @@
             } else {
                 if let Some(p) = outpath.parent() {
                     if !p.exists() {
-                        fs::create_dir_all(&p)?;
+                        fs::create_dir_all(p)?;
                     }
                 }
                 let mut outfile = fs::File::create(&outpath)?;
@@ -639,7 +649,7 @@
     reader: &mut R,
     archive_offset: u64,
 ) -> ZipResult<ZipFileData> {
-    let central_header_start = reader.seek(io::SeekFrom::Current(0))?;
+    let central_header_start = reader.stream_position()?;
     // Parse central header
     let signature = reader.read_u32::<LittleEndian>()?;
     if signature != spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE {
@@ -673,11 +683,11 @@
     reader.read_exact(&mut file_comment_raw)?;
 
     let file_name = match is_utf8 {
-        true => String::from_utf8_lossy(&*file_name_raw).into_owned(),
+        true => String::from_utf8_lossy(&file_name_raw).into_owned(),
         false => file_name_raw.clone().from_cp437(),
     };
     let file_comment = match is_utf8 {
-        true => String::from_utf8_lossy(&*file_comment_raw).into_owned(),
+        true => String::from_utf8_lossy(&file_comment_raw).into_owned(),
         false => file_comment_raw.from_cp437(),
     };
 
@@ -912,12 +922,12 @@
         self.data.compression_method
     }
 
-    /// Get the size of the file in the archive
+    /// Get the size of the file, in bytes, in the archive
     pub fn compressed_size(&self) -> u64 {
         self.data.compressed_size
     }
 
-    /// Get the size of the file when uncompressed
+    /// Get the size of the file, in bytes, when uncompressed
     pub fn size(&self) -> u64 {
         self.data.uncompressed_size
     }
@@ -949,7 +959,7 @@
         match self.data.system {
             System::Unix => Some(self.data.external_attributes >> 16),
             System::Dos => {
-                // Interpret MSDOS directory bit
+                // Interpret MS-DOS directory bit
                 let mut mode = if 0x10 == (self.data.external_attributes & 0x10) {
                     ffi::S_IFDIR | 0o0775
                 } else {
@@ -1077,7 +1087,7 @@
     reader.read_exact(&mut extra_field)?;
 
     let file_name = match is_utf8 {
-        true => String::from_utf8_lossy(&*file_name_raw).into_owned(),
+        true => String::from_utf8_lossy(&file_name_raw).into_owned(),
         false => file_name_raw.clone().from_cp437(),
     };
 
@@ -1121,7 +1131,7 @@
         return unsupported_zip_error("The file length is not available in the local header");
     }
 
-    let limit_reader = (reader as &'a mut dyn io::Read).take(result.compressed_size as u64);
+    let limit_reader = (reader as &'a mut dyn io::Read).take(result.compressed_size);
 
     let result_crc32 = result.crc32;
     let result_compression_method = result.compression_method;
@@ -1267,4 +1277,36 @@
             );
         }
     }
+
+    /// test case to ensure we don't preemptively over allocate based on the
+    /// declared number of files in the CDE of an invalid zip when the number of
+    /// files declared is more than the alleged offset in the CDE
+    #[test]
+    fn invalid_cde_number_of_files_allocation_smaller_offset() {
+        use super::ZipArchive;
+        use std::io;
+
+        let mut v = Vec::new();
+        v.extend_from_slice(include_bytes!(
+            "../tests/data/invalid_cde_number_of_files_allocation_smaller_offset.zip"
+        ));
+        let reader = ZipArchive::new(io::Cursor::new(v));
+        assert!(reader.is_err());
+    }
+
+    /// test case to ensure we don't preemptively over allocate based on the
+    /// declared number of files in the CDE of an invalid zip when the number of
+    /// files declared is less than the alleged offset in the CDE
+    #[test]
+    fn invalid_cde_number_of_files_allocation_greater_offset() {
+        use super::ZipArchive;
+        use std::io;
+
+        let mut v = Vec::new();
+        v.extend_from_slice(include_bytes!(
+            "../tests/data/invalid_cde_number_of_files_allocation_greater_offset.zip"
+        ));
+        let reader = ZipArchive::new(io::Cursor::new(v));
+        assert!(reader.is_err());
+    }
 }
diff --git a/src/result.rs b/src/result.rs
index 72a30e4..00d558c 100644
--- a/src/result.rs
+++ b/src/result.rs
@@ -44,9 +44,9 @@
 impl fmt::Display for ZipError {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
         match self {
-            ZipError::Io(err) => write!(fmt, "{}", err),
-            ZipError::InvalidArchive(err) => write!(fmt, "invalid Zip archive: {}", err),
-            ZipError::UnsupportedArchive(err) => write!(fmt, "unsupported Zip archive: {}", err),
+            ZipError::Io(err) => write!(fmt, "{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"),
         }
     }
@@ -81,3 +81,18 @@
         io::Error::new(io::ErrorKind::Other, err)
     }
 }
+
+/// Error type for time parsing
+#[derive(Debug)]
+pub struct DateTimeRangeError;
+
+impl fmt::Display for DateTimeRangeError {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        write!(
+            fmt,
+            "a date could not be represented within the bounds the MS-DOS date range (1980-2107)"
+        )
+    }
+}
+
+impl Error for DateTimeRangeError {}
diff --git a/src/spec.rs b/src/spec.rs
index 3ffcf73..1d8cb0a 100644
--- a/src/spec.rs
+++ b/src/spec.rs
@@ -23,6 +23,18 @@
 }
 
 impl CentralDirectoryEnd {
+    // Per spec 4.4.1.4 - a CentralDirectoryEnd field might be insufficient to hold the
+    // required data. In this case the file SHOULD contain a ZIP64 format record
+    // and the field of this record will be set to -1
+    pub(crate) fn record_too_small(&self) -> bool {
+        self.disk_number == 0xFFFF
+            || self.disk_with_central_directory == 0xFFFF
+            || self.number_of_files_on_this_disk == 0xFFFF
+            || self.number_of_files == 0xFFFF
+            || self.central_directory_size == 0xFFFFFFFF
+            || self.central_directory_offset == 0xFFFFFFFF
+    }
+
     pub fn parse<T: Read>(reader: &mut T) -> ZipResult<CentralDirectoryEnd> {
         let magic = reader.read_u32::<LittleEndian>()?;
         if magic != CENTRAL_DIRECTORY_END_SIGNATURE {
@@ -64,12 +76,12 @@
 
         let mut pos = file_length - HEADER_SIZE;
         while pos >= search_upper_bound {
-            reader.seek(io::SeekFrom::Start(pos as u64))?;
+            reader.seek(io::SeekFrom::Start(pos))?;
             if reader.read_u32::<LittleEndian>()? == CENTRAL_DIRECTORY_END_SIGNATURE {
                 reader.seek(io::SeekFrom::Current(
                     BYTES_BETWEEN_MAGIC_AND_COMMENT_SIZE as i64,
                 ))?;
-                let cde_start_pos = reader.seek(io::SeekFrom::Start(pos as u64))?;
+                let cde_start_pos = reader.seek(io::SeekFrom::Start(pos))?;
                 return CentralDirectoryEnd::parse(reader).map(|cde| (cde, cde_start_pos));
             }
             pos = match pos.checked_sub(1) {
diff --git a/src/types.rs b/src/types.rs
index b65fad4..ad3a570 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -1,13 +1,16 @@
 //! Types that specify what is contained in a ZIP.
-#[cfg(doc)]
-use {crate::read::ZipFile, crate::write::FileOptions};
-
+#[cfg(feature = "time")]
+use std::convert::{TryFrom, TryInto};
 #[cfg(not(any(
     all(target_arch = "arm", target_pointer_width = "32"),
     target_arch = "mips",
     target_arch = "powerpc"
 )))]
 use std::sync::atomic;
+#[cfg(not(feature = "time"))]
+use std::time::SystemTime;
+#[cfg(doc)]
+use {crate::read::ZipFile, crate::write::FileOptions};
 
 #[cfg(any(
     all(target_arch = "arm", target_pointer_width = "32"),
@@ -42,9 +45,11 @@
 }
 
 #[cfg(feature = "time")]
+use crate::result::DateTimeRangeError;
+#[cfg(feature = "time")]
 use time::{error::ComponentRange, Date, Month, OffsetDateTime, PrimitiveDateTime, Time};
 
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub enum System {
     Dos = 0,
     Unix = 3,
@@ -115,7 +120,7 @@
         let years = (datepart & 0b1111111000000000) >> 9;
 
         DateTime {
-            year: (years + 1980) as u16,
+            year: years + 1980,
             month: months as u8,
             day: days as u8,
             hour: hours as u8,
@@ -143,10 +148,8 @@
         second: u8,
     ) -> Result<DateTime, ()> {
         if (1980..=2107).contains(&year)
-            && month >= 1
-            && month <= 12
-            && day >= 1
-            && day <= 31
+            && (1..=12).contains(&month)
+            && (1..=31).contains(&day)
             && hour <= 23
             && minute <= 59
             && second <= 60
@@ -169,19 +172,9 @@
     ///
     /// Returns `Err` when this object is out of bounds
     #[allow(clippy::result_unit_err)]
+    #[deprecated(note = "use `DateTime::try_from()`")]
     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(())
-        }
+        dt.try_into().map_err(|_err| ())
     }
 
     /// Gets the time portion of this datetime in the msdos representation
@@ -197,8 +190,6 @@
     #[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)?;
@@ -256,6 +247,26 @@
     }
 }
 
+#[cfg(feature = "time")]
+impl TryFrom<OffsetDateTime> for DateTime {
+    type Error = DateTimeRangeError;
+
+    fn try_from(dt: OffsetDateTime) -> Result<Self, Self::Error> {
+        if dt.year() >= 1980 && dt.year() <= 2107 {
+            Ok(DateTime {
+                year: (dt.year()) as u16,
+                month: (dt.month()) as u8,
+                day: dt.day(),
+                hour: dt.hour(),
+                minute: dt.minute(),
+                second: dt.second(),
+            })
+        } else {
+            Err(DateTimeRangeError)
+        }
+    }
+}
+
 pub const DEFAULT_VERSION: u8 = 46;
 
 /// A type like `AtomicU64` except it implements `Clone` and has predefined
@@ -500,20 +511,43 @@
     #[cfg(feature = "time")]
     #[test]
     fn datetime_from_time_bounds() {
+        use std::convert::TryFrom;
+
         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());
+        assert!(DateTime::try_from(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());
+        assert!(DateTime::try_from(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());
+        assert!(DateTime::try_from(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());
+        assert!(DateTime::try_from(datetime!(2108-01-01 00:00:00 UTC)).is_err());
+    }
+
+    #[cfg(feature = "time")]
+    #[test]
+    fn datetime_try_from_bounds() {
+        use std::convert::TryFrom;
+
+        use super::DateTime;
+        use time::macros::datetime;
+
+        // 1979-12-31 23:59:59
+        assert!(DateTime::try_from(datetime!(1979-12-31 23:59:59 UTC)).is_err());
+
+        // 1980-01-01 00:00:00
+        assert!(DateTime::try_from(datetime!(1980-01-01 00:00:00 UTC)).is_ok());
+
+        // 2107-12-31 23:59:59
+        assert!(DateTime::try_from(datetime!(2107-12-31 23:59:59 UTC)).is_ok());
+
+        // 2108-01-01 00:00:00
+        assert!(DateTime::try_from(datetime!(2108-01-01 00:00:00 UTC)).is_err());
     }
 
     #[test]
@@ -564,10 +598,11 @@
     #[test]
     fn time_at_january() {
         use super::DateTime;
+        use std::convert::TryFrom;
 
         // 2020-01-01 00:00:00
         let clock = OffsetDateTime::from_unix_timestamp(1_577_836_800).unwrap();
 
-        assert!(DateTime::from_time(clock).is_ok());
+        assert!(DateTime::try_from(clock).is_ok());
     }
 }
diff --git a/src/write.rs b/src/write.rs
index 551b4e3..14252b4 100644
--- a/src/write.rs
+++ b/src/write.rs
@@ -7,6 +7,7 @@
 use crate::types::{AtomicU64, DateTime, System, ZipFileData, DEFAULT_VERSION};
 use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
 use crc32fast::Hasher;
+use std::convert::TryInto;
 use std::default::Default;
 use std::io;
 use std::io::prelude::*;
@@ -110,31 +111,6 @@
 }
 
 impl FileOptions {
-    /// Construct a new FileOptions object
-    pub fn default() -> FileOptions {
-        FileOptions {
-            #[cfg(any(
-                feature = "deflate",
-                feature = "deflate-miniz",
-                feature = "deflate-zlib"
-            ))]
-            compression_method: CompressionMethod::Deflated,
-            #[cfg(not(any(
-                feature = "deflate",
-                feature = "deflate-miniz",
-                feature = "deflate-zlib"
-            )))]
-            compression_method: CompressionMethod::Stored,
-            compression_level: None,
-            #[cfg(feature = "time")]
-            last_modified_time: DateTime::from_time(OffsetDateTime::now_utc()).unwrap_or_default(),
-            #[cfg(not(feature = "time"))]
-            last_modified_time: DateTime::default(),
-            permissions: None,
-            large_file: false,
-        }
-    }
-
     /// Set the compression method for the new file
     ///
     /// The default is `CompressionMethod::Deflated`. If the deflate compression feature is
@@ -174,7 +150,11 @@
     ///
     /// The format is represented with unix-style permissions.
     /// The default is `0o644`, which represents `rw-r--r--` for files,
-    /// and `0o755`, which represents `rwxr-xr-x` for directories
+    /// and `0o755`, which represents `rwxr-xr-x` for directories.
+    ///
+    /// This method only preserves the file permissions bits (via a `& 0o777`) and discards
+    /// higher file mode bits. So it cannot be used to denote an entry as a directory,
+    /// symlink, or other special file type.
     #[must_use]
     pub fn unix_permissions(mut self, mode: u32) -> FileOptions {
         self.permissions = Some(mode & 0o777);
@@ -194,8 +174,29 @@
 }
 
 impl Default for FileOptions {
+    /// Construct a new FileOptions object
     fn default() -> Self {
-        Self::default()
+        Self {
+            #[cfg(any(
+                feature = "deflate",
+                feature = "deflate-miniz",
+                feature = "deflate-zlib"
+            ))]
+            compression_method: CompressionMethod::Deflated,
+            #[cfg(not(any(
+                feature = "deflate",
+                feature = "deflate-miniz",
+                feature = "deflate-zlib"
+            )))]
+            compression_method: CompressionMethod::Stored,
+            compression_level: None,
+            #[cfg(feature = "time")]
+            last_modified_time: OffsetDateTime::now_utc().try_into().unwrap_or_default(),
+            #[cfg(not(feature = "time"))]
+            last_modified_time: DateTime::default(),
+            permissions: None,
+            large_file: false,
+        }
     }
 }
 
@@ -348,7 +349,7 @@
 
         {
             let writer = self.inner.get_plain();
-            let header_start = writer.seek(io::SeekFrom::Current(0))?;
+            let header_start = writer.stream_position()?;
 
             let permissions = options.permissions.unwrap_or(0o100644);
             let mut file = ZipFileData {
@@ -375,7 +376,7 @@
             };
             write_local_file_header(writer, &file)?;
 
-            let header_end = writer.seek(io::SeekFrom::Current(0))?;
+            let header_end = writer.stream_position()?;
             self.stats.start = header_end;
             *file.data_start.get_mut() = header_end;
 
@@ -404,7 +405,7 @@
             file.crc32 = self.stats.hasher.clone().finalize();
             file.uncompressed_size = self.stats.bytes_written;
 
-            let file_end = writer.seek(io::SeekFrom::Current(0))?;
+            let file_end = writer.stream_position()?;
             file.compressed_size = file_end - self.stats.start;
 
             update_local_file_header(writer, file)?;
@@ -723,7 +724,7 @@
 
     /// Add a directory entry, taking a Path as argument.
     ///
-    /// This function ensures that the '/' path seperator is used. It also ignores all non 'Normal'
+    /// This function ensures that the '/' path separator is used. It also ignores all non 'Normal'
     /// Components, such as a starting '/' or '..' and '.'.
     #[deprecated(
         since = "0.5.7",
@@ -747,17 +748,55 @@
         Ok(inner.unwrap())
     }
 
+    /// Add a symlink entry.
+    ///
+    /// The zip archive will contain an entry for path `name` which is a symlink to `target`.
+    ///
+    /// No validation or normalization of the paths is performed. For best results,
+    /// callers should normalize `\` to `/` and ensure symlinks are relative to other
+    /// paths within the zip archive.
+    ///
+    /// WARNING: not all zip implementations preserve symlinks on extract. Some zip
+    /// implementations may materialize a symlink as a regular file, possibly with the
+    /// content incorrectly set to the symlink target. For maximum portability, consider
+    /// storing a regular file instead.
+    pub fn add_symlink<N, T>(
+        &mut self,
+        name: N,
+        target: T,
+        mut options: FileOptions,
+    ) -> ZipResult<()>
+    where
+        N: Into<String>,
+        T: Into<String>,
+    {
+        if options.permissions.is_none() {
+            options.permissions = Some(0o777);
+        }
+        *options.permissions.as_mut().unwrap() |= 0o120000;
+        // The symlink target is stored as file content. And compressing the target path
+        // likely wastes space. So always store.
+        options.compression_method = CompressionMethod::Stored;
+
+        self.start_entry(name, options, None)?;
+        self.writing_to_file = true;
+        self.write_all(target.into().as_bytes())?;
+        self.writing_to_file = false;
+
+        Ok(())
+    }
+
     fn finalize(&mut self) -> ZipResult<()> {
         self.finish_file()?;
 
         {
             let writer = self.inner.get_plain();
 
-            let central_start = writer.seek(io::SeekFrom::Current(0))?;
+            let central_start = writer.stream_position()?;
             for file in self.files.iter() {
                 write_central_directory_header(writer, file)?;
             }
-            let central_size = writer.seek(io::SeekFrom::Current(0))? - central_start;
+            let central_size = writer.stream_position()? - central_start;
 
             if self.files.len() > spec::ZIP64_ENTRY_THR
                 || central_size.max(central_start) > spec::ZIP64_BYTES_THR
@@ -806,7 +845,7 @@
     fn drop(&mut self) {
         if !self.inner.is_closed() {
             if let Err(e) = self.finalize() {
-                let _ = write!(io::stderr(), "ZipWriter drop failed: {:?}", e);
+                let _ = write!(io::stderr(), "ZipWriter drop failed: {e:?}");
             }
         }
     }
@@ -1169,8 +1208,7 @@
                 return Err(ZipError::Io(io::Error::new(
                     io::ErrorKind::Other,
                     format!(
-                        "Extra data header ID {:#06} requires crate feature \"unreserved\"",
-                        kind,
+                        "Extra data header ID {kind:#06} requires crate feature \"unreserved\"",
                     ),
                 )));
             }
@@ -1259,7 +1297,7 @@
             if !path_str.is_empty() {
                 path_str.push('/');
             }
-            path_str.push_str(&*os_str.to_string_lossy());
+            path_str.push_str(&os_str.to_string_lossy());
         }
     }
     path_str
@@ -1286,6 +1324,13 @@
     }
 
     #[test]
+    fn unix_permissions_bitmask() {
+        // unix_permissions() throws away upper bits.
+        let options = FileOptions::default().unix_permissions(0o120777);
+        assert_eq!(options.permissions, Some(0o777));
+    }
+
+    #[test]
     fn write_zip_dir() {
         let mut writer = ZipWriter::new(io::Cursor::new(Vec::new()));
         writer
@@ -1314,6 +1359,67 @@
     }
 
     #[test]
+    fn write_symlink_simple() {
+        let mut writer = ZipWriter::new(io::Cursor::new(Vec::new()));
+        writer
+            .add_symlink(
+                "name",
+                "target",
+                FileOptions::default().last_modified_time(
+                    DateTime::from_date_and_time(2018, 8, 15, 20, 45, 6).unwrap(),
+                ),
+            )
+            .unwrap();
+        assert!(writer
+            .write(b"writing to a symlink is not allowed and will not write any data")
+            .is_err());
+        let result = writer.finish().unwrap();
+        assert_eq!(result.get_ref().len(), 112);
+        assert_eq!(
+            *result.get_ref(),
+            &[
+                80u8, 75, 3, 4, 20, 0, 0, 0, 0, 0, 163, 165, 15, 77, 252, 47, 111, 70, 6, 0, 0, 0,
+                6, 0, 0, 0, 4, 0, 0, 0, 110, 97, 109, 101, 116, 97, 114, 103, 101, 116, 80, 75, 1,
+                2, 46, 3, 20, 0, 0, 0, 0, 0, 163, 165, 15, 77, 252, 47, 111, 70, 6, 0, 0, 0, 6, 0,
+                0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 161, 0, 0, 0, 0, 110, 97, 109, 101,
+                80, 75, 5, 6, 0, 0, 0, 0, 1, 0, 1, 0, 50, 0, 0, 0, 40, 0, 0, 0, 0, 0
+            ] as &[u8],
+        );
+    }
+
+    #[test]
+    fn write_symlink_wonky_paths() {
+        let mut writer = ZipWriter::new(io::Cursor::new(Vec::new()));
+        writer
+            .add_symlink(
+                "directory\\link",
+                "/absolute/symlink\\with\\mixed/slashes",
+                FileOptions::default().last_modified_time(
+                    DateTime::from_date_and_time(2018, 8, 15, 20, 45, 6).unwrap(),
+                ),
+            )
+            .unwrap();
+        assert!(writer
+            .write(b"writing to a symlink is not allowed and will not write any data")
+            .is_err());
+        let result = writer.finish().unwrap();
+        assert_eq!(result.get_ref().len(), 162);
+        assert_eq!(
+            *result.get_ref(),
+            &[
+                80u8, 75, 3, 4, 20, 0, 0, 0, 0, 0, 163, 165, 15, 77, 95, 41, 81, 245, 36, 0, 0, 0,
+                36, 0, 0, 0, 14, 0, 0, 0, 100, 105, 114, 101, 99, 116, 111, 114, 121, 92, 108, 105,
+                110, 107, 47, 97, 98, 115, 111, 108, 117, 116, 101, 47, 115, 121, 109, 108, 105,
+                110, 107, 92, 119, 105, 116, 104, 92, 109, 105, 120, 101, 100, 47, 115, 108, 97,
+                115, 104, 101, 115, 80, 75, 1, 2, 46, 3, 20, 0, 0, 0, 0, 0, 163, 165, 15, 77, 95,
+                41, 81, 245, 36, 0, 0, 0, 36, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
+                161, 0, 0, 0, 0, 100, 105, 114, 101, 99, 116, 111, 114, 121, 92, 108, 105, 110,
+                107, 80, 75, 5, 6, 0, 0, 0, 0, 1, 0, 1, 0, 60, 0, 0, 0, 80, 0, 0, 0, 0, 0
+            ] as &[u8],
+        );
+    }
+
+    #[test]
     fn write_mimetype_zip() {
         let mut writer = ZipWriter::new(io::Cursor::new(Vec::new()));
         let options = FileOptions {
diff --git a/tests/data/invalid_cde_number_of_files_allocation_greater_offset.zip b/tests/data/invalid_cde_number_of_files_allocation_greater_offset.zip
new file mode 100644
index 0000000..a428ca7
--- /dev/null
+++ b/tests/data/invalid_cde_number_of_files_allocation_greater_offset.zip
Binary files differ
diff --git a/tests/data/invalid_cde_number_of_files_allocation_smaller_offset.zip b/tests/data/invalid_cde_number_of_files_allocation_smaller_offset.zip
new file mode 100644
index 0000000..2cc9007
--- /dev/null
+++ b/tests/data/invalid_cde_number_of_files_allocation_smaller_offset.zip
Binary files differ
diff --git a/tests/end_to_end.rs b/tests/end_to_end.rs
index 25d0c54..09e7ce4 100644
--- a/tests/end_to_end.rs
+++ b/tests/end_to_end.rs
@@ -13,7 +13,7 @@
     for &method in SUPPORTED_COMPRESSION_METHODS {
         let file = &mut Cursor::new(Vec::new());
 
-        println!("Writing file with {} compression", method);
+        println!("Writing file with {method} compression");
         write_test_archive(file, method).expect("Couldn't write test zip archive");
 
         println!("Checking file contents");
diff --git a/tests/zip64_large.rs b/tests/zip64_large.rs
index 3d10a31..468ef19 100644
--- a/tests/zip64_large.rs
+++ b/tests/zip64_large.rs
@@ -205,7 +205,7 @@
 
         match file.read_exact(&mut buf) {
             Ok(()) => println!("The first {} bytes are: {:?}", buf.len(), buf),
-            Err(e) => println!("Could not read the file: {:?}", e),
+            Err(e) => println!("Could not read the file: {e:?}"),
         };
     }
 }