nix_compat/nar/reader/
read.rs

1//! Helpers for reading [crate::nar::wire] format.
2
3use std::io::{
4    self,
5    ErrorKind::{Interrupted, InvalidData, UnexpectedEof},
6};
7
8use super::Reader;
9use crate::nar::wire::Tag;
10
11/// Consume a little-endian [prim@u64] from the reader.
12pub fn u64(reader: &mut Reader) -> io::Result<u64> {
13    let mut buf = [0; 8];
14    reader.read_exact(&mut buf)?;
15    Ok(u64::from_le_bytes(buf))
16}
17
18/// Consume a byte string from the reader into a provided buffer,
19/// returning the data bytes.
20pub fn bytes_buf<'a, const N: usize>(
21    reader: &mut Reader,
22    buf: &'a mut [u8; N],
23    max_len: usize,
24) -> io::Result<&'a [u8]> {
25    assert_eq!(N % 8, 0);
26    assert!(max_len <= N);
27
28    // read the length, and reject excessively large values
29    let len = self::u64(reader)?;
30    if len > max_len as u64 {
31        return Err(InvalidData.into());
32    }
33    // we know the length fits in a usize now
34    let len = len as usize;
35
36    // read the data and padding into a buffer
37    let buf_len = (len + 7) & !7;
38    reader.read_exact(&mut buf[..buf_len])?;
39
40    // verify that the padding is all zeroes
41    for &b in &buf[len..buf_len] {
42        if b != 0 {
43            return Err(InvalidData.into());
44        }
45    }
46
47    Ok(&buf[..len])
48}
49
50/// Consume a byte string of up to `max_len` bytes from the reader.
51pub fn bytes(reader: &mut Reader, max_len: usize) -> io::Result<Vec<u8>> {
52    assert!(max_len <= isize::MAX as usize);
53
54    // read the length, and reject excessively large values
55    let len = self::u64(reader)?;
56    if len > max_len as u64 {
57        return Err(InvalidData.into());
58    }
59    // we know the length fits in a usize now
60    let len = len as usize;
61
62    // read the data and padding into a buffer
63    let buf_len = (len + 7) & !7;
64    let mut buf = vec![0; buf_len];
65    reader.read_exact(&mut buf)?;
66
67    // verify that the padding is all zeroes
68    for b in buf.drain(len..) {
69        if b != 0 {
70            return Err(InvalidData.into());
71        }
72    }
73
74    Ok(buf)
75}
76
77/// Consume a known token from the reader.
78pub fn token<const N: usize>(reader: &mut Reader, token: &[u8; N]) -> io::Result<()> {
79    let mut buf = [0u8; N];
80
81    // This implements something similar to [Read::read_exact], but verifies that
82    // the input data matches the token while we read it. These two slices respectively
83    // represent the remaining token to be verified, and the remaining input buffer.
84    let mut token = &token[..];
85    let mut buf = &mut buf[..];
86
87    while !token.is_empty() {
88        match reader.read(buf) {
89            Ok(0) => {
90                return Err(UnexpectedEof.into());
91            }
92            Ok(n) => {
93                let (t, b);
94                (t, token) = token.split_at(n);
95                (b, buf) = buf.split_at_mut(n);
96
97                if t != b {
98                    return Err(InvalidData.into());
99                }
100            }
101            Err(e) => {
102                if e.kind() != Interrupted {
103                    return Err(e);
104                }
105            }
106        }
107    }
108
109    Ok(())
110}
111
112/// Consume a [Tag] from the reader.
113pub fn tag<T: Tag>(reader: &mut Reader) -> io::Result<T> {
114    let mut buf = T::make_buf();
115    let buf = buf.as_mut();
116
117    // first read the known minimum length…
118    reader.read_exact(&mut buf[..T::MIN])?;
119
120    // then decide which tag we're expecting
121    let tag = T::from_u8(buf[T::OFF]).ok_or(InvalidData)?;
122    let (head, tail) = tag.as_bytes().split_at(T::MIN);
123
124    // make sure what we've read so far is valid
125    if buf[..T::MIN] != *head {
126        return Err(InvalidData.into());
127    }
128
129    // …then read the rest, if any
130    if !tail.is_empty() {
131        let rest = tail.len();
132        reader.read_exact(&mut buf[..rest])?;
133
134        // and make sure it's what we expect
135        if buf[..rest] != *tail {
136            return Err(InvalidData.into());
137        }
138    }
139
140    Ok(tag)
141}