ext2/
superblock.rs

1// Copyright 2024 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Defines the superblock structure.
6
7use anyhow::Result;
8use zerocopy::FromBytes;
9use zerocopy::Immutable;
10use zerocopy::IntoBytes;
11use zerocopy::KnownLayout;
12
13use crate::arena::Arena;
14use crate::arena::BlockId;
15use crate::blockgroup::BLOCK_SIZE;
16use crate::builder::Builder;
17use crate::inode::Inode;
18
19/// The ext2 superblock.
20///
21/// The field names are based on [the specification](https://www.nongnu.org/ext2-doc/ext2.html#superblock).
22/// Note that this struct only holds the fields at the beginning of the superblock. All fields after
23/// the fields supported by this structure are filled with zeros.
24#[repr(C)]
25#[derive(Default, Debug, Copy, Clone, FromBytes, Immutable, IntoBytes, KnownLayout)]
26pub(crate) struct SuperBlock {
27    pub inodes_count: u32,
28    pub blocks_count: u32,
29    _r_blocks_count: u32,
30    pub free_blocks_count: u32,
31    pub free_inodes_count: u32,
32    _first_data_block: u32,
33    _log_block_size: u32,
34    log_frag_size: u32,
35    pub blocks_per_group: u32,
36    frags_per_group: u32,
37    pub inodes_per_group: u32,
38    mtime: u32,
39    wtime: u32,
40    _mnt_count: u16,
41    _max_mnt_count: u16,
42    magic: u16,
43    state: u16,
44    errors: u16,
45    _minor_rev_level: u16,
46    _lastcheck: u32,
47    _checkinterval: u32,
48    _creator_os: u32,
49    rev_level: u32,
50    _def_resuid: u16,
51    _def_resgid: u16,
52    first_ino: u32,
53    pub inode_size: u16,
54    pub block_group_nr: u16,
55    feature_compat: u32,
56    feature_incompat: u32,
57    _feature_ro_compat: u32,
58    uuid: [u8; 16],
59    // Add more fields if needed.
60}
61
62impl SuperBlock {
63    pub fn new<'a>(arena: &'a Arena<'a>, cfg: &Builder) -> Result<&'a mut SuperBlock> {
64        const EXT2_MAGIC_NUMBER: u16 = 0xEF53;
65        const COMPAT_EXT_ATTR: u32 = 0x8;
66
67        let num_groups = cfg.size / (cfg.blocks_per_group * BLOCK_SIZE as u32);
68        let blocks_per_group = cfg.blocks_per_group;
69        let inodes_per_group = cfg.inodes_per_group;
70
71        let log_block_size = 2; // (1024 << log_block_size) = 4K bytes
72
73        let now = std::time::SystemTime::now()
74            .duration_since(std::time::UNIX_EPOCH)?
75            .as_secs() as u32;
76
77        let uuid = uuid::Uuid::new_v4().into_bytes();
78        let inodes_count = inodes_per_group * num_groups;
79        let blocks_count = blocks_per_group * num_groups;
80
81        // Reserve 10 inodes. Usually inode 11 is used for the lost+found directory.
82        // <https://docs.kernel.org/filesystems/ext4/special_inodes.html>.
83        let first_ino = 11;
84
85        // Superblock is located at 1024 bytes in the first block.
86        let sb = arena.allocate::<SuperBlock>(BlockId::from(0), 1024)?;
87        *sb = Self {
88            inodes_count,
89            blocks_count,
90            free_blocks_count: 0, //blocks_count, // All blocks are free
91            free_inodes_count: inodes_count, // All inodes are free
92            _log_block_size: log_block_size,
93            log_frag_size: log_block_size,
94            blocks_per_group,
95            frags_per_group: blocks_per_group,
96            inodes_per_group,
97            mtime: now,
98            wtime: now,
99            magic: EXT2_MAGIC_NUMBER,
100            state: 1,     // clean
101            errors: 1,    // continue on errors
102            rev_level: 1, // Rev 1 for variable inode sizes
103            first_ino,
104            inode_size: Inode::INODE_RECORD_SIZE as u16,
105            block_group_nr: 1, // super block is in block group 1
106            feature_compat: COMPAT_EXT_ATTR,
107            feature_incompat: 0x2, // Directory entries contain a type field
108            uuid,
109            ..Default::default()
110        };
111
112        Ok(sb)
113    }
114
115    #[inline]
116    pub fn num_groups(&self) -> u16 {
117        (self.inodes_count / self.inodes_per_group) as u16
118    }
119}