use crate::common::get_cstring;
use crate::structures::common::{self, StructureError};
#[derive(Default, Debug, Clone)]
pub struct RomFSHeader {
pub image_size: usize,
pub header_size: usize,
pub volume_name: String,
}
pub fn parse_romfs_header(romfs_data: &[u8]) -> Result<RomFSHeader, StructureError> {
const MAX_HEADER_CRC_DATA_LEN: usize = 512;
let header_structure = vec![("magic", "u64"), ("image_size", "u32"), ("checksum", "u32")];
let header_size = common::size(&header_structure);
if let Ok(header) = common::parse(romfs_data, &header_structure, "big") {
if header["image_size"] > header_size {
if let Some(volume_name_bytes) = romfs_data.get(header_size..) {
let volume_name = get_cstring(volume_name_bytes);
let mut crc_data_len: usize = MAX_HEADER_CRC_DATA_LEN;
if header["image_size"] < crc_data_len {
crc_data_len = header["image_size"];
}
if let Some(crc_data) = romfs_data.get(0..crc_data_len) {
if romfs_crc_valid(crc_data) {
return Ok(RomFSHeader {
image_size: header["image_size"],
volume_name: volume_name.clone(),
header_size: header_size + romfs_align(volume_name.len() + 1),
});
}
}
}
}
}
Err(StructureError)
}
#[derive(Debug, Default, Clone)]
pub struct RomFSFileHeader {
pub info: usize,
pub size: usize,
pub name: String,
pub checksum: usize,
pub data_offset: usize,
pub file_type: usize,
pub executable: bool,
pub symlink: bool,
pub directory: bool,
pub regular: bool,
pub block_device: bool,
pub character_device: bool,
pub fifo: bool,
pub socket: bool,
pub next_header_offset: usize,
}
pub fn parse_romfs_file_entry(romfs_data: &[u8]) -> Result<RomFSFileHeader, StructureError> {
const FILE_TYPE_MASK: usize = 0b0111;
const FILE_EXEC_MASK: usize = 0b1000;
const NEXT_OFFSET_MASK: usize = 0b11111111_11111111_11111111_11110000;
const ROMFS_DIRECTORY: usize = 1;
const ROMFS_REGULAR_FILE: usize = 2;
const ROMFS_SYMLINK: usize = 3;
const ROMFS_BLOCK_DEVICE: usize = 4;
const ROMFS_CHAR_DEVICE: usize = 5;
const ROMFS_SOCKET: usize = 6;
const ROMFS_FIFO: usize = 7;
let file_header_structure = vec![
("next_header_offset", "u32"),
("info", "u32"),
("size", "u32"),
("checksum", "u32"),
];
let file_header_size = common::size(&file_header_structure);
if let Ok(file_entry_header) = common::parse(romfs_data, &file_header_structure, "big") {
if let Some(file_name_bytes) = romfs_data.get(file_header_size..) {
let file_name = get_cstring(file_name_bytes);
if !file_name.is_empty() {
let mut file_header = RomFSFileHeader {
..Default::default()
};
file_header.size = file_entry_header["size"];
file_header.info = file_entry_header["info"];
file_header.checksum = file_entry_header["checksum"];
file_header.name = file_name.clone();
file_header.data_offset = file_header_size + romfs_align(file_name.len() + 1);
file_header.file_type = file_entry_header["next_header_offset"] & FILE_TYPE_MASK;
file_header.executable =
(file_entry_header["next_header_offset"] & FILE_EXEC_MASK) != 0;
file_header.fifo = file_header.file_type == ROMFS_FIFO;
file_header.socket = file_header.file_type == ROMFS_SOCKET;
file_header.symlink = file_header.file_type == ROMFS_SYMLINK;
file_header.regular = file_header.file_type == ROMFS_REGULAR_FILE;
file_header.directory = file_header.file_type == ROMFS_DIRECTORY;
file_header.block_device = file_header.file_type == ROMFS_BLOCK_DEVICE;
file_header.character_device = file_header.file_type == ROMFS_CHAR_DEVICE;
file_header.next_header_offset =
file_entry_header["next_header_offset"] & NEXT_OFFSET_MASK;
return Ok(file_header);
}
}
}
Err(StructureError)
}
fn romfs_align(x: usize) -> usize {
const ALIGNMENT: usize = 16;
let mut padding: usize = 0;
let remainder = x % ALIGNMENT;
if remainder > 0 {
padding = ALIGNMENT - remainder;
}
x + padding
}
fn romfs_crc_valid(crc_data: &[u8]) -> bool {
let word_size: usize = std::mem::size_of::<u32>();
if (crc_data.len() % word_size) == 0 {
let mut i: usize = 0;
let mut sum: u32 = 0;
while i < crc_data.len() {
sum += u32::from_be_bytes(crc_data[i..i + word_size].try_into().unwrap());
i += word_size;
}
return sum == 0;
}
false
}