1use crate::image::Image;
2
3use snafu::{ResultExt, Snafu, ensure};
4use zerocopy::{
5 FromBytes, Immutable, KnownLayout,
6 little_endian::{U16, U32, U64},
7};
8
9#[derive(Clone, Copy, FromBytes, KnownLayout, Immutable)]
11#[repr(C)]
12pub struct InodeRaw {
13 pub mode: U16,
15 pub nlink: U16,
17 pub flags: U32,
19 pub size: U64,
21 pub size_compressed: U64,
23 pub atime: U64,
25 pub mtime: U64,
27 pub ctime: U64,
29 pub birthtime: U64,
31 pub mtimensec: U32,
33 pub atimensec: U32,
35 pub ctimensec: U32,
37 pub birthnsec: U32,
39 pub uid: U32,
41 pub gid: U32,
43 pub spare: [u8; 16],
45 pub blocks: U32,
47}
48
49#[derive(Debug, Snafu)]
51#[non_exhaustive]
52pub enum LoadBlocksError {
53 #[snafu(display("cannot read block #{block}"))]
54 Read { block: u32, source: std::io::Error },
55
56 #[snafu(display("block #{block} does not exist"))]
57 NotExists { block: u32 },
58
59 #[snafu(display("double indirect block is not supported for inode #{inode}"))]
60 DoubleIndirectBlockNotSupported { inode: usize },
61}
62
63pub struct Inode {
65 index: usize,
66 raw: InodeRaw,
67 direct_blocks: [u32; 12],
68 #[allow(dead_code)] direct_sigs: [Option<[u8; 32]>; 12],
70 indirect_blocks: [u32; 5],
71 #[allow(dead_code)] indirect_sigs: [Option<[u8; 32]>; 5],
73 signed: bool,
76}
77
78impl Inode {
79 fn blocks(&self) -> u32 {
80 self.raw.blocks.get()
81 }
82
83 pub(crate) fn contiguous_blocks(&self) -> Option<(u32, u32)> {
88 let count = self.blocks();
89
90 if count == 0 {
91 return None;
92 }
93
94 if self.direct_blocks[1] == 0xffffffff {
96 return Some((self.direct_blocks[0], count));
97 }
98
99 if count == 1 {
101 return Some((self.direct_blocks[0], 1));
102 }
103
104 None
105 }
106
107 pub(super) fn from_raw32_unsigned(index: usize, src: &mut &[u8]) -> Result<Self, FromRawError> {
108 let (raw, rest) = InodeRaw::read_from_prefix(src).map_err(|_| FromRawError::TooSmall)?;
110 *src = rest;
111
112 ensure!(src.len() >= 68, from_raw_error::TooSmallSnafu);
114
115 let block_data = &src[..68];
116 *src = &src[68..];
117
118 let mut direct_blocks = [0u32; 12];
119 let mut indirect_blocks = [0u32; 5];
120
121 for (i, block) in direct_blocks.iter_mut().enumerate() {
122 let offset = i * 4;
123 *block = u32::from_le_bytes(block_data[offset..offset + 4].try_into().unwrap());
124 }
125
126 for (i, block) in indirect_blocks.iter_mut().enumerate() {
127 let offset = 48 + i * 4;
128 *block = u32::from_le_bytes(block_data[offset..offset + 4].try_into().unwrap());
129 }
130
131 Ok(Self {
132 index,
133 raw,
134 direct_blocks,
135 direct_sigs: [None; 12],
136 indirect_blocks,
137 indirect_sigs: [None; 5],
138 signed: false,
139 })
140 }
141
142 pub(super) fn from_raw32_signed(index: usize, src: &mut &[u8]) -> Result<Self, FromRawError> {
143 let (raw, rest) = InodeRaw::read_from_prefix(src).map_err(|_| FromRawError::TooSmall)?;
145 *src = rest;
146
147 ensure!(src.len() >= 612, from_raw_error::TooSmallSnafu);
152
153 let block_data = &src[..612];
154 *src = &src[612..];
155
156 let mut direct_blocks = [0u32; 12];
157 let mut direct_sigs: [Option<[u8; 32]>; 12] = [None; 12];
158 let mut indirect_blocks = [0u32; 5];
159 let mut indirect_sigs: [Option<[u8; 32]>; 5] = [None; 5];
160
161 let mut offset = 0;
162 for (sig, block) in direct_sigs.iter_mut().zip(direct_blocks.iter_mut()) {
163 *sig = Some(block_data[offset..offset + 32].try_into().unwrap());
164 *block = u32::from_le_bytes(block_data[offset + 32..offset + 36].try_into().unwrap());
165 offset += 36;
166 }
167
168 for (sig, block) in indirect_sigs.iter_mut().zip(indirect_blocks.iter_mut()) {
169 *sig = Some(block_data[offset..offset + 32].try_into().unwrap());
170 *block = u32::from_le_bytes(block_data[offset + 32..offset + 36].try_into().unwrap());
171 offset += 36;
172 }
173
174 Ok(Self {
175 index,
176 raw,
177 direct_blocks,
178 direct_sigs,
179 indirect_blocks,
180 indirect_sigs,
181 signed: true,
182 })
183 }
184
185 pub fn load_block_map(
189 &self,
190 image: &dyn Image,
191 block_size: u32,
192 ) -> Result<Vec<u32>, LoadBlocksError> {
193 let block_count = self.blocks() as usize;
194 let mut blocks: Vec<u32> = Vec::with_capacity(block_count);
195
196 if block_count == 0 {
197 return Ok(blocks);
198 }
199
200 if self.direct_blocks[1] == 0xffffffff {
202 let start = self.direct_blocks[0];
203 for block in start..(start + self.blocks()) {
204 blocks.push(block);
205 }
206 return Ok(blocks);
207 }
208
209 for i in 0..12 {
211 blocks.push(self.direct_blocks[i]);
212 if blocks.len() == block_count {
213 return Ok(blocks);
214 }
215 }
216
217 let bs = block_size as u64;
218
219 let block_num = self.indirect_blocks[0];
221 let offset = (block_num as u64) * bs;
222
223 let mut block0 = vec![0; block_size as usize];
224
225 image
226 .read_exact_at(offset, &mut block0)
227 .context(ReadSnafu { block: block_num })?;
228
229 let mut data = block0.as_slice();
230
231 while let Some(i) = self.read_indirect(&mut data) {
232 blocks.push(i);
233 if blocks.len() == block_count {
234 return Ok(blocks);
235 }
236 }
237
238 let block_num = self.indirect_blocks[1];
240 let offset = (block_num as u64) * bs;
241
242 image
243 .read_exact_at(offset, &mut block0)
244 .context(ReadSnafu { block: block_num })?;
245
246 let mut block1 = vec![0; block_size as usize];
247 let mut data0 = block0.as_slice();
248
249 while let Some(i) = self.read_indirect(&mut data0) {
250 let offset = (i as u64) * bs;
251
252 image
253 .read_exact_at(offset, &mut block1)
254 .context(ReadSnafu { block: block_num })?;
255
256 let mut data1 = block1.as_slice();
257
258 while let Some(j) = self.read_indirect(&mut data1) {
259 blocks.push(j);
260 if blocks.len() == block_count {
261 return Ok(blocks);
262 }
263 }
264 }
265
266 DoubleIndirectBlockNotSupportedSnafu { inode: self.index }.fail()
267 }
268
269 fn read_indirect(&self, raw: &mut &[u8]) -> Option<u32> {
274 let (entry_size, value_offset) = if self.signed { (36, 32) } else { (4, 0) };
275
276 if raw.len() < entry_size {
277 return None;
278 }
279
280 let value = u32::from_le_bytes(raw[value_offset..value_offset + 4].try_into().unwrap());
281 *raw = &raw[entry_size..];
282 Some(value)
283 }
284
285 pub fn mode(&self) -> u16 {
286 self.raw.mode.get()
287 }
288
289 pub fn flags(&self) -> InodeFlags {
290 InodeFlags(self.raw.flags.get())
291 }
292
293 pub fn size(&self) -> u64 {
294 self.raw.size.get()
295 }
296
297 pub fn compressed_len(&self) -> u64 {
298 self.raw.size_compressed.get()
299 }
300
301 pub fn atime(&self) -> u64 {
302 self.raw.atime.get()
303 }
304
305 pub fn mtime(&self) -> u64 {
306 self.raw.mtime.get()
307 }
308
309 pub fn ctime(&self) -> u64 {
310 self.raw.ctime.get()
311 }
312
313 pub fn birthtime(&self) -> u64 {
314 self.raw.birthtime.get()
315 }
316
317 pub fn mtimensec(&self) -> u32 {
318 self.raw.mtimensec.get()
319 }
320
321 pub fn atimensec(&self) -> u32 {
322 self.raw.atimensec.get()
323 }
324
325 pub fn ctimensec(&self) -> u32 {
326 self.raw.ctimensec.get()
327 }
328
329 pub fn birthnsec(&self) -> u32 {
330 self.raw.birthnsec.get()
331 }
332
333 pub fn uid(&self) -> u32 {
334 self.raw.uid.get()
335 }
336
337 pub fn gid(&self) -> u32 {
338 self.raw.gid.get()
339 }
340
341 pub const fn raw(&self) -> &InodeRaw {
342 &self.raw
343 }
344}
345
346#[derive(Clone, Copy, Debug)]
348#[repr(transparent)]
349pub struct InodeFlags(u32);
350
351impl InodeFlags {
352 pub fn is_compressed(self) -> bool {
353 self.0 & 0x00000001 != 0
354 }
355
356 pub fn value(self) -> u32 {
357 self.0
358 }
359}
360
361#[derive(Debug, Snafu)]
363#[snafu(module)]
364#[non_exhaustive]
365pub enum FromRawError {
366 #[snafu(display("i/o failed"))]
368 IoFailed { source: std::io::Error },
369 #[snafu(display("data too small"))]
371 TooSmall,
372}