1use crate::crypto;
5use crate::dir::InlineDentry;
6use crate::reader::{NEW_ADDR, NULL_ADDR, Reader};
7use crate::superblock::BLOCK_SIZE;
8use crate::xattr::{XattrEntry, decode_xattr};
9use anyhow::{Error, anyhow, ensure};
10use bitflags::bitflags;
11use std::collections::HashMap;
12use std::fmt::Debug;
13use storage_device::buffer::Buffer;
14use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Ref, Unaligned};
15
16const NAME_MAX: usize = 255;
17const INODE_BLOCK_MAX_ADDR: usize = 923;
19const ADDR_BLOCK_NUM_ADDR: u32 = 1018;
21
22#[repr(C, packed)]
25#[derive(Copy, Clone, Debug, Immutable, KnownLayout, FromBytes, IntoBytes, Unaligned)]
26pub struct Extent {
27 file_offset: u32,
28 block_address: u32,
29 len: u32,
30}
31
32#[derive(Copy, Clone, Debug, Immutable, FromBytes, IntoBytes)]
33pub struct Mode(u16);
34bitflags! {
35 impl Mode: u16 {
36 const RegularFile = 0o100000;
37 const Directory = 0o040000;
38 }
39}
40
41#[derive(Copy, Clone, Debug, Immutable, FromBytes, IntoBytes)]
42pub struct AdviseFlags(u8);
43bitflags! {
44 impl AdviseFlags: u8 {
45 const Encrypted = 0x04;
46 const EncryptedName = 0x08;
47 const Verity = 0x40;
48 }
49}
50
51#[derive(Copy, Clone, Debug, Immutable, FromBytes, IntoBytes)]
52pub struct InlineFlags(u8);
53bitflags! {
54 impl InlineFlags: u8 {
55 const Xattr = 0b00000001;
56 const Data = 0b00000010;
57 const Dentry = 0b00000100;
58 const ExtraAttr = 0b00100000;
59 }
60}
61
62#[derive(Copy, Clone, Debug, Immutable, FromBytes, IntoBytes)]
63pub struct Flags(u32);
64bitflags! {
65 impl Flags: u32 {
66 const Casefold = 0x40000000;
67 }
68}
69
70#[repr(C, packed)]
71#[derive(Copy, Clone, Debug, Immutable, KnownLayout, FromBytes, IntoBytes, Unaligned)]
72pub struct InodeHeader {
73 pub mode: Mode,
74 pub advise_flags: AdviseFlags,
75 pub inline_flags: InlineFlags,
76 pub uid: u32,
77 pub gid: u32,
78 pub links: u32,
79 pub size: u64,
80 pub block_size: u64,
81 pub atime: i64,
82 pub ctime: i64,
83 pub mtime: i64,
84 pub atime_nanos: u32,
85 pub ctime_nanos: u32,
86 pub mtime_nanos: u32,
87 pub generation: u32,
88 pub dir_depth: u32,
89 pub xattr_nid: u32,
90 pub flags: Flags,
91 pub parent_inode: u32,
92 pub name_len: u32,
93 pub name: [u8; NAME_MAX],
94 pub dir_level: u8,
95
96 ext: Extent, }
98
99#[repr(C, packed)]
101#[derive(Copy, Clone, Debug, Immutable, KnownLayout, FromBytes, IntoBytes, Unaligned)]
102pub struct InodeExtraAttr {
103 pub extra_size: u16,
104 pub inline_xattr_size: u16,
105 pub project_id: u32,
106 pub inode_checksum: u32,
107 pub creation_time: u64,
108 pub creation_time_nanos: u32,
109 pub compressed_blocks: u64,
110 pub compression_algorithm: u8,
111 pub log_cluster_size: u8,
112 pub compression_flags: u16,
113}
114
115#[repr(C, packed)]
116#[derive(Copy, Clone, Debug, Immutable, KnownLayout, FromBytes, IntoBytes, Unaligned)]
117pub struct InodeFooter {
118 pub nid: u32,
119 pub ino: u32,
120 pub flag: u32,
121 pub cp_ver: u64,
122 pub next_blkaddr: u32,
123}
124
125pub struct Inode {
170 pub header: InodeHeader,
171 pub extra: Option<InodeExtraAttr>,
172 pub inline_data: Option<Box<[u8]>>,
173 pub(super) inline_dentry: Option<InlineDentry>,
174 pub(super) i_addrs: Vec<u32>,
175 nids: [u32; 5],
176 pub footer: InodeFooter,
177
178 nid_pages: HashMap<u32, Box<RawAddrBlock>>,
180 pub xattr: Vec<XattrEntry>,
181
182 pub context: Option<fscrypt::Context>,
184
185 pub block_addrs: Vec<u32>,
188}
189
190#[repr(C, packed)]
194#[derive(Copy, Clone, Debug, Immutable, KnownLayout, FromBytes, IntoBytes, Unaligned)]
195pub struct RawAddrBlock {
196 pub addrs: [u32; ADDR_BLOCK_NUM_ADDR as usize],
197 _reserved:
198 [u8; BLOCK_SIZE - std::mem::size_of::<InodeFooter>() - 4 * ADDR_BLOCK_NUM_ADDR as usize],
199 pub footer: InodeFooter,
200}
201
202impl TryFrom<Buffer<'_>> for Box<RawAddrBlock> {
203 type Error = Error;
204 fn try_from(block: Buffer<'_>) -> Result<Self, Self::Error> {
205 let raw = block
206 .as_ptr_slice()
207 .read::<RawAddrBlock>()
208 .ok_or_else(|| anyhow!("Block size too small"))?;
209 Ok(Box::new(raw))
210 }
211}
212
213impl Debug for Inode {
214 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
215 let mut out = f.debug_struct("Inode");
216 out.field("header", &self.header);
217 if let Some(extra) = &self.extra {
218 out.field("extra", &extra);
219 }
220 if let Some(inline_dentry) = &self.inline_dentry {
221 out.field("inline_dentry", &inline_dentry);
222 }
223 out.field("i_addrs", &self.i_addrs).field("footer", &self.footer);
224 out.field("xattr", &self.xattr);
225 out.finish()
226 }
227}
228
229impl Inode {
230 pub(super) async fn try_load(f2fs: &impl Reader, ino: u32) -> Result<Box<Inode>, Error> {
232 let mut block_addrs = vec![];
233 let mut raw_xattr = vec![];
234 let mut this = {
235 let block = f2fs.read_node(ino).await?;
236 block_addrs.push(f2fs.get_nat_entry(ino).await?.block_addr);
237 let data = block.to_vec();
238 let (header, rest): (Ref<_, InodeHeader>, _) = Ref::from_prefix(&data[..]).unwrap();
246 let (rest, footer): (_, Ref<_, InodeFooter>) = Ref::from_suffix(rest).unwrap();
247 ensure!(footer.ino == ino, "Footer ino doesn't match.");
248
249 let mut nids = [0u32; 5];
254 nids.as_mut_bytes()
255 .copy_from_slice(&rest[INODE_BLOCK_MAX_ADDR * 4..(INODE_BLOCK_MAX_ADDR + 5) * 4]);
256 let rest = &rest[..INODE_BLOCK_MAX_ADDR * 4];
257
258 let (extra, rest) = if header.inline_flags.contains(InlineFlags::ExtraAttr) {
259 let (extra, _): (Ref<_, InodeExtraAttr>, _) = Ref::from_prefix(rest).unwrap();
260 let extra_size = extra.extra_size as usize;
261 ensure!(extra_size <= rest.len(), "Bad extra_size in inode");
262 (Some((*extra).clone()), &rest[extra_size..])
263 } else {
264 (None, rest)
265 };
266 let rest = if header.inline_flags.contains(InlineFlags::Xattr) {
267 ensure!(
269 rest.len() >= 200,
270 "Insufficient space for inline xattr. Likely bad extra_size."
271 );
272 raw_xattr.extend_from_slice(&rest[rest.len() - 200..]);
273 &rest[..rest.len() - 200]
274 } else {
275 rest
276 };
277
278 let mut inline_data = None;
279 let mut inline_dentry = None;
280 let mut i_addrs: Vec<u32> = Vec::new();
281
282 if header.inline_flags.contains(InlineFlags::Data) {
283 ensure!(rest.len() >= 4, "Invalid inline data (insufficient remaining space)");
285 let data = &rest[4..];
286 ensure!(header.size <= data.len() as u64, "Invalid or corrupt inode.");
287 inline_data = Some(data[..header.size as usize].to_vec().into_boxed_slice());
288 } else if header.inline_flags.contains(InlineFlags::Dentry) {
289 inline_dentry = Some(InlineDentry::try_from_bytes(rest)?);
291 } else {
292 i_addrs.resize(rest.len() / 4, 0);
294 i_addrs.as_mut_bytes().copy_from_slice(&rest[..rest.len() / 4 * 4]);
295 };
296
297 Box::new(Self {
298 header: (*header).clone(),
299 extra,
300 inline_data: inline_data.map(|x| x.into()),
301 inline_dentry,
302 i_addrs,
303 nids,
304 footer: (*footer).clone(),
305
306 nid_pages: HashMap::new(),
307 xattr: vec![],
308 context: None,
309
310 block_addrs,
311 })
312 };
313
314 if this.header.xattr_nid != 0 {
317 let node_block = f2fs.read_node(this.header.xattr_nid).await?;
318 raw_xattr.extend_from_slice(&node_block.to_vec());
319 this.block_addrs.push(f2fs.get_nat_entry(this.header.xattr_nid).await?.block_addr);
320 }
321 this.xattr = decode_xattr(&raw_xattr)?;
322
323 this.context = crypto::try_read_context_from_xattr(&this.xattr)?;
324
325 for (i, nid) in this.nids.into_iter().enumerate() {
328 if nid == NULL_ADDR {
329 continue;
330 }
331 match i {
332 0..2 => {
333 this.nid_pages.insert(nid, f2fs.read_node(nid).await?.try_into()?);
334 this.block_addrs.push(f2fs.get_nat_entry(nid).await?.block_addr);
335 }
336 2..4 => {
337 let indirect = Box::<RawAddrBlock>::try_from(f2fs.read_node(nid).await?)?;
338 this.block_addrs.push(f2fs.get_nat_entry(nid).await?.block_addr);
339 for nid in indirect.addrs {
340 if nid != NULL_ADDR {
341 this.nid_pages.insert(nid, f2fs.read_node(nid).await?.try_into()?);
342 this.block_addrs.push(f2fs.get_nat_entry(nid).await?.block_addr);
343 }
344 }
345 this.nid_pages.insert(nid, indirect);
346 }
347 4 => {
348 let double_indirect =
349 Box::<RawAddrBlock>::try_from(f2fs.read_node(nid).await?)?;
350 this.block_addrs.push(f2fs.get_nat_entry(nid).await?.block_addr);
351 for nid in double_indirect.addrs {
352 if nid != NULL_ADDR {
353 let indirect =
354 Box::<RawAddrBlock>::try_from(f2fs.read_node(nid).await?)?;
355 this.block_addrs.push(f2fs.get_nat_entry(nid).await?.block_addr);
356 for nid in indirect.addrs {
357 if nid != NULL_ADDR {
358 this.nid_pages
359 .insert(nid, f2fs.read_node(nid).await?.try_into()?);
360 this.block_addrs
361 .push(f2fs.get_nat_entry(nid).await?.block_addr);
362 }
363 }
364 this.nid_pages.insert(nid, indirect);
365 }
366 }
367 this.nid_pages.insert(nid, double_indirect);
368 }
369 _ => unreachable!(),
370 }
371 }
372
373 Ok(this)
374 }
375
376 pub fn data_blocks(&self) -> DataBlocksIter<'_> {
379 DataBlocksIter {
380 iter: BlockIter { inode: self, stage: 0, offset: 0, a: 0, b: 0, c: 0 },
381 next_block: None,
382 }
383 }
384
385 pub fn data_block_addr(&self, mut block_num: u32) -> u32 {
388 let offset = block_num;
389
390 if block_num < self.i_addrs.len() as u32 {
391 return self.i_addrs[block_num as usize];
392 }
393 block_num -= self.i_addrs.len() as u32;
394
395 const NID0_END: u32 = ADDR_BLOCK_NUM_ADDR;
397 const NID1_END: u32 = NID0_END + ADDR_BLOCK_NUM_ADDR;
398 const NID2_END: u32 = NID1_END + ADDR_BLOCK_NUM_ADDR * ADDR_BLOCK_NUM_ADDR;
399 const NID3_END: u32 = NID2_END + ADDR_BLOCK_NUM_ADDR * ADDR_BLOCK_NUM_ADDR;
400
401 let mut iter = match block_num {
402 ..NID0_END => {
403 let a = block_num;
404 BlockIter { inode: self, stage: 1, offset, a, b: 0, c: 0 }
405 }
406 ..NID1_END => {
407 let a = block_num - NID0_END;
408 BlockIter { inode: self, stage: 2, offset, a, b: 0, c: 0 }
409 }
410 ..NID2_END => {
411 block_num -= NID1_END;
412 let a = block_num / ADDR_BLOCK_NUM_ADDR;
413 let b = block_num % ADDR_BLOCK_NUM_ADDR;
414 BlockIter { inode: self, stage: 3, offset, a, b, c: 0 }
415 }
416 ..NID3_END => {
417 block_num -= NID2_END;
418 let a = block_num / ADDR_BLOCK_NUM_ADDR;
419 let b = block_num % ADDR_BLOCK_NUM_ADDR;
420 BlockIter { inode: self, stage: 4, offset, a, b, c: 0 }
421 }
422 _ => {
423 block_num -= NID3_END;
424 let a = block_num / ADDR_BLOCK_NUM_ADDR / ADDR_BLOCK_NUM_ADDR;
425 let b = (block_num / ADDR_BLOCK_NUM_ADDR) % ADDR_BLOCK_NUM_ADDR;
426 let c = block_num % ADDR_BLOCK_NUM_ADDR;
427 BlockIter { inode: self, stage: 5, offset, a, b, c }
428 }
429 };
430 if let Some((logical, physical)) = iter.next() {
431 if logical == offset { physical } else { NULL_ADDR }
432 } else {
433 NULL_ADDR
434 }
435 }
436}
437
438#[derive(Copy, Clone, Debug, PartialEq, Eq)]
439pub struct DataBlockExtent {
440 pub logical_block_num: u32,
442 pub physical_block_num: u32,
444 pub length: u32,
446}
447
448pub struct DataBlocksIter<'a> {
450 iter: BlockIter<'a>,
451 next_block: Option<(u32, u32)>,
452}
453
454impl Iterator for DataBlocksIter<'_> {
455 type Item = DataBlockExtent;
456 fn next(&mut self) -> Option<Self::Item> {
457 let (log_start, phys_start) = self.next_block.take().or_else(|| self.iter.next())?;
458 let mut len = 1;
459
460 if self.iter.inode.header.size > BLOCK_SIZE as u64 * u32::MAX as u64 {
463 return None;
464 }
465 let file_end = (self.iter.inode.header.size.next_multiple_of(BLOCK_SIZE as u64)
466 / BLOCK_SIZE as u64) as u32;
467
468 loop {
469 match self.iter.next() {
470 Some((log, phys))
471 if Some(log) == log_start.checked_add(len)
472 && Some(phys) == phys_start.checked_add(len)
473 && log != file_end =>
474 {
475 len += 1;
476 }
477 other => {
478 self.next_block = other;
479 return Some(DataBlockExtent {
480 logical_block_num: log_start,
481 physical_block_num: phys_start,
482 length: len,
483 });
484 }
485 }
486 }
487 }
488}
489
490struct BlockIter<'a> {
491 inode: &'a Inode,
492 stage: u32, offset: u32,
494 a: u32, b: u32, c: u32, }
498
499impl Iterator for BlockIter<'_> {
500 type Item = (u32, u32);
501 fn next(&mut self) -> Option<Self::Item> {
502 loop {
503 match self.stage {
504 0 => {
505 while let Some(&addr) = self.inode.i_addrs.get(self.a as usize) {
507 self.a += 1;
508 self.offset += 1;
509 if addr != NULL_ADDR && addr != NEW_ADDR {
510 return Some((self.offset - 1, addr));
511 }
512 }
513 self.stage += 1;
514 self.a = 0;
515 }
516 1..3 => {
517 let nid = self.inode.nids[self.stage as usize - 1];
519
520 if nid == NULL_ADDR || nid == NEW_ADDR {
521 self.stage += 1;
522 self.offset += ADDR_BLOCK_NUM_ADDR;
523 } else {
524 let addrs = self.inode.nid_pages.get(&nid).unwrap().addrs;
525 while let Some(&addr) = addrs.get(self.a as usize) {
526 self.a += 1;
527 self.offset += 1;
528 if addr != NULL_ADDR && addr != NEW_ADDR {
529 return Some((self.offset - 1, addr));
530 }
531 }
532 self.stage += 1;
533 self.a = 0;
534 }
535 }
536
537 3..5 => {
538 let nid = self.inode.nids[self.stage as usize - 1];
539 if nid == NULL_ADDR || nid == NEW_ADDR {
541 self.stage += 1;
542 self.offset += ADDR_BLOCK_NUM_ADDR * ADDR_BLOCK_NUM_ADDR;
543 } else {
544 let addrs = self.inode.nid_pages.get(&nid).unwrap().addrs;
545 while let Some(&nid) = addrs.get(self.a as usize) {
546 if nid == NULL_ADDR || nid == NEW_ADDR {
547 self.a += 1;
548 self.offset += ADDR_BLOCK_NUM_ADDR;
549 } else {
550 let addrs = self.inode.nid_pages.get(&nid).unwrap().addrs;
551 while let Some(&addr) = addrs.get(self.b as usize) {
552 self.b += 1;
553 self.offset += 1;
554 if addr != NULL_ADDR && addr != NEW_ADDR {
555 return Some((self.offset - 1, addr));
556 }
557 }
558 self.a += 1;
559 self.b = 0;
560 }
561 }
562 self.stage += 1;
563 self.a = 0;
564 }
565 }
566
567 5 => {
568 let nid = self.inode.nids[4];
569 if nid != NULL_ADDR && nid != NEW_ADDR {
571 let addrs = self.inode.nid_pages.get(&nid).unwrap().addrs;
572 while let Some(&nid) = addrs.get(self.a as usize) {
573 if nid == NULL_ADDR || nid == NEW_ADDR {
574 self.a += 1;
575 self.offset += ADDR_BLOCK_NUM_ADDR * ADDR_BLOCK_NUM_ADDR;
576 } else {
577 let addrs = self.inode.nid_pages.get(&nid).unwrap().addrs;
578 while let Some(&nid) = addrs.get(self.b as usize) {
579 if nid == NULL_ADDR || nid == NEW_ADDR {
580 self.b += 1;
581 self.offset += ADDR_BLOCK_NUM_ADDR;
582 } else {
583 let addrs = self.inode.nid_pages.get(&nid).unwrap().addrs;
584 while let Some(&addr) = addrs.get(self.c as usize) {
585 self.c += 1;
586 self.offset += 1;
587 if addr != NULL_ADDR && addr != NEW_ADDR {
588 return Some((self.offset - 1, addr));
589 }
590 }
591 self.b += 1;
592 self.c = 0;
593 }
594 }
595
596 self.a += 1;
597 self.b = 0;
598 }
599 }
600 }
601 self.stage += 1;
602 }
603 _ => {
604 break;
605 }
606 }
607 }
608 None
609 }
610}
611
612#[cfg(test)]
613mod test {
614 use super::*;
615 use crate::nat::RawNatEntry;
616 use crate::reader;
617 use anyhow;
618 use async_trait::async_trait;
619 use storage_device::buffer_allocator::{BufferAllocator, BufferSource};
620 use zerocopy::FromZeros;
621
622 struct FakeReader {
624 data: HashMap<u32, Box<[u8; 4096]>>,
625 nids: HashMap<u32, Box<[u8; 4096]>>,
626 allocator: BufferAllocator,
627 }
628
629 #[async_trait]
630 impl reader::Reader for FakeReader {
631 async fn read_raw_block(&self, block_addr: u32) -> Result<Buffer<'_>, Error> {
632 match self.data.get(&block_addr) {
633 None => Err(anyhow!("unexpected block {block_addr}")),
634 Some(value) => {
635 let mut block = self.allocator.allocate_buffer(BLOCK_SIZE).await;
636 block.copy_from_slice(value.as_ref());
637 Ok(block)
638 }
639 }
640 }
641
642 async fn read_node(&self, nid: u32) -> Result<Buffer<'_>, Error> {
643 match self.nids.get(&nid) {
644 None => Err(anyhow!("unexpected nid {nid}")),
645 Some(value) => {
646 let mut block = self.allocator.allocate_buffer(BLOCK_SIZE).await;
647 block.copy_from_slice(value.as_ref());
648 Ok(block)
649 }
650 }
651 }
652
653 fn fs_uuid(&self) -> &[u8; 16] {
654 &[0; 16]
655 }
656
657 async fn get_nat_entry(&self, nid: u32) -> Result<RawNatEntry, Error> {
658 Ok(RawNatEntry { ino: nid, block_addr: 0, ..Default::default() })
659 }
660 }
661
662 fn build_inode(ino: u32) -> Box<[u8; BLOCK_SIZE]> {
664 let mut header = InodeHeader::new_zeroed();
665 let mut footer = InodeFooter::new_zeroed();
666 let mut extra = InodeExtraAttr::new_zeroed();
667
668 extra.extra_size = std::mem::size_of::<InodeExtraAttr>().try_into().unwrap();
669
670 header.mode = Mode::RegularFile;
671 header.inline_flags.set(InlineFlags::ExtraAttr, true);
672 header.inline_flags.set(InlineFlags::Xattr, true);
673 footer.ino = ino;
674
675 let mut out = [0u8; BLOCK_SIZE];
676 out[..std::mem::size_of::<InodeHeader>()].copy_from_slice(&header.as_bytes());
677 out[std::mem::size_of::<InodeHeader>()
678 ..std::mem::size_of::<InodeHeader>() + std::mem::size_of::<InodeExtraAttr>()]
679 .copy_from_slice(&extra.as_bytes());
680 out[BLOCK_SIZE - std::mem::size_of::<InodeFooter>()..].copy_from_slice(&footer.as_bytes());
681 Box::new(out)
682 }
683
684 #[fuchsia::test]
685 async fn test_xattr_bounds() {
686 let mut reader = FakeReader {
687 data: [].into(),
688 nids: [(1, build_inode(1)), (2, [0u8; 4096].into()), (3, [0u8; 4096].into())].into(),
689 allocator: BufferAllocator::new(BLOCK_SIZE, BufferSource::new(BLOCK_SIZE * 10)),
690 };
691 assert!(Inode::try_load(&reader, 1).await.is_ok());
692
693 let header_len = std::mem::size_of::<InodeHeader>();
694 let footer_len = std::mem::size_of::<InodeFooter>();
695 let nids_len = std::mem::size_of::<u32>() * 5;
696 let overheads = header_len + footer_len + nids_len;
697
698 let mut extra = InodeExtraAttr::new_zeroed();
700 extra.extra_size = (BLOCK_SIZE - overheads - 200) as u16;
701 reader.nids.get_mut(&1).unwrap()[std::mem::size_of::<InodeHeader>()
702 ..std::mem::size_of::<InodeHeader>() + std::mem::size_of::<InodeExtraAttr>()]
703 .copy_from_slice(&extra.as_bytes());
704 assert!(Inode::try_load(&reader, 1).await.is_ok());
705
706 let mut extra = InodeExtraAttr::new_zeroed();
708 extra.extra_size = (BLOCK_SIZE - overheads - 199) as u16;
709 reader.nids.get_mut(&1).unwrap()[std::mem::size_of::<InodeHeader>()
710 ..std::mem::size_of::<InodeHeader>() + std::mem::size_of::<InodeExtraAttr>()]
711 .copy_from_slice(&extra.as_bytes());
712 assert!(Inode::try_load(&reader, 1).await.is_err());
713 }
714}
715
716#[cfg(test)]
717mod tests {
718 use zerocopy::FromZeros;
719
720 use super::*;
721
722 fn last_addr_block(addr: u32) -> Box<RawAddrBlock> {
723 let mut addr_block = RawAddrBlock::new_zeroed();
724 addr_block.addrs[ADDR_BLOCK_NUM_ADDR as usize - 1] = addr;
725 Box::new(addr_block)
726 }
727
728 #[test]
729 fn test_data_iter() {
730 let header = InodeHeader::new_zeroed();
737 let footer = InodeFooter::new_zeroed();
738 let mut nids = [0u32; 5];
739 let mut nid_pages = HashMap::new();
740 nid_pages.insert(101, last_addr_block(1001));
741 nid_pages.insert(102, last_addr_block(1002));
742
743 let mut i_addrs: Vec<u32> = Vec::new();
744 i_addrs.resize(INODE_BLOCK_MAX_ADDR, 0);
745 i_addrs[0] = 100;
746 i_addrs[1] = 101;
747 i_addrs[2] = 102;
748 i_addrs[INODE_BLOCK_MAX_ADDR - 1] = 1000;
749
750 nids[0] = 101;
751 nid_pages.insert(101, last_addr_block(1001));
752
753 nids[1] = 102;
754 nid_pages.insert(102, last_addr_block(1002));
755
756 nids[2] = 103;
757 nid_pages.insert(103, last_addr_block(104));
758 nid_pages.insert(104, last_addr_block(1003));
759
760 nids[3] = 105;
761 nid_pages.insert(105, last_addr_block(106));
762 nid_pages.insert(106, last_addr_block(1004));
763
764 nids[4] = 107;
765 nid_pages.insert(107, last_addr_block(108));
766 nid_pages.insert(108, last_addr_block(109));
767 nid_pages.insert(109, last_addr_block(1005));
768
769 let inode = Box::new(Inode {
770 header,
771 extra: None,
772 inline_data: None,
773 inline_dentry: None,
774 i_addrs,
775 nids,
776 footer: footer,
777
778 nid_pages,
779 xattr: vec![],
780 context: None,
781
782 block_addrs: vec![],
783 });
784
785 assert_eq!(inode.data_block_addr(0), 100);
787
788 let mut iter = inode.data_blocks();
789 assert_eq!(
790 iter.next(),
791 Some(DataBlockExtent { logical_block_num: 0, physical_block_num: 100, length: 3 })
792 );
793
794 let mut block_num = 922;
795 assert_eq!(
796 iter.next(),
797 Some(DataBlockExtent {
798 logical_block_num: block_num,
799 physical_block_num: 1000,
800 length: 1
801 })
802 ); assert_eq!(inode.data_block_addr(block_num), 1000);
804 block_num += ADDR_BLOCK_NUM_ADDR;
805 assert_eq!(
806 iter.next(),
807 Some(DataBlockExtent {
808 logical_block_num: block_num,
809 physical_block_num: 1001,
810 length: 1
811 })
812 ); assert_eq!(inode.data_block_addr(block_num), 1001);
814 block_num += ADDR_BLOCK_NUM_ADDR;
815 assert_eq!(
816 iter.next(),
817 Some(DataBlockExtent {
818 logical_block_num: block_num,
819 physical_block_num: 1002,
820 length: 1
821 })
822 ); assert_eq!(inode.data_block_addr(block_num), 1002);
824 block_num += ADDR_BLOCK_NUM_ADDR * ADDR_BLOCK_NUM_ADDR;
825 assert_eq!(
826 iter.next(),
827 Some(DataBlockExtent {
828 logical_block_num: block_num,
829 physical_block_num: 1003,
830 length: 1
831 })
832 ); assert_eq!(inode.data_block_addr(block_num), 1003);
834 block_num += ADDR_BLOCK_NUM_ADDR * ADDR_BLOCK_NUM_ADDR;
835 assert_eq!(
836 iter.next(),
837 Some(DataBlockExtent {
838 logical_block_num: block_num,
839 physical_block_num: 1004,
840 length: 1
841 })
842 ); assert_eq!(inode.data_block_addr(block_num), 1004);
844 block_num += ADDR_BLOCK_NUM_ADDR * ADDR_BLOCK_NUM_ADDR * ADDR_BLOCK_NUM_ADDR;
845 assert_eq!(
846 iter.next(),
847 Some(DataBlockExtent {
848 logical_block_num: block_num,
849 physical_block_num: 1005,
850 length: 1
851 })
852 ); assert_eq!(inode.data_block_addr(block_num), 1005);
854 assert_eq!(iter.next(), None);
855 assert_eq!(inode.data_block_addr(block_num - 1), 0);
856 assert_eq!(inode.data_block_addr(block_num + 1), 0);
857 }
858}