Skip to content

Commit

Permalink
Merge pull request #723 from ReFirmLabs/btrfs_support
Browse files Browse the repository at this point in the history
Btrfs support
  • Loading branch information
devttys0 authored Oct 29, 2024
2 parents 437db68 + 9391f89 commit 04329b6
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 0 deletions.
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ entropy = "0.4.2"
colored = "2.1.0"
termsize = "0.1"
crc32-v2 = "0.0.4"
crc32c = "0.6.8"
plotters = "0.3.6"
xz2 = "0.1.7"
bzip2 = "0.4.4"
Expand Down
11 changes: 11 additions & 0 deletions src/magic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,17 @@ pub fn patterns() -> Vec<signatures::common::Signature> {
description: signatures::apfs::DESCRIPTION.to_string(),
extractor: Some(extractors::apfs::apfs_extractor()),
},
// BTRFS
signatures::common::Signature {
name: "btrfs".to_string(),
short: false,
magic_offset: 0,
always_display: true,
magic: signatures::btrfs::btrfs_magic(),
parser: signatures::btrfs::btrfs_parser,
description: signatures::btrfs::DESCRIPTION.to_string(),
extractor: None,
},
];

binary_signatures
Expand Down
1 change: 1 addition & 0 deletions src/signatures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ pub mod apfs;
pub mod arcadyan;
pub mod autel;
pub mod binhdr;
pub mod btrfs;
pub mod bzip2;
pub mod cab;
pub mod cfe;
Expand Down
41 changes: 41 additions & 0 deletions src/signatures/btrfs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use crate::signatures::common::{SignatureError, SignatureResult, CONFIDENCE_MEDIUM};
use crate::structures::btrfs::parse_btrfs_header;

/// Human readable description
pub const DESCRIPTION: &str = "BTRFS file system";

/// BTRFS magic bytes
pub fn btrfs_magic() -> Vec<Vec<u8>> {
vec![b"_BHRfS_M".to_vec()]
}

/// Validates the BTRFS header
pub fn btrfs_parser(file_data: &[u8], offset: usize) -> Result<SignatureResult, SignatureError> {
// Offset of the superblock magic bytes in a BTRFS image
const MAGIC_OFFSET: usize = 0x10040;

// Successful return value
let mut result = SignatureResult {
description: DESCRIPTION.to_string(),
confidence: CONFIDENCE_MEDIUM,
..Default::default()
};

// Sanity check the reported offset
if offset >= MAGIC_OFFSET {
// Actual offset is the location of the magic bytes minus the magic byte offset
result.offset = offset - MAGIC_OFFSET;

// Parse the superblock header; this also validates the superblock CRC
if let Ok(btrfs_header) = parse_btrfs_header(&file_data[result.offset..]) {
result.size = btrfs_header.total_size;
result.description = format!(
"{}, node size: {}, sector size: {}, leaf size: {}, stripe size: {}, bytes used: {}, total size: {} bytes",
result.description, btrfs_header.node_size, btrfs_header.sector_size, btrfs_header.leaf_size, btrfs_header.stripe_size, btrfs_header.bytes_used, result.size
);
return Ok(result);
}
}

Err(SignatureError)
}
1 change: 1 addition & 0 deletions src/structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ pub mod androidsparse;
pub mod apfs;
pub mod autel;
pub mod binhdr;
pub mod btrfs;
pub mod cab;
pub mod chk;
pub mod common;
Expand Down
68 changes: 68 additions & 0 deletions src/structures/btrfs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use crate::structures::common::{self, StructureError};
use crc32c::crc32c;

/// Struct to store BTRFS super block info
#[derive(Debug, Default, Clone)]
pub struct BTRFSHeader {
pub bytes_used: usize,
pub total_size: usize,
pub leaf_size: usize,
pub node_size: usize,
pub stripe_size: usize,
pub sector_size: usize,
}

/// Parse and validate a BTRFS super block
pub fn parse_btrfs_header(btrfs_data: &[u8]) -> Result<BTRFSHeader, StructureError> {
const SUPERBLOCK_OFFSET: usize = 0x10000;
const SUPERBLOCK_END: usize = SUPERBLOCK_OFFSET + 0x1000;
const CRC_START: usize = 0x20;

// Partial BTRFS superblock structure for obtaining image size and CRC validation
// https://archive.kernel.org/oldwiki/btrfs.wiki.kernel.org/index.php/On-disk_Format.html#Superblock
let btrfs_structure = vec![
("header_checksum", "u32"),
("unused1", "u32"),
("unused2", "u64"),
("unused3", "u64"),
("unused4", "u64"),
("uuid_p1", "u64"),
("uuid_p2", "u64"),
("block_phys_addr", "u64"),
("flags", "u64"),
("magic", "u64"),
("generation", "u64"),
("root_tree_address", "u64"),
("chunk_tree_address", "u64"),
("log_tree_address", "u64"),
("log_root_transid", "u64"),
("total_bytes", "u64"),
("bytes_used", "u64"),
("root_dir_objid", "u64"),
("num_devices", "u64"),
("sector_size", "u32"),
("node_size", "u32"),
("leaf_size", "u32"),
("stripe_size", "u32"),
];

// Parse the header
if let Some(btrfs_header_data) = btrfs_data.get(SUPERBLOCK_OFFSET..SUPERBLOCK_END) {
if let Ok(btrfs_header) = common::parse(btrfs_header_data, &btrfs_structure, "little") {
// Validate the superblock CRC
if btrfs_header["header_checksum"] == (crc32c(&btrfs_header_data[CRC_START..]) as usize)
{
return Ok(BTRFSHeader {
sector_size: btrfs_header["sector_size"],
node_size: btrfs_header["node_size"],
leaf_size: btrfs_header["leaf_size"],
stripe_size: btrfs_header["stripe_size"],
bytes_used: btrfs_header["bytes_used"],
total_size: btrfs_header["total_bytes"],
});
}
}
}

Err(StructureError)
}

0 comments on commit 04329b6

Please sign in to comment.