Skip to main content

git_zero_server/receive/
attributes.rs

1//! Streaming strict-receive checks for a designated attributes blob.
2use git_zero_core::{Error, Result};
3
4/// Match Git's 100 MiB file bound and 2048-byte line bound. As in Git's
5/// attributes fsck, NUL terminates line inspection, while the complete blob
6/// length remains subject to the file bound and canonical authentication.
7pub struct FsckAttributes {
8    remaining: u64,
9    line: u16,
10    stopped: bool,
11    failure: Option<Error>,
12}
13
14impl FsckAttributes {
15    pub fn new(size: u64) -> Result<Self> {
16        if size > 100 * 1024 * 1024 {
17            return Err(Error::InvalidObject);
18        }
19        Ok(Self {
20            remaining: size,
21            line: 0,
22            stopped: false,
23            failure: None,
24        })
25    }
26
27    pub fn update(&mut self, bytes: &[u8]) -> Result<()> {
28        if let Some(error) = self.failure {
29            return Err(error);
30        }
31        let result = self.feed(bytes);
32        if let Err(error) = result {
33            self.failure = Some(error);
34        }
35        result
36    }
37
38    fn feed(&mut self, bytes: &[u8]) -> Result<()> {
39        self.remaining = self
40            .remaining
41            .checked_sub(bytes.len() as u64)
42            .ok_or(Error::InvalidObject)?;
43        if self.stopped {
44            return Ok(());
45        }
46        for &byte in bytes {
47            if byte == 0 {
48                self.stopped = true;
49                break;
50            }
51            if byte == b'\n' {
52                self.line = 0;
53            } else {
54                self.line += 1;
55                if self.line >= 2048 {
56                    return Err(Error::InvalidObject);
57                }
58            }
59        }
60        Ok(())
61    }
62
63    pub fn finish(&self) -> Result<()> {
64        if let Some(error) = self.failure {
65            return Err(error);
66        }
67        if self.remaining == 0 {
68            Ok(())
69        } else {
70            Err(Error::InvalidObject)
71        }
72    }
73}