Skip to content

Commit

Permalink
superblock: Convert to zerocopy/endian-aware
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
ikeycode committed Jan 19, 2025
1 parent b42ee37 commit 2058604
Show file tree
Hide file tree
Showing 9 changed files with 626 additions and 225 deletions.
409 changes: 409 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ members = [

[workspace.dependencies]
log = "0.4.21"
test-log = "0.2.17"
thiserror = "2.0.3"
uuid = { version = "1.8.0", features = ["v8"] }
zstd = "0.13.1"
zerocopy = "0.8.0"
4 changes: 3 additions & 1 deletion crates/superblock/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ edition = "2021"
uuid = { workspace = true, features = ["v8"] }
thiserror.workspace = true
log.workspace = true
zerocopy = { workspace = true, features = ["derive", "std"] }

[dev-dependencies]
zstd.workspace = true
test-log.workspace = true
zstd.workspace = true
35 changes: 16 additions & 19 deletions crates/superblock/src/btrfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,45 +8,42 @@
use crate::{Error, Kind, Superblock};
use log;
use std::{
io::{self, Read},
slice,
};
use std::
io::{self, Read}
;
use uuid::Uuid;
use zerocopy::*;

/// BTRFS superblock definition (as seen in the kernel)
/// This is a PARTIAL representation that matches only the
/// first 72 bytes, verifies the magic, and permits extraction
/// of the UUID
#[derive(Debug)]
#[derive(FromBytes, Debug)]
#[repr(C)]
pub struct Btrfs {
csum: [u8; 32],
fsid: [u8; 16],
bytenr: u64,
flags: u64,
magic: u64,
generation: u64,
root: u64,
chunk_root: u64,
log_root: u64,
bytenr: U64<LittleEndian>,
flags: U64<LittleEndian>,
magic: U64<LittleEndian>,
generation: U64<LittleEndian>,
root: U64<LittleEndian>,
chunk_root: U64<LittleEndian>,
log_root: U64<LittleEndian>,
}

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

// "_BHRfS_M"
const MAGIC: u64 = 0x4D5F53665248425F;
const MAGIC: U64<LittleEndian> = U64::new(0x4D5F53665248425F);

/// Attempt to decode the Superblock from the given read stream
pub fn from_reader<R: Read>(reader: &mut R) -> Result<Btrfs, Error> {
const SIZE: usize = std::mem::size_of::<Btrfs>();
let mut data: Btrfs = unsafe { std::mem::zeroed() };
let data_sliced = unsafe { slice::from_raw_parts_mut(&mut data as *mut _ as *mut u8, SIZE) };

// Drop unwanted bytes (Seek not possible with zstd streamed inputs)
io::copy(&mut reader.by_ref().take(START_POSITION), &mut io::sink())?;
reader.read_exact(data_sliced)?;

let data = Btrfs::read_from_io(reader).map_err(|_| Error::InvalidSuperblock)?;

if data.magic != MAGIC {
Err(Error::InvalidMagic)
Expand Down Expand Up @@ -78,7 +75,7 @@ mod tests {

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

#[test]
#[test_log::test]
fn test_basic() {
let mut fi = fs::File::open("tests/btrfs.img.zst").expect("cannot open ext4 img");
let mut stream = zstd::stream::Decoder::new(&mut fi).expect("Unable to decode stream");
Expand Down
161 changes: 78 additions & 83 deletions crates/superblock/src/ext4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,114 +6,109 @@
use crate::{Error, Kind, Superblock};
use log;
use std::{
io::{self, Read},
slice,
};
use std::io::{self, Read};
use uuid::Uuid;
use zerocopy::*;

/// EXT4 Superblock definition (as seen in the kernel)
#[derive(Debug)]
#[derive(Debug, FromBytes)]
#[repr(C)]
pub struct Ext4 {
inodes_count: u32,
block_counts_lo: u32,
r_blocks_count_lo: u32,
free_blocks_count_lo: u32,
free_inodes_count: u32,
first_data_block: u32,
log_block_size: u32,
log_cluster_size: u32,
blocks_per_group: u32,
clusters_per_group: u32,
inodes_per_group: u32,
m_time: u32,
w_time: 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,
inode_size: u16,
block_group_nr: u16,
feature_compat: u32,
feature_incompat: u32,
feature_ro_compat: u32,
inodes_count: U32<LittleEndian>,
block_counts_lo: U32<LittleEndian>,
r_blocks_count_lo: U32<LittleEndian>,
free_blocks_count_lo: U32<LittleEndian>,
free_inodes_count: U32<LittleEndian>,
first_data_block: U32<LittleEndian>,
log_block_size: U32<LittleEndian>,
log_cluster_size: U32<LittleEndian>,
blocks_per_group: U32<LittleEndian>,
clusters_per_group: U32<LittleEndian>,
inodes_per_group: U32<LittleEndian>,
m_time: U32<LittleEndian>,
w_time: U32<LittleEndian>,
mnt_count: U16<LittleEndian>,
max_mnt_count: U16<LittleEndian>,
magic: U16<LittleEndian>,
state: U16<LittleEndian>,
errors: U16<LittleEndian>,
minor_rev_level: U16<LittleEndian>,
lastcheck: U32<LittleEndian>,
checkinterval: U32<LittleEndian>,
creator_os: U32<LittleEndian>,
rev_level: U32<LittleEndian>,
def_resuid: U16<LittleEndian>,
def_resgid: U16<LittleEndian>,
first_ino: U32<LittleEndian>,
inode_size: U16<LittleEndian>,
block_group_nr: U16<LittleEndian>,
feature_compat: U32<LittleEndian>,
feature_incompat: U32<LittleEndian>,
feature_ro_compat: U32<LittleEndian>,
uuid: [u8; 16],
volume_name: [u8; 16],
last_mounted: [u8; 64],
algorithm_usage_bitmap: u32,
algorithm_usage_bitmap: U32<LittleEndian>,
prealloc_blocks: u8,
prealloc_dir_blocks: u8,
reserved_gdt_blocks: u16,
reserved_gdt_blocks: U16<LittleEndian>,
journal_uuid: [u8; 16],
journal_inum: u32,
journal_dev: u32,
last_orphan: u32,
hash_seed: [u32; 4],
journal_inum: U32<LittleEndian>,
journal_dev: U32<LittleEndian>,
last_orphan: U32<LittleEndian>,
hash_seed: [U32<LittleEndian>; 4],
def_hash_version: u8,
jnl_backup_type: u8,
desc_size: u16,
default_mount_opts: u32,
first_meta_bg: u32,
mkfs_time: u32,
jnl_blocks: [u32; 17],
blocks_count_hi: u32,
free_blocks_count_hi: u32,
min_extra_isize: u16,
want_extra_isize: u16,
flags: u32,
raid_stride: u16,
mmp_update_interval: u16,
mmp_block: u64,
raid_stripe_width: u32,
desc_size: U16<LittleEndian>,
default_mount_opts: U32<LittleEndian>,
first_meta_bg: U32<LittleEndian>,
mkfs_time: U32<LittleEndian>,
jnl_blocks: [U32<LittleEndian>; 17],
blocks_count_hi: U32<LittleEndian>,
free_blocks_count_hi: U32<LittleEndian>,
min_extra_isize: U16<LittleEndian>,
want_extra_isize: U16<LittleEndian>,
flags: U32<LittleEndian>,
raid_stride: U16<LittleEndian>,
mmp_update_interval: U16<LittleEndian>,
mmp_block: U64<LittleEndian>,
raid_stripe_width: U32<LittleEndian>,
log_groups_per_flex: u8,
checksum_type: u8,
reserved_pad: u16,
kbytes_written: u64,
snapshot_inum: u32,
snapshot_id: u32,
snapshot_r_blocks_count: u64,
snapshot_list: u32,
error_count: u32,
first_error_time: u32,
first_error_inod: u32,
first_error_block: u64,
reserved_pad: U16<LittleEndian>,
kbytes_written: U64<LittleEndian>,
snapshot_inum: U32<LittleEndian>,
snapshot_id: U32<LittleEndian>,
snapshot_r_blocks_count: U64<LittleEndian>,
snapshot_list: U32<LittleEndian>,
error_count: U32<LittleEndian>,
first_error_time: U32<LittleEndian>,
first_error_inod: U32<LittleEndian>,
first_error_block: U64<LittleEndian>,
first_error_func: [u8; 32],
first_error_line: u32,
last_error_time: u32,
last_error_inod: u32,
last_error_line: u32,
last_error_block: u64,
first_error_line: U32<LittleEndian>,
last_error_time: U32<LittleEndian>,
last_error_inod: U32<LittleEndian>,
last_error_line: U32<LittleEndian>,
last_error_block: U64<LittleEndian>,
last_error_func: [u8; 32],
mount_opts: [u8; 64],
usr_quota_inum: u32,
grp_quota_inum: u32,
overhead_clusters: u32,
reserved: [u32; 108],
checksum: u32,
usr_quota_inum: U32<LittleEndian>,
grp_quota_inum: U32<LittleEndian>,
overhead_clusters: U32<LittleEndian>,
reserved: [U32<LittleEndian>; 108],
checksum: U32<LittleEndian>,
}

const MAGIC: u16 = 0xEF53;
const MAGIC: U16<LittleEndian> = U16::new(0xEF53);
const START_POSITION: u64 = 1024;

/// Attempt to decode the Superblock from the given read stream
pub fn from_reader<R: Read>(reader: &mut R) -> Result<Ext4, Error> {
const SIZE: usize = std::mem::size_of::<Ext4>();
let mut data: Ext4 = unsafe { std::mem::zeroed() };
let data_sliced = unsafe { slice::from_raw_parts_mut(&mut data as *mut _ as *mut u8, SIZE) };

// Drop unwanted bytes (Seek not possible with zstd streamed inputs)
io::copy(&mut reader.by_ref().take(START_POSITION), &mut io::sink())?;
reader.read_exact(data_sliced)?;

let data = Ext4::read_from_io(reader).map_err(|_| Error::InvalidSuperblock)?;

if data.magic != MAGIC {
Err(Error::InvalidMagic)
Expand Down Expand Up @@ -150,7 +145,7 @@ mod tests {

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

#[test]
#[test_log::test]
fn test_basic() {
let mut fi = fs::File::open("tests/ext4.img.zst").expect("cannot open ext4 img");
let mut stream = zstd::stream::Decoder::new(&mut fi).expect("Unable to decode stream");
Expand Down
Loading

0 comments on commit 2058604

Please sign in to comment.