From 15072820c0f90128100d5b1ad343f47081b61f73 Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Sat, 27 Jan 2024 18:37:27 +0100 Subject: [PATCH] feat: Add deflate64 method (#43) --- .vscode/settings.json | 2 +- Cargo.lock | 8 +++ crates/jean/Cargo.toml | 2 + crates/jean/src/main.rs | 60 ++++++++++--------- crates/rc-zip/Cargo.toml | 6 +- crates/rc-zip/src/reader/sync/decoder.rs | 7 +-- .../reader/sync/entry_reader/deflate64_dec.rs | 22 +++++++ .../src/reader/sync/entry_reader/mod.rs | 18 +++++- crates/rc-zip/src/tests.rs | 12 ++++ 9 files changed, 101 insertions(+), 36 deletions(-) create mode 100644 crates/rc-zip/src/reader/sync/entry_reader/deflate64_dec.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 4ac34f4..9a92fea 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "rust-analyzer.cargo.features": ["default", "lzma"] + "rust-analyzer.cargo.features": ["default", "lzma", "deflate64"] } \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 6ccc0a4..ecd8a95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -233,6 +233,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "deflate64" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9576c1de19747eb6f5efb6a806c3e836512bbdb17bfedc984ccb0bcc953c8390" + [[package]] name = "encode_unicode" version = "0.3.6" @@ -350,6 +356,7 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" name = "jean" version = "2.0.1" dependencies = [ + "cfg-if", "clap", "humansize", "indicatif", @@ -630,6 +637,7 @@ dependencies = [ "chrono", "circular", "crc32fast", + "deflate64", "encoding_rs", "flate2", "lzma-rs", diff --git a/crates/jean/Cargo.toml b/crates/jean/Cargo.toml index 77f8643..98dcc24 100644 --- a/crates/jean/Cargo.toml +++ b/crates/jean/Cargo.toml @@ -14,8 +14,10 @@ humansize = "2.1.3" positioned-io.workspace = true indicatif = "0.17.7" tracing-subscriber = "0.3.18" +cfg-if = "1.0.0" [features] default = ["lzma"] deflate = ["rc-zip/deflate"] +deflate64 = ["rc-zip/deflate64"] lzma = ["rc-zip/lzma"] diff --git a/crates/jean/src/main.rs b/crates/jean/src/main.rs index 66ecef2..841f179 100644 --- a/crates/jean/src/main.rs +++ b/crates/jean/src/main.rs @@ -1,3 +1,4 @@ +use cfg_if::cfg_if; use clap::{Parser, Subcommand}; use humansize::{format_size, BINARY}; use rc_zip::{prelude::*, EntryContents}; @@ -147,6 +148,10 @@ fn do_main(cli: Cli) -> Result<(), Box> { size = format_size(entry.inner.uncompressed_size, BINARY), ); if verbose { + print!( + " ({} compressed)", + format_size(entry.inner.compressed_size, BINARY) + ); print!( " {modified} {uid} {gid}", modified = entry.modified(), @@ -210,39 +215,38 @@ fn do_main(cli: Cli) -> Result<(), Box> { match entry.contents() { EntryContents::Symlink => { num_symlinks += 1; - #[cfg(windows)] - { - let path = dir.join(entry_name); - std::fs::create_dir_all( - path.parent() - .expect("all full entry paths should have parent paths"), - )?; - let mut entry_writer = File::create(path)?; - let mut entry_reader = entry.reader(); - std::io::copy(&mut entry_reader, &mut entry_writer)?; - } - #[cfg(not(windows))] - { - let path = dir.join(entry_name); - std::fs::create_dir_all( - path.parent() - .expect("all full entry paths should have parent paths"), - )?; - if let Ok(metadata) = std::fs::symlink_metadata(&path) { - if metadata.is_file() { - std::fs::remove_file(&path)?; + cfg_if! { + if #[cfg(windows)] { + let path = dir.join(entry_name); + std::fs::create_dir_all( + path.parent() + .expect("all full entry paths should have parent paths"), + )?; + let mut entry_writer = File::create(path)?; + let mut entry_reader = entry.reader(); + std::io::copy(&mut entry_reader, &mut entry_writer)?; + } else { + let path = dir.join(entry_name); + std::fs::create_dir_all( + path.parent() + .expect("all full entry paths should have parent paths"), + )?; + if let Ok(metadata) = std::fs::symlink_metadata(&path) { + if metadata.is_file() { + std::fs::remove_file(&path)?; + } } - } - let mut src = String::new(); - entry.reader().read_to_string(&mut src)?; + let mut src = String::new(); + entry.reader().read_to_string(&mut src)?; - // validate pointing path before creating a symbolic link - if src.contains("..") { - continue; + // validate pointing path before creating a symbolic link + if src.contains("..") { + continue; + } + std::os::unix::fs::symlink(src, &path)?; } - std::os::unix::fs::symlink(src, &path)?; } } EntryContents::Directory => { diff --git a/crates/rc-zip/Cargo.toml b/crates/rc-zip/Cargo.toml index b1343e1..6b7c35c 100644 --- a/crates/rc-zip/Cargo.toml +++ b/crates/rc-zip/Cargo.toml @@ -28,13 +28,15 @@ num_enum = "0.7.2" byteorder = "1.5.0" cfg-if = "1.0.0" lzma-rs = { version = "0.3.0", features = ["stream"], optional = true } +deflate64 = { version = "0.1.7", optional = true } [features] default = ["sync", "file", "deflate"] sync = [] file = ["positioned-io"] -deflate = ["flate2"] -lzma = ["lzma-rs"] +deflate = ["dep:flate2"] +lzma = ["dep:lzma-rs"] +deflate64 = ["dep:deflate64"] [dev-dependencies] tracing-test = "0.2.4" diff --git a/crates/rc-zip/src/reader/sync/decoder.rs b/crates/rc-zip/src/reader/sync/decoder.rs index 975a46b..2f51465 100644 --- a/crates/rc-zip/src/reader/sync/decoder.rs +++ b/crates/rc-zip/src/reader/sync/decoder.rs @@ -73,13 +73,12 @@ impl LimitedReader { impl io::Read for LimitedReader { fn read(&mut self, buf: &mut [u8]) -> io::Result { - if self.inner.available_space() == 0 { - self.inner.shift(); - } - let len = cmp::min(buf.len() as u64, self.remaining) as usize; + tracing::trace!(%len, buf_len = buf.len(), remaining = self.remaining, available_data = self.inner.available_data(), available_space = self.inner.available_space(), "computing len"); + let res = self.inner.read(&mut buf[..len]); if let Ok(n) = res { + tracing::trace!(%n, "read ok"); self.remaining -= n as u64; } res diff --git a/crates/rc-zip/src/reader/sync/entry_reader/deflate64_dec.rs b/crates/rc-zip/src/reader/sync/entry_reader/deflate64_dec.rs new file mode 100644 index 0000000..43ac3cd --- /dev/null +++ b/crates/rc-zip/src/reader/sync/entry_reader/deflate64_dec.rs @@ -0,0 +1,22 @@ +use std::io::{BufReader, Read}; + +use deflate64::Deflate64Decoder; + +use crate::reader::sync::{Decoder, LimitedReader}; + +impl Decoder for Deflate64Decoder> +where + R: Read, +{ + fn into_inner(self: Box) -> R { + Self::into_inner(*self).into_inner() + } + + fn get_mut(&mut self) -> &mut R { + Self::get_mut(self).get_mut() + } +} + +pub(crate) fn mk_decoder(r: LimitedReader) -> impl Decoder { + Deflate64Decoder::new(r) +} diff --git a/crates/rc-zip/src/reader/sync/entry_reader/mod.rs b/crates/rc-zip/src/reader/sync/entry_reader/mod.rs index aa7acbd..6dffe23 100644 --- a/crates/rc-zip/src/reader/sync/entry_reader/mod.rs +++ b/crates/rc-zip/src/reader/sync/entry_reader/mod.rs @@ -13,6 +13,9 @@ mod lzma_dec; #[cfg(feature = "deflate")] mod deflate_dec; +#[cfg(feature = "deflate64")] +mod deflate64_dec; + use cfg_if::cfg_if; use nom::Offset; use std::io; @@ -102,7 +105,11 @@ where } => { { let buffer = decoder.get_mut().get_mut(); - if !self.eof && buffer.available_space() > 0 { + if !self.eof && buffer.available_data() == 0 { + if buffer.available_space() == 0 { + buffer.shift(); + } + match self.rd.read(buffer.space())? { 0 => { self.eof = true; @@ -271,6 +278,15 @@ where } } } + Method::Deflate64 => { + cfg_if! { + if #[cfg(feature = "deflate64")] { + Box::new(deflate64_dec::mk_decoder(limited_reader)) + } else { + return Err(Error::method_not_enabled(self.method)); + } + } + } Method::Lzma => { cfg_if! { if #[cfg(feature = "lzma")] { diff --git a/crates/rc-zip/src/tests.rs b/crates/rc-zip/src/tests.rs index 2f08ea5..51c5eb1 100644 --- a/crates/rc-zip/src/tests.rs +++ b/crates/rc-zip/src/tests.rs @@ -284,6 +284,18 @@ fn test_cases() -> Vec { }], ..Default::default() }, + #[cfg(feature = "deflate64")] + ZipTest { + source: ZipSource::File("found-me-deflate64.zip"), + expected_encoding: Some(Encoding::Utf8), + files: vec![ZipTestFile { + name: "found-me.txt", + content: FileContent::Bytes("Oh no, you found me\n".repeat(5000).into()), + modified: Some(date((2024, 1, 26), (17, 14, 36), 0, time_zone(0)).unwrap()), + ..Default::default() + }], + ..Default::default() + }, ] }