Skip to content

Commit

Permalink
BIGGER CHANGE! add EntryReader/etc
Browse files Browse the repository at this point in the history
  • Loading branch information
cosmicexplorer committed Jul 2, 2024
1 parent 9c1ec1b commit 33f000d
Show file tree
Hide file tree
Showing 2 changed files with 179 additions and 23 deletions.
78 changes: 68 additions & 10 deletions src/crc32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ use std::io;
use std::io::prelude::*;

use crc32fast::Hasher;
use displaydoc::Display;
use thiserror::Error;

/// Reader that validates the CRC32 when it reaches the EOF.
pub struct Crc32Reader<R> {
pub struct Crc32AesReader<R> {
inner: R,
hasher: Hasher,
check: u32,
Expand All @@ -15,11 +17,11 @@ pub struct Crc32Reader<R> {
ae2_encrypted: bool,
}

impl<R> Crc32Reader<R> {
/// Get a new Crc32Reader which checks the inner reader against checksum.
impl<R> Crc32AesReader<R> {
/// Get a new Crc32AesReader which checks the inner reader against checksum.
/// The check is disabled if `ae2_encrypted == true`.
pub(crate) fn new(inner: R, checksum: u32, ae2_encrypted: bool) -> Crc32Reader<R> {
Crc32Reader {
pub(crate) fn new(inner: R, checksum: u32, ae2_encrypted: bool) -> Crc32AesReader<R> {
Crc32AesReader {
inner,
hasher: Hasher::new(),
check: checksum,
Expand All @@ -36,7 +38,7 @@ impl<R> Crc32Reader<R> {
}
}

impl<R: Read> Read for Crc32Reader<R> {
impl<R: Read> Read for Crc32AesReader<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let invalid_check = !buf.is_empty() && !self.check_matches() && !self.ae2_encrypted;

Expand All @@ -52,6 +54,62 @@ impl<R: Read> Read for Crc32Reader<R> {
}
}

/// Errors from crc32 calculation.
#[derive(Debug, Display, Error)]
pub enum Crc32Error {
/// invalid checksum: expected {0}, got {1}
InvalidChecksum(u32, u32),
}

/// Reader that validates the CRC32 when it reaches the EOF.
pub struct Crc32Reader<R> {
inner: R,
hasher: Hasher,
check: u32,
}

impl<R> Crc32Reader<R> {
/// Get a new Crc32Reader which checks the inner reader against checksum.
pub(crate) fn new(inner: R, checksum: u32) -> Self {
Crc32Reader {
inner,
hasher: Hasher::new(),
check: checksum,
}
}

fn check_matches(&self) -> Result<(), Crc32Error> {
let res = self.hasher.clone().finalize();
if self.check == res {
Ok(())
} else {
Err(Crc32Error::InvalidChecksum(self.check, res))
}
}

pub fn into_inner(self) -> R {
self.inner
}
}

impl<R: Read> Read for Crc32Reader<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if buf.is_empty() {
return self.inner.read(buf).inspect(|n| assert_eq!(*n, 0));
}

let count = self.inner.read(buf)?;
if count == 0 {
return self
.check_matches()
.map(|()| 0)
.map_err(|e| io::Error::other(e));
}
self.hasher.update(&buf[0..count]);
Ok(count)
}
}

#[cfg(test)]
mod test {
use super::*;
Expand All @@ -61,10 +119,10 @@ mod test {
let data: &[u8] = b"";
let mut buf = [0; 1];

let mut reader = Crc32Reader::new(data, 0, false);
let mut reader = Crc32AesReader::new(data, 0, false);
assert_eq!(reader.read(&mut buf).unwrap(), 0);

let mut reader = Crc32Reader::new(data, 1, false);
let mut reader = Crc32AesReader::new(data, 1, false);
assert!(reader
.read(&mut buf)
.unwrap_err()
Expand All @@ -77,7 +135,7 @@ mod test {
let data: &[u8] = b"1234";
let mut buf = [0; 1];

let mut reader = Crc32Reader::new(data, 0x9be3e0a3, false);
let mut reader = Crc32AesReader::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);
Expand All @@ -92,7 +150,7 @@ mod test {
let data: &[u8] = b"1234";
let mut buf = [0; 5];

let mut reader = Crc32Reader::new(data, 0x9be3e0a3, false);
let mut reader = Crc32AesReader::new(data, 0x9be3e0a3, false);
assert_eq!(reader.read(&mut buf[..0]).unwrap(), 0);
assert_eq!(reader.read(&mut buf).unwrap(), 4);
}
Expand Down
124 changes: 111 additions & 13 deletions src/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use crate::aes::{AesReader, AesReaderValid};
use crate::compression::CompressionMethod;
use crate::cp437::FromCp437;
use crate::crc32::Crc32Reader;
use crate::crc32::{Crc32AesReader, Crc32Reader};
use crate::extra_fields::{ExtendedTimestamp, ExtraField};
use crate::read::zip_archive::{Shared, SharedBuilder};
use crate::result::{ZipError, ZipResult};
Expand Down Expand Up @@ -178,20 +178,57 @@ impl<'a> CryptoReader<'a> {
}
}

pub(crate) enum EntryReader<R> {
Raw(R),
Stored(Crc32Reader<R>),
#[cfg(feature = "_deflate-any")]
Deflated(Crc32Reader<DeflateDecoder<R>>),
#[cfg(feature = "deflate64")]
Deflate64(Crc32Reader<Deflate64Decoder<io::BufReader<R>>>),
#[cfg(feature = "bzip2")]
Bzip2(Crc32Reader<BzDecoder<R>>),
/* #[cfg(feature = "zstd")] */
/* Zstd(Crc32Reader<ZstdDecoder<'static, io::BufReader<R>>>), */
#[cfg(feature = "lzma")]
Lzma(Crc32Reader<Box<LzmaDecoder<R>>>),
}

impl<R> Read for EntryReader<R>
where
R: Read,
{
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self {
Self::Raw(r) => r.read(buf),
Self::Stored(r) => r.read(buf),
#[cfg(feature = "_deflate-any")]
Self::Deflated(r) => r.read(buf),
#[cfg(feature = "deflate64")]
Self::Deflate64(r) => r.read(buf),
#[cfg(feature = "bzip2")]
Self::Bzip2(r) => r.read(buf),
/* #[cfg(feature = "zstd")] */
/* Self::Zstd(r) => r.read(buf), */
#[cfg(feature = "lzma")]
Self::Lzma(r) => r.read(buf),
}
}
}

pub(crate) enum ZipFileReader<'a> {
NoReader,
Raw(io::Take<&'a mut dyn Read>),
Stored(Crc32Reader<CryptoReader<'a>>),
Stored(Crc32AesReader<CryptoReader<'a>>),
#[cfg(feature = "_deflate-any")]
Deflated(Crc32Reader<DeflateDecoder<CryptoReader<'a>>>),
Deflated(Crc32AesReader<DeflateDecoder<CryptoReader<'a>>>),
#[cfg(feature = "deflate64")]
Deflate64(Crc32Reader<Deflate64Decoder<io::BufReader<CryptoReader<'a>>>>),
Deflate64(Crc32AesReader<Deflate64Decoder<io::BufReader<CryptoReader<'a>>>>),
#[cfg(feature = "bzip2")]
Bzip2(Crc32Reader<BzDecoder<CryptoReader<'a>>>),
Bzip2(Crc32AesReader<BzDecoder<CryptoReader<'a>>>),
#[cfg(feature = "zstd")]
Zstd(Crc32Reader<ZstdDecoder<'a, io::BufReader<CryptoReader<'a>>>>),
Zstd(Crc32AesReader<ZstdDecoder<'a, io::BufReader<CryptoReader<'a>>>>),
#[cfg(feature = "lzma")]
Lzma(Crc32Reader<Box<LzmaDecoder<CryptoReader<'a>>>>),
Lzma(Crc32AesReader<Box<LzmaDecoder<CryptoReader<'a>>>>),
}

impl<'a> Read for ZipFileReader<'a> {
Expand Down Expand Up @@ -348,15 +385,15 @@ pub(crate) fn make_reader(
let ae2_encrypted = reader.is_ae2_encrypted();

match compression_method {
CompressionMethod::Stored => Ok(ZipFileReader::Stored(Crc32Reader::new(
CompressionMethod::Stored => Ok(ZipFileReader::Stored(Crc32AesReader::new(
reader,
crc32,
ae2_encrypted,
))),
#[cfg(feature = "_deflate-any")]
CompressionMethod::Deflated => {
let deflate_reader = DeflateDecoder::new(reader);
Ok(ZipFileReader::Deflated(Crc32Reader::new(
Ok(ZipFileReader::Deflated(Crc32AesReader::new(
deflate_reader,
crc32,
ae2_encrypted,
Expand All @@ -365,7 +402,7 @@ pub(crate) fn make_reader(
#[cfg(feature = "deflate64")]
CompressionMethod::Deflate64 => {
let deflate64_reader = Deflate64Decoder::new(reader);
Ok(ZipFileReader::Deflate64(Crc32Reader::new(
Ok(ZipFileReader::Deflate64(Crc32AesReader::new(
deflate64_reader,
crc32,
ae2_encrypted,
Expand All @@ -374,7 +411,7 @@ pub(crate) fn make_reader(
#[cfg(feature = "bzip2")]
CompressionMethod::Bzip2 => {
let bzip2_reader = BzDecoder::new(reader);
Ok(ZipFileReader::Bzip2(Crc32Reader::new(
Ok(ZipFileReader::Bzip2(Crc32AesReader::new(
bzip2_reader,
crc32,
ae2_encrypted,
Expand All @@ -383,7 +420,7 @@ pub(crate) fn make_reader(
#[cfg(feature = "zstd")]
CompressionMethod::Zstd => {
let zstd_reader = ZstdDecoder::new(reader).unwrap();
Ok(ZipFileReader::Zstd(Crc32Reader::new(
Ok(ZipFileReader::Zstd(Crc32AesReader::new(
zstd_reader,
crc32,
ae2_encrypted,
Expand All @@ -392,7 +429,7 @@ pub(crate) fn make_reader(
#[cfg(feature = "lzma")]
CompressionMethod::Lzma => {
let reader = LzmaDecoder::new(reader);
Ok(ZipFileReader::Lzma(Crc32Reader::new(
Ok(ZipFileReader::Lzma(Crc32AesReader::new(
Box::new(reader),
crc32,
ae2_encrypted,
Expand All @@ -402,6 +439,65 @@ pub(crate) fn make_reader(
}
}

pub(crate) fn find_entry_content<R>(data: &ZipFileData, mut reader: R) -> ZipResult<io::Take<R>>
where
R: Read + Seek,
{
// TODO: use .get_or_try_init() once stabilized to provide a closure returning a Result!
let data_start = match data.data_start.get() {
Some(data_start) => *data_start,
None => find_data_start(data, &mut reader)?,
};

reader.seek(io::SeekFrom::Start(data_start))?;
Ok(reader.take(data.compressed_size))
}

pub(crate) fn make_entry_reader<R>(
compression_method: CompressionMethod,
crc32: u32,
mut reader: R,
) -> ZipResult<EntryReader<R>>
where
R: Read,
{
match compression_method {
CompressionMethod::Stored => Ok(EntryReader::Stored(Crc32Reader::new(reader, crc32))),
#[cfg(feature = "_deflate-any")]
CompressionMethod::Deflated => {
let deflate_reader = DeflateDecoder::new(reader);
Ok(EntryReader::Deflated(Crc32Reader::new(
deflate_reader,
crc32,
)))
}
#[cfg(feature = "deflate64")]
CompressionMethod::Deflate64 => {
let deflate64_reader = Deflate64Decoder::new(reader);
Ok(EntryReader::Deflate64(Crc32Reader::new(
deflate64_reader,
crc32,
)))
}
#[cfg(feature = "bzip2")]
CompressionMethod::Bzip2 => {
let bzip2_reader = BzDecoder::new(reader);
Ok(EntryReader::Bzip2(Crc32Reader::new(bzip2_reader, crc32)))
}
/* #[cfg(feature = "zstd")] */
/* CompressionMethod::Zstd => { */
/* let zstd_reader = ZstdDecoder::new(reader).unwrap(); */
/* Ok(EntryReader::Zstd(Crc32Reader::new(zstd_reader, crc32))) */
/* } */
#[cfg(feature = "lzma")]
CompressionMethod::Lzma => {
let reader = LzmaDecoder::new(reader);
Ok(EntryReader::Lzma(Crc32Reader::new(Box::new(reader), crc32)))
}
_ => Err(UnsupportedArchive("Compression method not supported")),
}
}

#[derive(Debug)]
pub(crate) struct CentralDirectoryInfo {
pub(crate) archive_offset: u64,
Expand Down Expand Up @@ -910,6 +1006,8 @@ impl<R: Read + Seek> ZipArchive<R> {
}
let symlink_target = if file.is_symlink() && (cfg!(unix) || cfg!(windows)) {
let mut target = Vec::with_capacity(file.size() as usize);
/* FIXME: this is broken: needs to be .read_to_end(), otherwise it writes into an
* empty slice. */
file.read_exact(&mut target)?;
Some(target)
} else {
Expand Down

0 comments on commit 33f000d

Please sign in to comment.