Skip to main content

pacenotes_installer\zip/
entry_data.rs

1const LOCAL_HEADER_LENGTH: usize = 30;
2const METHOD_STORED: u16 = 0;
3const METHOD_DEFLATED: u16 = 8;
4
5/// One entry's bytes, inflated if need be and checked against its CRC, which
6/// catches a cache file that rotted after its sha256 was verified.
7pub fn read(
8    zip: &[u8],
9    entry: &super::central_directory::Entry,
10) -> Result<Vec<u8>, super::ZipError> {
11    let data = compressed_bytes(zip, entry)?;
12    let content = match entry.method {
13        METHOD_STORED => data.to_vec(),
14        METHOD_DEFLATED => {
15            miniz_oxide::inflate::decompress_to_vec_with_limit(data, entry.uncompressed).map_err(
16                |e| {
17                    super::ZipError::new(format!(
18                        "{} could not be decompressed ({:?})",
19                        entry.path, e.status
20                    ))
21                },
22            )?
23        }
24        other => {
25            return Err(super::ZipError::new(format!(
26                "{} uses compression method {other}, which the installer cannot read",
27                entry.path
28            )))
29        }
30    };
31
32    if content.len() != entry.uncompressed {
33        return Err(super::ZipError::new(format!(
34            "{} is {} bytes but the package says {}",
35            entry.path,
36            content.len(),
37            entry.uncompressed
38        )));
39    }
40    if super::crc32::crc32(&content) != entry.crc32 {
41        return Err(super::ZipError::new(format!(
42            "{} is damaged (checksum mismatch); delete it from the cache and \
43             download it again",
44            entry.path
45        )));
46    }
47    Ok(content)
48}
49
50/// Located through the local header's own name and extra lengths, which an
51/// archiver may write differently from the central copy.
52fn compressed_bytes<'a>(
53    zip: &'a [u8],
54    entry: &super::central_directory::Entry,
55) -> Result<&'a [u8], super::ZipError> {
56    let at = entry.local_offset;
57    if super::central_directory::u32_at(zip, at)
58        != Some(super::central_directory::SIGNATURE_LOCAL_HEADER)
59    {
60        return Err(super::ZipError::new(format!(
61            "{} is not where this package says it is",
62            entry.path
63        )));
64    }
65    let name_length = super::central_directory::u16_at(zip, at + 26)
66        .ok_or_else(super::ZipError::truncated)? as usize;
67    let extra_length = super::central_directory::u16_at(zip, at + 28)
68        .ok_or_else(super::ZipError::truncated)? as usize;
69    let start = at + LOCAL_HEADER_LENGTH + name_length + extra_length;
70    zip.get(start..start + entry.compressed)
71        .ok_or_else(super::ZipError::truncated)
72}