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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
use crate::prelude::*;
use crate::utils::*;
use super::*;
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Ext4Superblock {
pub inodes_count: u32, // Inodes count
blocks_count_lo: u32, // Blocks count
reserved_blocks_count_lo: u32, // Reserved blocks count
free_blocks_count_lo: u32, // Free blocks count
free_inodes_count: u32, // Free inodes count
pub first_data_block: u32, // First data block
log_block_size: u32, // Block size
log_cluster_size: u32, // Deprecated fragment size
blocks_per_group: u32, // Blocks per group
frags_per_group: u32, // Deprecated fragments per group
pub inodes_per_group: u32, // Inodes per group
mount_time: u32, // Mount time
write_time: u32, // Write time
mount_count: u16, // Mount count
max_mount_count: u16, // Maximum mount count
magic: u16, // Magic signature, 0xEF53
state: u16, // Filesystem state
errors: u16, // Behavior when errors detected
minor_rev_level: u16, // Minor revision level
last_check_time: u32, // Last check time
check_interval: u32, // Check interval
pub creator_os: u32, // Creator OS
pub rev_level: u32, // Revision level
def_resuid: u16, // Default reserved blocks uid
def_resgid: u16, // Default reserved blocks gid
// Fields for EXT4_DYNAMIC_REV superblocks only
first_inode: u32, // First non-reserved inode
pub inode_size: u16, // Size of inode structure
block_group_index: u16, // Block group index of this superblock
features_compatible: u32, // Compatible feature set
features_incompatible: u32, // Incompatible feature set
pub features_read_only: u32, // Read-only compatible feature set
pub uuid: [u8; 16], // 128-bit UUID for volume
volume_name: [u8; 16], // Volume name
last_mounted: [u8; 64], // Directory where last mounted
algorithm_usage_bitmap: u32, // Algorithm usage bitmap
// Performance hints. Directory preallocation only when EXT4_FEATURE_COMPAT_DIR_PREALLOC flag is on
s_prealloc_blocks: u8, // Number of blocks to try to preallocate
s_prealloc_dir_blocks: u8, // Number of blocks to preallocate for directories
s_reserved_gdt_blocks: u16, // Number of reserved GDT entries for online growth per group
// Journaling support - if EXT4_FEATURE_COMPAT_HAS_JOURNAL set
journal_uuid: [u8; 16], // UUID of journal superblock
journal_inode_number: u32, // Inode number of journal file
journal_dev: u32, // Device number of journal file
last_orphan: u32, // Head of list of inodes to delete
hash_seed: [u32; 4], // HTREE hash seed
default_hash_version: u8, // Default hash version
journal_backup_type: u8,
pub desc_size: u16, // Group descriptor size
default_mount_opts: u32, // Default mount options
first_meta_bg: u32, // First metadata block group
mkfs_time: u32, // Filesystem creation time
journal_blocks: [u32; 17], // Journal node backup
// If EXT4_FEATURE_COMPAT_64BIT set, supports 64-bit
blocks_count_hi: u32, // Blocks count
reserved_blocks_count_hi: u32, // Reserved blocks count
free_blocks_count_hi: u32, // Free blocks count
min_extra_isize: u16, // All inodes have at least # bytes
want_extra_isize: u16, // New inodes should reserve # bytes
flags: u32, // Miscellaneous flags
raid_stride: u16, // RAID stride
mmp_interval: u16, // MMP check wait seconds
mmp_block: u64, // Multi-mount protection block
raid_stripe_width: u32, // Blocks on all data disks (N * stride)
log_groups_per_flex: u8, // FLEX_BG group size
checksum_type: u8,
reserved_pad: u16,
kbytes_written: u64, // Written kilobytes
snapshot_inum: u32, // Active snapshot inode number
snapshot_id: u32, // Active snapshot sequence ID
snapshot_r_blocks_count: u64, // Reserved blocks for future use of active snapshot
snapshot_list: u32, // Head node number of snapshot list on disk
error_count: u32, // Number of filesystem errors
first_error_time: u32, // Time of first error occurrence
first_error_ino: u32, // Inode number of first error occurrence
first_error_block: u64, // Block number of first error occurrence
first_error_func: [u8; 32], // Function of first error occurrence
first_error_line: u32, // Line number of first error occurrence
last_error_time: u32, // Time of last error occurrence
last_error_ino: u32, // Inode number of last error occurrence
last_error_line: u32, // Line number of last error occurrence
last_error_block: u64, // Block number of last error occurrence
last_error_func: [u8; 32], // Function of last error occurrence
mount_opts: [u8; 64],
usr_quota_inum: u32, // Node for tracking user quota
grp_quota_inum: u32, // Node for tracking group quota
overhead_clusters: u32, // Overhead blocks/clusters in filesystem
backup_bgs: [u32; 2], // Groups with sparse_super2 superblock
encrypt_algos: [u8; 4], // Used encryption algorithms
encrypt_pw_salt: [u8; 16], // Salt for string2key algorithm
lpf_ino: u32, // Location of lost+found node
padding: [u32; 100], // Padding at end of block
checksum: u32, // crc32c(superblock)
}
impl Ext4Superblock {
/// Returns the size of inode structure.
pub fn inode_size(&self) -> u16 {
self.inode_size
}
/// Returns the size of inode structure.
pub fn inode_size_file(&self, inode: &Ext4Inode) -> u64 {
let mode = inode.mode;
let mut v = inode.size as u64;
if self.rev_level > 0 && (mode & EXT4_INODE_MODE_TYPE_MASK) == EXT4_INODE_MODE_FILE as u16 {
let hi = (inode.size_hi as u64) << 32;
v |= hi;
}
v
}
pub fn free_inodes_count(&self) -> u32 {
self.free_inodes_count
}
/// Returns total number of inodes.
pub fn total_inodes(&self) -> u32 {
self.inodes_count
}
/// Returns the number of blocks in each block group.
pub fn blocks_per_group(&self) -> u32 {
self.blocks_per_group
}
/// Returns the size of block.
pub fn block_size(&self) -> u32 {
1024 << self.log_block_size
}
/// Returns the number of inodes in each block group.
pub fn inodes_per_group(&self) -> u32 {
self.inodes_per_group
}
/// Returns the first data block.
pub fn first_data_block(&self) -> u32{
self.first_data_block
}
/// Returns the number of block groups.
pub fn block_group_count(&self) -> u32 {
let blocks_count = (self.blocks_count_hi as u64) << 32 | self.blocks_count_lo as u64;
let blocks_per_group = self.blocks_per_group as u64;
let mut block_group_count = blocks_count / blocks_per_group;
if (blocks_count % blocks_per_group) != 0 {
block_group_count += 1;
}
block_group_count as u32
}
pub fn blocks_count(&self) -> u32 {
((self.blocks_count_hi.to_le() as u64) << 32) as u32 | self.blocks_count_lo
}
pub fn desc_size(&self) -> u16 {
let size = self.desc_size;
if size < EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE {
EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE
} else {
size
}
}
pub fn extra_size(&self) -> u16 {
self.want_extra_isize
}
pub fn get_inodes_in_group_cnt(&self, bgid: u32) -> u32 {
let block_group_count = self.block_group_count();
let inodes_per_group = self.inodes_per_group;
let total_inodes = self.inodes_count;
if bgid < block_group_count - 1 {
inodes_per_group
} else {
total_inodes - ((block_group_count - 1) * inodes_per_group)
}
}
pub fn decrease_free_inodes_count(&mut self) {
self.free_inodes_count -= 1;
}
pub fn free_blocks_count(&self) -> u64 {
self.free_blocks_count_lo as u64 | ((self.free_blocks_count_hi as u64) << 32).to_le()
}
pub fn set_free_blocks_count(&mut self, free_blocks: u64) {
self.free_blocks_count_lo = (free_blocks & 0xffffffff) as u32;
self.free_blocks_count_hi = (free_blocks >> 32) as u32;
}
pub fn sync_to_disk(&self, block_device: &Arc<dyn BlockDevice>) {
let data = unsafe {
core::slice::from_raw_parts(self as *const _ as *const u8, size_of::<Ext4Superblock>())
};
block_device.write_offset(SUPERBLOCK_OFFSET, data);
}
pub fn sync_to_disk_with_csum(&mut self, block_device: &Arc<dyn BlockDevice>) {
let data = unsafe {
core::slice::from_raw_parts(self as *const _ as *const u8, size_of::<Ext4Superblock>())
};
let checksum = ext4_crc32c(EXT4_CRC32_INIT, data, 0x3fc);
self.checksum = checksum;
let data = unsafe {
core::slice::from_raw_parts(self as *const _ as *const u8, size_of::<Ext4Superblock>())
};
block_device.write_offset(SUPERBLOCK_OFFSET, data);
}
pub fn incompat_features(&self) -> u32 {
self.features_incompatible
}
pub fn reserved_gdt_blocks(&self) -> u16 {
self.s_reserved_gdt_blocks
}
}
impl Ext4Superblock {
/// Returns the checksum of the block bitmap
pub fn ext4_balloc_bitmap_csum(&self, bitmap: &[u8]) -> u32 {
let mut csum = 0;
let blocks_per_group = self.blocks_per_group;
let uuid = self.uuid;
csum = ext4_crc32c(EXT4_CRC32_INIT, &uuid, uuid.len() as u32);
csum = ext4_crc32c(csum, bitmap, blocks_per_group / 8);
csum
}
/// Returns the checksum of the inode bitmap
pub fn ext4_ialloc_bitmap_csum(&self, bitmap: &[u8]) -> u32 {
let mut csum = 0;
let inodes_per_group = self.inodes_per_group;
let uuid = self.uuid;
csum = ext4_crc32c(EXT4_CRC32_INIT, &uuid, uuid.len() as u32);
csum = ext4_crc32c(csum, bitmap, (inodes_per_group + 7) / 8);
csum
}
}