Skip to content

Commit 2058604

Browse files
committed
superblock: Convert to zerocopy/endian-aware
Vastly simplify processing of superblock structs by using the zerocopy derive traits and eliminate a whole bunch of hacks/unsafe. Also nice: Endian aware now. For more niceness, add `test-log` to allow use of the logging macros in test suites. Signed-off-by: Ikey Doherty <[email protected]>
1 parent b42ee37 commit 2058604

File tree

9 files changed

+626
-225
lines changed

9 files changed

+626
-225
lines changed

Cargo.lock

Lines changed: 409 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ members = [
1111

1212
[workspace.dependencies]
1313
log = "0.4.21"
14+
test-log = "0.2.17"
1415
thiserror = "2.0.3"
1516
uuid = { version = "1.8.0", features = ["v8"] }
1617
zstd = "0.13.1"
18+
zerocopy = "0.8.0"

crates/superblock/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ edition = "2021"
99
uuid = { workspace = true, features = ["v8"] }
1010
thiserror.workspace = true
1111
log.workspace = true
12+
zerocopy = { workspace = true, features = ["derive", "std"] }
1213

1314
[dev-dependencies]
14-
zstd.workspace = true
15+
test-log.workspace = true
16+
zstd.workspace = true

crates/superblock/src/btrfs.rs

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,45 +8,42 @@
88
99
use crate::{Error, Kind, Superblock};
1010
use log;
11-
use std::{
12-
io::{self, Read},
13-
slice,
14-
};
11+
use std::
12+
io::{self, Read}
13+
;
1514
use uuid::Uuid;
15+
use zerocopy::*;
1616

1717
/// BTRFS superblock definition (as seen in the kernel)
1818
/// This is a PARTIAL representation that matches only the
1919
/// first 72 bytes, verifies the magic, and permits extraction
2020
/// of the UUID
21-
#[derive(Debug)]
21+
#[derive(FromBytes, Debug)]
2222
#[repr(C)]
2323
pub struct Btrfs {
2424
csum: [u8; 32],
2525
fsid: [u8; 16],
26-
bytenr: u64,
27-
flags: u64,
28-
magic: u64,
29-
generation: u64,
30-
root: u64,
31-
chunk_root: u64,
32-
log_root: u64,
26+
bytenr: U64<LittleEndian>,
27+
flags: U64<LittleEndian>,
28+
magic: U64<LittleEndian>,
29+
generation: U64<LittleEndian>,
30+
root: U64<LittleEndian>,
31+
chunk_root: U64<LittleEndian>,
32+
log_root: U64<LittleEndian>,
3333
}
3434

3535
// Superblock starts at 65536 for btrfs.
3636
const START_POSITION: u64 = 0x10000;
3737

3838
// "_BHRfS_M"
39-
const MAGIC: u64 = 0x4D5F53665248425F;
39+
const MAGIC: U64<LittleEndian> = U64::new(0x4D5F53665248425F);
4040

4141
/// Attempt to decode the Superblock from the given read stream
4242
pub fn from_reader<R: Read>(reader: &mut R) -> Result<Btrfs, Error> {
43-
const SIZE: usize = std::mem::size_of::<Btrfs>();
44-
let mut data: Btrfs = unsafe { std::mem::zeroed() };
45-
let data_sliced = unsafe { slice::from_raw_parts_mut(&mut data as *mut _ as *mut u8, SIZE) };
46-
4743
// Drop unwanted bytes (Seek not possible with zstd streamed inputs)
4844
io::copy(&mut reader.by_ref().take(START_POSITION), &mut io::sink())?;
49-
reader.read_exact(data_sliced)?;
45+
46+
let data = Btrfs::read_from_io(reader).map_err(|_| Error::InvalidSuperblock)?;
5047

5148
if data.magic != MAGIC {
5249
Err(Error::InvalidMagic)
@@ -78,7 +75,7 @@ mod tests {
7875

7976
use crate::{btrfs::from_reader, Superblock};
8077

81-
#[test]
78+
#[test_log::test]
8279
fn test_basic() {
8380
let mut fi = fs::File::open("tests/btrfs.img.zst").expect("cannot open ext4 img");
8481
let mut stream = zstd::stream::Decoder::new(&mut fi).expect("Unable to decode stream");

crates/superblock/src/ext4.rs

Lines changed: 78 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -6,114 +6,109 @@
66
77
use crate::{Error, Kind, Superblock};
88
use log;
9-
use std::{
10-
io::{self, Read},
11-
slice,
12-
};
9+
use std::io::{self, Read};
1310
use uuid::Uuid;
11+
use zerocopy::*;
1412

1513
/// EXT4 Superblock definition (as seen in the kernel)
16-
#[derive(Debug)]
14+
#[derive(Debug, FromBytes)]
1715
#[repr(C)]
1816
pub struct Ext4 {
19-
inodes_count: u32,
20-
block_counts_lo: u32,
21-
r_blocks_count_lo: u32,
22-
free_blocks_count_lo: u32,
23-
free_inodes_count: u32,
24-
first_data_block: u32,
25-
log_block_size: u32,
26-
log_cluster_size: u32,
27-
blocks_per_group: u32,
28-
clusters_per_group: u32,
29-
inodes_per_group: u32,
30-
m_time: u32,
31-
w_time: u32,
32-
mnt_count: u16,
33-
max_mnt_count: u16,
34-
magic: u16,
35-
state: u16,
36-
errors: u16,
37-
minor_rev_level: u16,
38-
lastcheck: u32,
39-
checkinterval: u32,
40-
creator_os: u32,
41-
rev_level: u32,
42-
def_resuid: u16,
43-
def_resgid: u16,
44-
first_ino: u32,
45-
inode_size: u16,
46-
block_group_nr: u16,
47-
feature_compat: u32,
48-
feature_incompat: u32,
49-
feature_ro_compat: u32,
17+
inodes_count: U32<LittleEndian>,
18+
block_counts_lo: U32<LittleEndian>,
19+
r_blocks_count_lo: U32<LittleEndian>,
20+
free_blocks_count_lo: U32<LittleEndian>,
21+
free_inodes_count: U32<LittleEndian>,
22+
first_data_block: U32<LittleEndian>,
23+
log_block_size: U32<LittleEndian>,
24+
log_cluster_size: U32<LittleEndian>,
25+
blocks_per_group: U32<LittleEndian>,
26+
clusters_per_group: U32<LittleEndian>,
27+
inodes_per_group: U32<LittleEndian>,
28+
m_time: U32<LittleEndian>,
29+
w_time: U32<LittleEndian>,
30+
mnt_count: U16<LittleEndian>,
31+
max_mnt_count: U16<LittleEndian>,
32+
magic: U16<LittleEndian>,
33+
state: U16<LittleEndian>,
34+
errors: U16<LittleEndian>,
35+
minor_rev_level: U16<LittleEndian>,
36+
lastcheck: U32<LittleEndian>,
37+
checkinterval: U32<LittleEndian>,
38+
creator_os: U32<LittleEndian>,
39+
rev_level: U32<LittleEndian>,
40+
def_resuid: U16<LittleEndian>,
41+
def_resgid: U16<LittleEndian>,
42+
first_ino: U32<LittleEndian>,
43+
inode_size: U16<LittleEndian>,
44+
block_group_nr: U16<LittleEndian>,
45+
feature_compat: U32<LittleEndian>,
46+
feature_incompat: U32<LittleEndian>,
47+
feature_ro_compat: U32<LittleEndian>,
5048
uuid: [u8; 16],
5149
volume_name: [u8; 16],
5250
last_mounted: [u8; 64],
53-
algorithm_usage_bitmap: u32,
51+
algorithm_usage_bitmap: U32<LittleEndian>,
5452
prealloc_blocks: u8,
5553
prealloc_dir_blocks: u8,
56-
reserved_gdt_blocks: u16,
54+
reserved_gdt_blocks: U16<LittleEndian>,
5755
journal_uuid: [u8; 16],
58-
journal_inum: u32,
59-
journal_dev: u32,
60-
last_orphan: u32,
61-
hash_seed: [u32; 4],
56+
journal_inum: U32<LittleEndian>,
57+
journal_dev: U32<LittleEndian>,
58+
last_orphan: U32<LittleEndian>,
59+
hash_seed: [U32<LittleEndian>; 4],
6260
def_hash_version: u8,
6361
jnl_backup_type: u8,
64-
desc_size: u16,
65-
default_mount_opts: u32,
66-
first_meta_bg: u32,
67-
mkfs_time: u32,
68-
jnl_blocks: [u32; 17],
69-
blocks_count_hi: u32,
70-
free_blocks_count_hi: u32,
71-
min_extra_isize: u16,
72-
want_extra_isize: u16,
73-
flags: u32,
74-
raid_stride: u16,
75-
mmp_update_interval: u16,
76-
mmp_block: u64,
77-
raid_stripe_width: u32,
62+
desc_size: U16<LittleEndian>,
63+
default_mount_opts: U32<LittleEndian>,
64+
first_meta_bg: U32<LittleEndian>,
65+
mkfs_time: U32<LittleEndian>,
66+
jnl_blocks: [U32<LittleEndian>; 17],
67+
blocks_count_hi: U32<LittleEndian>,
68+
free_blocks_count_hi: U32<LittleEndian>,
69+
min_extra_isize: U16<LittleEndian>,
70+
want_extra_isize: U16<LittleEndian>,
71+
flags: U32<LittleEndian>,
72+
raid_stride: U16<LittleEndian>,
73+
mmp_update_interval: U16<LittleEndian>,
74+
mmp_block: U64<LittleEndian>,
75+
raid_stripe_width: U32<LittleEndian>,
7876
log_groups_per_flex: u8,
7977
checksum_type: u8,
80-
reserved_pad: u16,
81-
kbytes_written: u64,
82-
snapshot_inum: u32,
83-
snapshot_id: u32,
84-
snapshot_r_blocks_count: u64,
85-
snapshot_list: u32,
86-
error_count: u32,
87-
first_error_time: u32,
88-
first_error_inod: u32,
89-
first_error_block: u64,
78+
reserved_pad: U16<LittleEndian>,
79+
kbytes_written: U64<LittleEndian>,
80+
snapshot_inum: U32<LittleEndian>,
81+
snapshot_id: U32<LittleEndian>,
82+
snapshot_r_blocks_count: U64<LittleEndian>,
83+
snapshot_list: U32<LittleEndian>,
84+
error_count: U32<LittleEndian>,
85+
first_error_time: U32<LittleEndian>,
86+
first_error_inod: U32<LittleEndian>,
87+
first_error_block: U64<LittleEndian>,
9088
first_error_func: [u8; 32],
91-
first_error_line: u32,
92-
last_error_time: u32,
93-
last_error_inod: u32,
94-
last_error_line: u32,
95-
last_error_block: u64,
89+
first_error_line: U32<LittleEndian>,
90+
last_error_time: U32<LittleEndian>,
91+
last_error_inod: U32<LittleEndian>,
92+
last_error_line: U32<LittleEndian>,
93+
last_error_block: U64<LittleEndian>,
9694
last_error_func: [u8; 32],
9795
mount_opts: [u8; 64],
98-
usr_quota_inum: u32,
99-
grp_quota_inum: u32,
100-
overhead_clusters: u32,
101-
reserved: [u32; 108],
102-
checksum: u32,
96+
usr_quota_inum: U32<LittleEndian>,
97+
grp_quota_inum: U32<LittleEndian>,
98+
overhead_clusters: U32<LittleEndian>,
99+
reserved: [U32<LittleEndian>; 108],
100+
checksum: U32<LittleEndian>,
103101
}
104102

105-
const MAGIC: u16 = 0xEF53;
103+
const MAGIC: U16<LittleEndian> = U16::new(0xEF53);
106104
const START_POSITION: u64 = 1024;
107105

108106
/// Attempt to decode the Superblock from the given read stream
109107
pub fn from_reader<R: Read>(reader: &mut R) -> Result<Ext4, Error> {
110-
const SIZE: usize = std::mem::size_of::<Ext4>();
111-
let mut data: Ext4 = unsafe { std::mem::zeroed() };
112-
let data_sliced = unsafe { slice::from_raw_parts_mut(&mut data as *mut _ as *mut u8, SIZE) };
113-
114108
// Drop unwanted bytes (Seek not possible with zstd streamed inputs)
115109
io::copy(&mut reader.by_ref().take(START_POSITION), &mut io::sink())?;
116-
reader.read_exact(data_sliced)?;
110+
111+
let data = Ext4::read_from_io(reader).map_err(|_| Error::InvalidSuperblock)?;
117112

118113
if data.magic != MAGIC {
119114
Err(Error::InvalidMagic)
@@ -150,7 +145,7 @@ mod tests {
150145

151146
use crate::{ext4::from_reader, Superblock};
152147

153-
#[test]
148+
#[test_log::test]
154149
fn test_basic() {
155150
let mut fi = fs::File::open("tests/ext4.img.zst").expect("cannot open ext4 img");
156151
let mut stream = zstd::stream::Decoder::new(&mut fi).expect("Unable to decode stream");

0 commit comments

Comments
 (0)