1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Defines the superblock structure.

use anyhow::Result;
use zerocopy::AsBytes;
use zerocopy_derive::FromBytes;
use zerocopy_derive::FromZeroes;

use crate::arena::Arena;
use crate::arena::BlockId;
use crate::blockgroup::BLOCK_SIZE;
use crate::builder::Builder;
use crate::inode::Inode;

/// A struct to represent the configuration of an ext2 filesystem.

/// The ext2 superblock.
///
/// The field names are based on [the specification](https://www.nongnu.org/ext2-doc/ext2.html#superblock).
/// Note that this struct only holds the fields at the beginning of the superblock. All fields after
/// the fields supported by this structure are filled with zeros.
#[repr(C)]
#[derive(Default, Debug, Copy, Clone, FromZeroes, FromBytes, AsBytes)]
pub(crate) struct SuperBlock {
    pub inodes_count: u32,
    pub blocks_count: u32,
    _r_blocks_count: u32,
    pub free_blocks_count: u32,
    pub free_inodes_count: u32,
    _first_data_block: u32,
    _log_block_size: u32,
    log_frag_size: u32,
    pub blocks_per_group: u32,
    frags_per_group: u32,
    pub inodes_per_group: u32,
    mtime: u32,
    wtime: u32,
    _mnt_count: u16,
    _max_mnt_count: u16,
    magic: u16,
    state: u16,
    errors: u16,
    _minor_rev_level: u16,
    _lastcheck: u32,
    _checkinterval: u32,
    _creator_os: u32,
    rev_level: u32,
    _def_resuid: u16,
    _def_resgid: u16,
    first_ino: u32,
    pub inode_size: u16,
    pub block_group_nr: u16,
    feature_compat: u32,
    feature_incompat: u32,
    _feature_ro_compat: u32,
    uuid: [u8; 16],
    // Add more fields if needed.
}

impl SuperBlock {
    pub fn new<'a>(arena: &'a Arena<'a>, cfg: &Builder) -> Result<&'a mut SuperBlock> {
        const EXT2_MAGIC_NUMBER: u16 = 0xEF53;
        const COMPAT_EXT_ATTR: u32 = 0x8;

        let num_groups = cfg.size / (cfg.blocks_per_group * BLOCK_SIZE as u32);
        let blocks_per_group = cfg.blocks_per_group;
        let inodes_per_group = cfg.inodes_per_group;

        let log_block_size = 2; // (1024 << log_block_size) = 4K bytes

        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)?
            .as_secs() as u32;

        let uuid = uuid::Uuid::new_v4().into_bytes();
        let inodes_count = inodes_per_group * num_groups;
        let blocks_count = blocks_per_group * num_groups;

        // Reserve 10 inodes. Usually inode 11 is used for the lost+found directory.
        // <https://docs.kernel.org/filesystems/ext4/special_inodes.html>.
        let first_ino = 11;

        // Superblock is located at 1024 bytes in the first block.
        let sb = arena.allocate::<SuperBlock>(BlockId::from(0), 1024)?;
        *sb = Self {
            inodes_count,
            blocks_count,
            free_blocks_count: 0, //blocks_count, // All blocks are free
            free_inodes_count: inodes_count, // All inodes are free
            _log_block_size: log_block_size,
            log_frag_size: log_block_size,
            blocks_per_group,
            frags_per_group: blocks_per_group,
            inodes_per_group,
            mtime: now,
            wtime: now,
            magic: EXT2_MAGIC_NUMBER,
            state: 1,     // clean
            errors: 1,    // continue on errors
            rev_level: 1, // Rev 1 for variable inode sizes
            first_ino,
            inode_size: Inode::INODE_RECORD_SIZE as u16,
            block_group_nr: 1, // super block is in block group 1
            feature_compat: COMPAT_EXT_ATTR,
            feature_incompat: 0x2, // Directory entries contain a type field
            uuid,
            ..Default::default()
        };

        Ok(sb)
    }

    #[inline]
    pub fn num_groups(&self) -> u16 {
        (self.inodes_count / self.inodes_per_group) as u16
    }
}