1use crate::block_io::BlockDevice;
24use crate::error::{Error, Result};
25
26pub const DIRECT_COUNT: u64 = 12;
28
29#[inline]
31fn ppb(block_size: u32) -> u64 {
32 (block_size / 4) as u64
33}
34
35#[inline]
37fn ptr_at(buf: &[u8], i: usize) -> u64 {
38 let off = i * 4;
39 u32::from_le_bytes(buf[off..off + 4].try_into().unwrap()) as u64
40}
41
42pub struct IndirectCache {
54 cached: Option<(u64, Vec<u8>)>,
55}
56
57impl Default for IndirectCache {
58 fn default() -> Self {
59 Self::new()
60 }
61}
62
63impl IndirectCache {
64 pub fn new() -> Self {
65 Self { cached: None }
66 }
67
68 fn get(&mut self, dev: &dyn BlockDevice, block_no: u64, block_size: u32) -> Result<&[u8]> {
71 let needs_read = !matches!(&self.cached, Some((b, _)) if *b == block_no);
72 if needs_read {
73 let mut buf = vec![0u8; block_size as usize];
74 let byte_off = block_no
75 .checked_mul(block_size as u64)
76 .ok_or(Error::Corrupt("indirect: block byte offset overflow"))?;
77 dev.read_at(byte_off, &mut buf)?;
78 self.cached = Some((block_no, buf));
79 }
80 Ok(&self.cached.as_ref().unwrap().1)
81 }
82}
83
84pub fn map_logical_any(
101 i_block: &[u8; 60],
102 inode_flags: u32,
103 dev: &dyn BlockDevice,
104 block_size: u32,
105 logical_block: u64,
106) -> Result<Option<u64>> {
107 if (inode_flags & crate::inode::InodeFlags::EXTENTS.bits()) != 0 {
108 crate::extent::map_logical(i_block, dev, block_size, logical_block)
109 } else {
110 let mut cache = IndirectCache::new();
111 lookup(i_block, dev, block_size, logical_block, &mut cache)
112 }
113}
114
115pub fn lookup(
127 i_block: &[u8; 60],
128 dev: &dyn BlockDevice,
129 block_size: u32,
130 logical_block: u64,
131 cache: &mut IndirectCache,
132) -> Result<Option<u64>> {
133 if block_size < 1024 || !block_size.is_power_of_two() {
134 return Err(Error::Corrupt("indirect: block_size out of range"));
135 }
136 let ppb = ppb(block_size);
137
138 if logical_block < DIRECT_COUNT {
140 let phys = ptr_at(i_block, logical_block as usize);
141 return Ok(if phys == 0 { None } else { Some(phys) });
142 }
143
144 let single_base = DIRECT_COUNT;
146 let single_end = single_base + ppb;
147 if logical_block < single_end {
148 let single_blk = ptr_at(i_block, 12);
149 if single_blk == 0 {
150 return Ok(None);
151 }
152 let idx = (logical_block - single_base) as usize;
153 let buf = cache.get(dev, single_blk, block_size)?;
154 let phys = ptr_at(buf, idx);
155 return Ok(if phys == 0 { None } else { Some(phys) });
156 }
157
158 let double_base = single_end;
160 let double_end = double_base + ppb * ppb;
161 if logical_block < double_end {
162 let double_blk = ptr_at(i_block, 13);
163 if double_blk == 0 {
164 return Ok(None);
165 }
166 let off = logical_block - double_base;
167 let outer_idx = (off / ppb) as usize;
168 let inner_idx = (off % ppb) as usize;
169
170 let outer_buf = cache.get(dev, double_blk, block_size)?;
171 let inner_blk = ptr_at(outer_buf, outer_idx);
172 if inner_blk == 0 {
173 return Ok(None);
174 }
175 let inner_buf = cache.get(dev, inner_blk, block_size)?;
176 let phys = ptr_at(inner_buf, inner_idx);
177 return Ok(if phys == 0 { None } else { Some(phys) });
178 }
179
180 let triple_base = double_end;
182 let triple_end = triple_base + ppb * ppb * ppb;
183 if logical_block < triple_end {
184 let triple_blk = ptr_at(i_block, 14);
185 if triple_blk == 0 {
186 return Ok(None);
187 }
188 let off = logical_block - triple_base;
189 let l1_idx = (off / (ppb * ppb)) as usize;
190 let rem = off % (ppb * ppb);
191 let l2_idx = (rem / ppb) as usize;
192 let l3_idx = (rem % ppb) as usize;
193
194 let l1_buf = cache.get(dev, triple_blk, block_size)?;
195 let l2_blk = ptr_at(l1_buf, l1_idx);
196 if l2_blk == 0 {
197 return Ok(None);
198 }
199 let l2_buf = cache.get(dev, l2_blk, block_size)?;
200 let l3_blk = ptr_at(l2_buf, l2_idx);
201 if l3_blk == 0 {
202 return Ok(None);
203 }
204 let l3_buf = cache.get(dev, l3_blk, block_size)?;
205 let phys = ptr_at(l3_buf, l3_idx);
206 return Ok(if phys == 0 { None } else { Some(phys) });
207 }
208
209 Err(Error::Corrupt(
211 "indirect: logical block exceeds triple-indirect address space",
212 ))
213}
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218 use crate::block_io::BlockDevice;
219 use std::sync::Mutex;
220
221 struct MemDev {
223 data: Mutex<Vec<u8>>,
224 }
225
226 impl MemDev {
227 fn new(size: usize) -> Self {
228 Self {
229 data: Mutex::new(vec![0u8; size]),
230 }
231 }
232
233 fn write_block(&self, block_no: u64, block_size: u32, contents: &[u8]) {
234 let mut d = self.data.lock().unwrap();
235 let off = (block_no as usize) * (block_size as usize);
236 d[off..off + contents.len()].copy_from_slice(contents);
237 }
238 }
239
240 impl BlockDevice for MemDev {
241 fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
242 let d = self.data.lock().unwrap();
243 let start = offset as usize;
244 buf.copy_from_slice(&d[start..start + buf.len()]);
245 Ok(())
246 }
247 fn size_bytes(&self) -> u64 {
248 self.data.lock().unwrap().len() as u64
249 }
250 }
251
252 fn make_iblock(ptrs: [u32; 15]) -> [u8; 60] {
254 let mut out = [0u8; 60];
255 for (i, p) in ptrs.iter().enumerate() {
256 out[i * 4..i * 4 + 4].copy_from_slice(&p.to_le_bytes());
257 }
258 out
259 }
260
261 fn pack_ptrs(ptrs: &[u32], block_size: u32) -> Vec<u8> {
263 let mut buf = vec![0u8; block_size as usize];
264 for (i, p) in ptrs.iter().enumerate() {
265 buf[i * 4..i * 4 + 4].copy_from_slice(&p.to_le_bytes());
266 }
267 buf
268 }
269
270 #[test]
271 fn direct_pointer_lookup() {
272 let bs = 1024u32;
273 let dev = MemDev::new(1024 * 64);
274 let mut cache = IndirectCache::new();
275
276 let mut ptrs = [0u32; 15];
278 ptrs[0] = 100;
279 ptrs[5] = 105;
280 let i_block = make_iblock(ptrs);
281
282 assert_eq!(
283 lookup(&i_block, &dev, bs, 0, &mut cache).unwrap(),
284 Some(100)
285 );
286 assert_eq!(
287 lookup(&i_block, &dev, bs, 5, &mut cache).unwrap(),
288 Some(105)
289 );
290 assert_eq!(lookup(&i_block, &dev, bs, 1, &mut cache).unwrap(), None);
292 assert_eq!(lookup(&i_block, &dev, bs, 11, &mut cache).unwrap(), None);
293 }
294
295 #[test]
296 fn single_indirect_lookup() {
297 let bs = 1024u32; let dev = MemDev::new(1024 * 1024);
299 let mut cache = IndirectCache::new();
300
301 let mut indirect_ptrs = vec![0u32; 256];
304 indirect_ptrs[0] = 200;
305 indirect_ptrs[3] = 203;
306 indirect_ptrs[255] = 455;
307 dev.write_block(50, bs, &pack_ptrs(&indirect_ptrs, bs));
308
309 let mut ptrs = [0u32; 15];
310 ptrs[12] = 50;
311 let i_block = make_iblock(ptrs);
312
313 assert_eq!(
315 lookup(&i_block, &dev, bs, 12, &mut cache).unwrap(),
316 Some(200)
317 );
318 assert_eq!(
320 lookup(&i_block, &dev, bs, 15, &mut cache).unwrap(),
321 Some(203)
322 );
323 assert_eq!(lookup(&i_block, &dev, bs, 13, &mut cache).unwrap(), None);
325 assert_eq!(
327 lookup(&i_block, &dev, bs, 12 + 255, &mut cache).unwrap(),
328 Some(455)
329 );
330 }
331
332 #[test]
333 fn single_indirect_zero_pointer_is_hole() {
334 let bs = 1024u32;
335 let dev = MemDev::new(1024 * 64);
336 let mut cache = IndirectCache::new();
337
338 let i_block = make_iblock([0u32; 15]);
340 assert_eq!(lookup(&i_block, &dev, bs, 12, &mut cache).unwrap(), None);
341 assert_eq!(lookup(&i_block, &dev, bs, 200, &mut cache).unwrap(), None);
342 }
343
344 #[test]
345 fn double_indirect_lookup() {
346 let bs = 1024u32; let dev = MemDev::new(1024 * 4096);
348 let mut cache = IndirectCache::new();
349
350 let mut outer = vec![0u32; 256];
354 outer[0] = 71;
355 outer[2] = 72;
356 dev.write_block(70, bs, &pack_ptrs(&outer, bs));
357
358 let mut inner1 = vec![0u32; 256];
359 inner1[5] = 555;
360 dev.write_block(71, bs, &pack_ptrs(&inner1, bs));
361
362 let mut inner2 = vec![0u32; 256];
363 inner2[10] = 1010;
364 dev.write_block(72, bs, &pack_ptrs(&inner2, bs));
365
366 let mut ptrs = [0u32; 15];
367 ptrs[13] = 70;
368 let i_block = make_iblock(ptrs);
369
370 assert_eq!(
373 lookup(&i_block, &dev, bs, 273, &mut cache).unwrap(),
374 Some(555)
375 );
376 assert_eq!(
378 lookup(&i_block, &dev, bs, 790, &mut cache).unwrap(),
379 Some(1010)
380 );
381 assert_eq!(
383 lookup(&i_block, &dev, bs, 268 + 256, &mut cache).unwrap(),
384 None
385 );
386 assert_eq!(lookup(&i_block, &dev, bs, 274, &mut cache).unwrap(), None);
388 }
389
390 #[test]
391 fn triple_indirect_lookup() {
392 let bs = 1024u32; let dev = MemDev::new(1024 * 8192);
397 let mut cache = IndirectCache::new();
398
399 let mut l1 = vec![0u32; 256];
403 l1[0] = 81;
404 dev.write_block(80, bs, &pack_ptrs(&l1, bs));
405
406 let mut l2 = vec![0u32; 256];
407 l2[0] = 82;
408 dev.write_block(81, bs, &pack_ptrs(&l2, bs));
409
410 let mut l3 = vec![0u32; 256];
411 l3[7] = 7777;
412 dev.write_block(82, bs, &pack_ptrs(&l3, bs));
413
414 let mut ptrs = [0u32; 15];
415 ptrs[14] = 80;
416 let i_block = make_iblock(ptrs);
417
418 assert_eq!(
421 lookup(&i_block, &dev, bs, 65811, &mut cache).unwrap(),
422 Some(7777)
423 );
424 assert_eq!(lookup(&i_block, &dev, bs, 65812, &mut cache).unwrap(), None);
426 }
427
428 #[test]
429 fn beyond_triple_indirect_is_corrupt() {
430 let bs = 1024u32;
431 let dev = MemDev::new(1024 * 64);
432 let mut cache = IndirectCache::new();
433
434 let i_block = make_iblock([0u32; 15]);
435 let result = lookup(&i_block, &dev, bs, 1u64 << 32, &mut cache);
438 assert!(matches!(result, Err(Error::Corrupt(_))));
439 }
440}