rawzip 0.5.1

A Zip archive reader and writer
Documentation
There are a few ways one can measure an entity's integrity.

NOTE: For a more holistic view of what goes into safely extracting files, see the README's [Security section][security] and the [extractor example][extract-example].

The quickstart's [`verifying_reader`][crate::ZipSliceEntry::verifying_reader]
is the normal read path. It checks the decompressed CRC-32 and byte count against
the central directory. This is where most zip libraries stop.

A stricter policy can be created that requires the local header or trailing data descriptor
to repeat the central directory's CRC and sizes.

```rust
# use std::io::Write;
# fn deflate_zip(body: &[u8]) -> Vec<u8> {
#     let mut output = Vec::new();
#     let mut archive = rawzip::ZipArchiveWriter::new(&mut output);
#     let (mut entry, config) = archive.new_file("data.txt")
#         .compression_method(rawzip::CompressionMethod::DEFLATE).start().unwrap();
#     let encoder = flate2::write::DeflateEncoder::new(&mut entry, flate2::Compression::default());
#     let mut writer = config.wrap(encoder);
#     writer.write_all(body).unwrap();
#     let (encoder, descriptor) = writer.finish().unwrap();
#     encoder.finish().unwrap();
#     entry.finish(descriptor).unwrap();
#     archive.finish().unwrap();
#     output
# }
# let bytes = deflate_zip(b"trust, but verify");
use std::io::{self, Read};
use rawzip::{Crc32, ZipDataDescriptor, ZipVerification};

struct VerifiedReader<R> {
    inner: R,
    crc: Crc32,
    size: u64,
    central: ZipVerification,
    central_compressed_size: u64,
    descriptor: ZipDataDescriptor,
    verified: bool,
}

impl<R: Read> Read for VerifiedReader<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        if buf.is_empty() {
            return Ok(0);
        }

        let read = self.inner.read(buf)?;
        self.crc.update(&buf[..read]);
        self.size += read as u64;

        if !self.verified && (read == 0 || self.size >= self.central.uncompressed_size) {
            self.central.valid(ZipVerification {
                crc: self.descriptor.crc32(),
                uncompressed_size: self.descriptor.uncompressed_size(),
            }).map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;

            if self.descriptor.compressed_size() != self.central_compressed_size {
                return Err(io::Error::new(
                    io::ErrorKind::InvalidData,
                    "descriptor compressed size differs from central directory",
                ));
            }

            self.central.valid(ZipVerification {
                crc: self.crc.checksum(),
                uncompressed_size: self.size,
            }).map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;
            self.verified = true;
        }

        Ok(read)
    }
}

let archive = rawzip::ZipArchive::from_slice(&bytes)?;
let entry = archive.entries().next_entry()?.expect("one entry");
let central_compressed_size = entry.compressed_size_hint();
let local = archive.get_entry(entry.wayfinder())?;
let central = local.claim_verifier();
let descriptor = local.data_descriptor()?.expect("data descriptor");
let inflater = flate2::bufread::DeflateDecoder::new(local.data());
let verifier = VerifiedReader {
    inner: inflater,
    crc: Crc32::new(),
    size: 0,
    central,
    central_compressed_size,
    descriptor,
    verified: false,
};

let mut output = Vec::new();
io::copy(&mut verifier.take(central.uncompressed_size + 1), &mut output)?;
assert_eq!(output, b"trust, but verify");
# Ok::<(), Box<dyn std::error::Error>>(())
```

Next: [Encryption](crate::guide::encryption)

[crc-tests]: https://github.com/nickbabcock/rawzip/blob/master/tests/it/crc_tests.rs
[extract-example]: https://github.com/nickbabcock/rawzip/blob/master/examples/extract.rs
[security]: https://github.com/nickbabcock/rawzip#security