Skip to main content

git_zero_core/zlib/
gzip.rs

1//! Single-member RFC 1952 decoding for bounded HTTP upload bodies.
2use super::{InflateLimits, InflateStatus, Inflater, WINDOW_SIZE};
3use crate::{Error, Result, pack::crc32};
4
5/// Decode a complete single gzip member into caller-owned storage. Validate
6/// optional header fields/checksum, raw DEFLATE, CRC32 and ISIZE. Trailing data
7/// and concatenated members are rejected. All output is provisional on error.
8pub fn decode_gzip(
9    input: &[u8],
10    output: &mut [u8],
11    window: &mut [u8; WINDOW_SIZE],
12    limits: InflateLimits,
13) -> Result<usize> {
14    let header = input.get(..10).ok_or(Error::InvalidZlib)?;
15    if header[..3] != [31, 139, 8] || header[3] & 0xe0 != 0 {
16        return Err(Error::InvalidZlib);
17    }
18    let flags = header[3];
19    let mut offset = 10;
20    if flags & 4 != 0 {
21        let length = input.get(offset..offset + 2).ok_or(Error::InvalidZlib)?;
22        offset += 2 + usize::from(u16::from_le_bytes([length[0], length[1]]));
23        input.get(..offset).ok_or(Error::InvalidZlib)?;
24    }
25    for flag in [8, 16] {
26        if flags & flag != 0 {
27            offset += input
28                .get(offset..)
29                .ok_or(Error::InvalidZlib)?
30                .iter()
31                .position(|&b| b == 0)
32                .ok_or(Error::InvalidZlib)?
33                + 1;
34        }
35    }
36    if flags & 2 != 0 {
37        let checksum = input.get(offset..offset + 2).ok_or(Error::InvalidZlib)?;
38        if u16::from_le_bytes([checksum[0], checksum[1]]) != crc32(&input[..offset]) as u16 {
39            return Err(Error::InvalidZlib);
40        }
41        offset += 2;
42    }
43    let mut decoder = Inflater::new_raw(window, limits);
44    let progress = decoder.update(input.get(offset..).ok_or(Error::InvalidZlib)?, output, true)?;
45    if progress.status != InflateStatus::Finished {
46        return Err(Error::BufferTooSmall);
47    }
48    let trailer = input
49        .get(offset + progress.consumed..)
50        .ok_or(Error::InvalidZlib)?;
51    if trailer.len() != 8
52        || trailer[..4] != crc32(&output[..progress.written]).to_le_bytes()
53        || trailer[4..] != (progress.written as u32).to_le_bytes()
54    {
55        return Err(Error::InvalidZlib);
56    }
57    Ok(progress.written)
58}