1use std::io::{self, Read, Write};
42
43pub const FOOTER_SIZE: usize = 76;
44pub const MAGIC: [u8; 8] = *b"ONELF\x00\x01\x00";
45pub const END_MAGIC: [u8; 8] = *b"FLENONE\x00";
46
47bitflags! {
48 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
49 pub struct Flags: u16 {
50 const HAS_DICT = 1 << 0;
51 const MEMFD_HINT = 1 << 1;
52 const SHARUN_COMPAT = 1 << 2;
53 const STORED = 1 << 3;
57 const EXTERNAL_UPDATER = 1 << 4;
63 const NO_HOST_LIB_DIRS = 1 << 5;
74 }
75}
76
77#[derive(Debug, Clone)]
78pub struct Footer {
79 pub format_version: u16,
81 pub flags: Flags,
83 pub manifest_offset: u64,
85 pub manifest_compressed: u64,
87 pub manifest_original: u64,
89 pub payload_offset: u64,
91 pub payload_size: u64,
93 pub dict_offset: u64,
95 pub dict_size: u32,
97 pub manifest_checksum: [u8; 4],
100}
101
102impl Footer {
103 pub fn is_stored(&self) -> bool {
106 self.flags.contains(Flags::STORED)
107 }
108
109 pub fn write_to<W: Write>(&self, w: &mut W) -> io::Result<()> {
110 w.write_all(&MAGIC)?; w.write_all(&self.format_version.to_le_bytes())?; w.write_all(&self.flags.bits().to_le_bytes())?; w.write_all(&self.manifest_offset.to_le_bytes())?; w.write_all(&self.manifest_compressed.to_le_bytes())?; w.write_all(&self.manifest_original.to_le_bytes())?; w.write_all(&self.payload_offset.to_le_bytes())?; w.write_all(&self.payload_size.to_le_bytes())?; w.write_all(&self.dict_offset.to_le_bytes())?; w.write_all(&self.dict_size.to_le_bytes())?; w.write_all(&self.manifest_checksum)?; w.write_all(&END_MAGIC)?; Ok(()) }
124
125 pub fn read_from<R: Read>(r: &mut R) -> io::Result<Self> {
126 let mut buf = [0u8; FOOTER_SIZE];
127 r.read_exact(&mut buf)?;
128 Self::from_bytes(&buf)
129 }
130
131 pub fn from_bytes(buf: &[u8; FOOTER_SIZE]) -> io::Result<Self> {
132 if buf[0..8] != MAGIC {
133 return Err(io::Error::new(
134 io::ErrorKind::InvalidData,
135 "invalid onelf magic",
136 ));
137 }
138 if buf[68..76] != END_MAGIC {
139 return Err(io::Error::new(
140 io::ErrorKind::InvalidData,
141 "invalid onelf end magic",
142 ));
143 }
144
145 let format_version = u16::from_le_bytes(buf[8..10].try_into().unwrap());
146 if format_version != 1 {
147 return Err(io::Error::new(
148 io::ErrorKind::InvalidData,
149 format!("unsupported format version: {}", format_version),
150 ));
151 }
152
153 let flags_raw = u16::from_le_bytes(buf[10..12].try_into().unwrap());
154 let flags = Flags::from_bits_retain(flags_raw);
155
156 Ok(Footer {
157 format_version,
158 flags,
159 manifest_offset: u64::from_le_bytes(buf[12..20].try_into().unwrap()),
160 manifest_compressed: u64::from_le_bytes(buf[20..28].try_into().unwrap()),
161 manifest_original: u64::from_le_bytes(buf[28..36].try_into().unwrap()),
162 payload_offset: u64::from_le_bytes(buf[36..44].try_into().unwrap()),
163 payload_size: u64::from_le_bytes(buf[44..52].try_into().unwrap()),
164 dict_offset: u64::from_le_bytes(buf[52..60].try_into().unwrap()),
165 dict_size: u32::from_le_bytes(buf[60..64].try_into().unwrap()),
166 manifest_checksum: buf[64..68].try_into().unwrap(),
167 })
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 fn sample() -> Footer {
176 Footer {
177 format_version: 1,
178 flags: Flags::HAS_DICT | Flags::STORED,
179 manifest_offset: 0x1122,
180 manifest_compressed: 0x33,
181 manifest_original: 0x44,
182 payload_offset: 0x55,
183 payload_size: 0x66,
184 dict_offset: 0x77,
185 dict_size: 0x88,
186 manifest_checksum: [1, 2, 3, 4],
187 }
188 }
189
190 #[test]
191 fn footer_roundtrips() {
192 let f = sample();
193 let mut buf = Vec::new();
194 f.write_to(&mut buf).unwrap();
195 assert_eq!(buf.len(), FOOTER_SIZE);
196 let back = Footer::from_bytes(&buf.try_into().unwrap()).unwrap();
197 assert_eq!(back.format_version, f.format_version);
198 assert_eq!(back.flags, f.flags);
199 assert_eq!(back.manifest_offset, f.manifest_offset);
200 assert_eq!(back.dict_size, f.dict_size);
201 assert_eq!(back.manifest_checksum, f.manifest_checksum);
202 }
203
204 #[test]
205 fn malformed_footers_error_without_panic() {
206 let mut buf = Vec::new();
207 sample().write_to(&mut buf).unwrap();
208
209 let mut bad = buf.clone();
211 bad[0] ^= 0xff;
212 assert!(Footer::from_bytes(&bad.clone().try_into().unwrap()).is_err());
213
214 let mut bad = buf.clone();
216 bad[68] ^= 0xff;
217 assert!(Footer::from_bytes(&bad.clone().try_into().unwrap()).is_err());
218
219 let mut bad = buf.clone();
221 bad[8..10].copy_from_slice(&2u16.to_le_bytes());
222 assert!(Footer::from_bytes(&bad.try_into().unwrap()).is_err());
223 }
224}