use crate::{AafError, Result};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Seek, SeekFrom, Write};
const SECTOR_SIZE: usize = 512;
const SIGNATURE: &[u8; 8] = b"\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1";
const MAX_REG_SECT_SHIFT: u16 = 9;
const MINI_SECT_SHIFT: u16 = 6;
const MINI_SECTOR_SIZE: usize = 64;
const END_OF_CHAIN: u32 = 0xFFFFFFFE;
const FREE_SECTOR: u32 = 0xFFFFFFFF;
const FAT_SECTOR: u32 = 0xFFFFFFFD;
const DIFAT_SECTOR: u32 = 0xFFFFFFFC;
const DIR_ENTRY_SIZE: usize = 128;
const HEADER_DIFAT_ENTRIES: usize = 109;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ObjectType {
Unknown,
Storage,
Stream,
Root,
}
impl ObjectType {
fn from_byte(b: u8) -> Self {
match b {
0 => ObjectType::Unknown,
1 => ObjectType::Storage,
2 => ObjectType::Stream,
5 => ObjectType::Root,
_ => ObjectType::Unknown,
}
}
fn to_byte(self) -> u8 {
match self {
ObjectType::Unknown => 0,
ObjectType::Storage => 1,
ObjectType::Stream => 2,
ObjectType::Root => 5,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NodeColor {
Red,
Black,
}
impl NodeColor {
fn from_byte(b: u8) -> Self {
match b {
0 => NodeColor::Red,
1 => NodeColor::Black,
_ => NodeColor::Black,
}
}
fn to_byte(self) -> u8 {
match self {
NodeColor::Red => 0,
NodeColor::Black => 1,
}
}
}
#[derive(Debug, Clone)]
pub struct DirectoryEntry {
pub name: String,
pub object_type: ObjectType,
pub color: NodeColor,
pub left_sibling_id: u32,
pub right_sibling_id: u32,
pub child_id: u32,
pub clsid: [u8; 16],
pub state_bits: u32,
pub creation_time: u64,
pub modified_time: u64,
pub starting_sector: u32,
pub size: u64,
}
impl DirectoryEntry {
pub fn parse(data: &[u8]) -> Result<Self> {
if data.len() < DIR_ENTRY_SIZE {
return Err(AafError::InvalidStructuredStorage(
"Directory entry too small".to_string(),
));
}
let mut cursor = std::io::Cursor::new(data);
let mut name_buf = [0u16; 32];
for item in &mut name_buf {
*item = cursor.read_u16::<LittleEndian>()?;
}
let name_len = cursor.read_u16::<LittleEndian>()? as usize;
let name =
String::from_utf16_lossy(&name_buf[..((name_len.saturating_sub(2)) / 2).min(32)]);
let object_type = ObjectType::from_byte(cursor.read_u8()?);
let color = NodeColor::from_byte(cursor.read_u8()?);
let left_sibling_id = cursor.read_u32::<LittleEndian>()?;
let right_sibling_id = cursor.read_u32::<LittleEndian>()?;
let child_id = cursor.read_u32::<LittleEndian>()?;
let mut clsid = [0u8; 16];
cursor.read_exact(&mut clsid)?;
let state_bits = cursor.read_u32::<LittleEndian>()?;
let creation_time = cursor.read_u64::<LittleEndian>()?;
let modified_time = cursor.read_u64::<LittleEndian>()?;
let starting_sector = cursor.read_u32::<LittleEndian>()?;
let size = cursor.read_u64::<LittleEndian>()?;
Ok(DirectoryEntry {
name,
object_type,
color,
left_sibling_id,
right_sibling_id,
child_id,
clsid,
state_bits,
creation_time,
modified_time,
starting_sector,
size,
})
}
pub fn serialize(&self) -> Result<Vec<u8>> {
let mut data = Vec::with_capacity(DIR_ENTRY_SIZE);
let name_utf16: Vec<u16> = self.name.encode_utf16().take(31).collect();
for &ch in &name_utf16 {
data.write_u16::<LittleEndian>(ch)?;
}
for _ in name_utf16.len()..32 {
data.write_u16::<LittleEndian>(0)?;
}
data.write_u16::<LittleEndian>((name_utf16.len() * 2 + 2) as u16)?;
data.write_u8(self.object_type.to_byte())?;
data.write_u8(self.color.to_byte())?;
data.write_u32::<LittleEndian>(self.left_sibling_id)?;
data.write_u32::<LittleEndian>(self.right_sibling_id)?;
data.write_u32::<LittleEndian>(self.child_id)?;
data.write_all(&self.clsid)?;
data.write_u32::<LittleEndian>(self.state_bits)?;
data.write_u64::<LittleEndian>(self.creation_time)?;
data.write_u64::<LittleEndian>(self.modified_time)?;
data.write_u32::<LittleEndian>(self.starting_sector)?;
data.write_u64::<LittleEndian>(self.size)?;
Ok(data)
}
#[must_use]
pub fn is_stream(&self) -> bool {
self.object_type == ObjectType::Stream
}
#[must_use]
pub fn is_storage(&self) -> bool {
self.object_type == ObjectType::Storage || self.object_type == ObjectType::Root
}
#[must_use]
pub fn uses_mini_stream(&self) -> bool {
self.size < 4096 && self.is_stream()
}
}
#[derive(Debug, Clone)]
pub struct Header {
pub sector_shift: u16,
pub mini_sector_shift: u16,
pub total_sectors: u32,
pub fat_sectors: u32,
pub first_dir_sector: u32,
pub transaction_signature: u32,
pub mini_stream_cutoff_size: u32,
pub first_mini_fat_sector: u32,
pub mini_fat_sectors: u32,
pub first_difat_sector: u32,
pub difat_sectors: u32,
pub difat: [u32; HEADER_DIFAT_ENTRIES],
}
impl Header {
pub fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self> {
reader.seek(SeekFrom::Start(0))?;
let mut sig = [0u8; 8];
reader.read_exact(&mut sig)?;
if &sig != SIGNATURE {
return Err(AafError::InvalidStructuredStorage(
"Invalid signature".to_string(),
));
}
reader.seek(SeekFrom::Current(16))?;
let _minor_version = reader.read_u16::<LittleEndian>()?;
let major_version = reader.read_u16::<LittleEndian>()?;
if major_version != 3 && major_version != 4 {
return Err(AafError::InvalidStructuredStorage(format!(
"Unsupported version: {major_version}"
)));
}
let byte_order = reader.read_u16::<LittleEndian>()?;
if byte_order != 0xFFFE {
return Err(AafError::InvalidStructuredStorage(
"Invalid byte order".to_string(),
));
}
let sector_shift = reader.read_u16::<LittleEndian>()?;
let mini_sector_shift = reader.read_u16::<LittleEndian>()?;
reader.seek(SeekFrom::Current(6))?;
let total_sectors = reader.read_u32::<LittleEndian>()?;
let fat_sectors = reader.read_u32::<LittleEndian>()?;
let first_dir_sector = reader.read_u32::<LittleEndian>()?;
let transaction_signature = reader.read_u32::<LittleEndian>()?;
let mini_stream_cutoff_size = reader.read_u32::<LittleEndian>()?;
let first_mini_fat_sector = reader.read_u32::<LittleEndian>()?;
let mini_fat_sectors = reader.read_u32::<LittleEndian>()?;
let first_difat_sector = reader.read_u32::<LittleEndian>()?;
let difat_sectors = reader.read_u32::<LittleEndian>()?;
let mut difat = [FREE_SECTOR; HEADER_DIFAT_ENTRIES];
for item in &mut difat {
*item = reader.read_u32::<LittleEndian>()?;
}
Ok(Header {
sector_shift,
mini_sector_shift,
total_sectors,
fat_sectors,
first_dir_sector,
transaction_signature,
mini_stream_cutoff_size,
first_mini_fat_sector,
mini_fat_sectors,
first_difat_sector,
difat_sectors,
difat,
})
}
#[must_use]
pub fn sector_size(&self) -> usize {
1 << self.sector_shift
}
#[must_use]
pub fn mini_sector_size(&self) -> usize {
1 << self.mini_sector_shift
}
#[must_use]
pub fn new() -> Self {
Self {
sector_shift: MAX_REG_SECT_SHIFT,
mini_sector_shift: MINI_SECT_SHIFT,
total_sectors: 0,
fat_sectors: 0,
first_dir_sector: 0,
transaction_signature: 0,
mini_stream_cutoff_size: 4096,
first_mini_fat_sector: END_OF_CHAIN,
mini_fat_sectors: 0,
first_difat_sector: END_OF_CHAIN,
difat_sectors: 0,
difat: [FREE_SECTOR; HEADER_DIFAT_ENTRIES],
}
}
pub fn write<W: Write + Seek>(&self, writer: &mut W) -> Result<()> {
writer.seek(SeekFrom::Start(0))?;
writer.write_all(SIGNATURE)?;
writer.write_all(&[0u8; 16])?;
writer.write_u16::<LittleEndian>(0x003E)?;
writer.write_u16::<LittleEndian>(3)?;
writer.write_u16::<LittleEndian>(0xFFFE)?;
writer.write_u16::<LittleEndian>(self.sector_shift)?;
writer.write_u16::<LittleEndian>(self.mini_sector_shift)?;
writer.write_all(&[0u8; 6])?;
writer.write_u32::<LittleEndian>(self.total_sectors)?;
writer.write_u32::<LittleEndian>(self.fat_sectors)?;
writer.write_u32::<LittleEndian>(self.first_dir_sector)?;
writer.write_u32::<LittleEndian>(self.transaction_signature)?;
writer.write_u32::<LittleEndian>(self.mini_stream_cutoff_size)?;
writer.write_u32::<LittleEndian>(self.first_mini_fat_sector)?;
writer.write_u32::<LittleEndian>(self.mini_fat_sectors)?;
writer.write_u32::<LittleEndian>(self.first_difat_sector)?;
writer.write_u32::<LittleEndian>(self.difat_sectors)?;
for &entry in &self.difat {
writer.write_u32::<LittleEndian>(entry)?;
}
Ok(())
}
}
impl Default for Header {
fn default() -> Self {
Self::new()
}
}
pub struct StorageReader<R: Read + Seek> {
reader: R,
header: Header,
fat: Vec<u32>,
mini_fat: Vec<u32>,
directory_entries: Vec<DirectoryEntry>,
mini_stream_data: Vec<u8>,
}
impl<R: Read + Seek> StorageReader<R> {
pub fn new(mut reader: R) -> Result<Self> {
let header = Header::parse(&mut reader)?;
let fat = Self::read_fat(&mut reader, &header)?;
let directory_entries = Self::read_directory(&mut reader, &header, &fat)?;
let mini_stream_data = if directory_entries.is_empty() {
Vec::new()
} else {
let root = &directory_entries[0];
Self::read_stream_data(&mut reader, &header, &fat, root)?
};
let mini_fat = Self::read_mini_fat(&mut reader, &header, &fat)?;
Ok(Self {
reader,
header,
fat,
mini_fat,
directory_entries,
mini_stream_data,
})
}
fn read_fat<T: Read + Seek>(reader: &mut T, header: &Header) -> Result<Vec<u32>> {
let sector_size = header.sector_size();
let entries_per_sector = sector_size / 4;
let mut fat = Vec::new();
let mut fat_sectors = Vec::new();
for §or in &header.difat {
if sector != FREE_SECTOR {
fat_sectors.push(sector);
}
}
let mut difat_sector = header.first_difat_sector;
while difat_sector != END_OF_CHAIN && difat_sector != FREE_SECTOR {
let offset = 512 + (u64::from(difat_sector) * sector_size as u64);
reader.seek(SeekFrom::Start(offset))?;
for _ in 0..entries_per_sector - 1 {
let sector = reader.read_u32::<LittleEndian>()?;
if sector != FREE_SECTOR {
fat_sectors.push(sector);
}
}
difat_sector = reader.read_u32::<LittleEndian>()?;
}
for §or in &fat_sectors {
let offset = 512 + (u64::from(sector) * sector_size as u64);
reader.seek(SeekFrom::Start(offset))?;
for _ in 0..entries_per_sector {
fat.push(reader.read_u32::<LittleEndian>()?);
}
}
Ok(fat)
}
fn read_mini_fat<T: Read + Seek>(
reader: &mut T,
header: &Header,
fat: &[u32],
) -> Result<Vec<u32>> {
if header.first_mini_fat_sector == END_OF_CHAIN
|| header.first_mini_fat_sector == FREE_SECTOR
{
return Ok(Vec::new());
}
let sector_size = header.sector_size();
let entries_per_sector = sector_size / 4;
let mut mini_fat = Vec::new();
let mut sector = header.first_mini_fat_sector;
while sector != END_OF_CHAIN && sector != FREE_SECTOR {
let offset = 512 + (u64::from(sector) * sector_size as u64);
reader.seek(SeekFrom::Start(offset))?;
for _ in 0..entries_per_sector {
mini_fat.push(reader.read_u32::<LittleEndian>()?);
}
sector = fat.get(sector as usize).copied().unwrap_or(END_OF_CHAIN);
}
Ok(mini_fat)
}
fn read_directory<T: Read + Seek>(
reader: &mut T,
header: &Header,
fat: &[u32],
) -> Result<Vec<DirectoryEntry>> {
let sector_size = header.sector_size();
let entries_per_sector = sector_size / DIR_ENTRY_SIZE;
let mut entries = Vec::new();
let mut sector = header.first_dir_sector;
while sector != END_OF_CHAIN && sector != FREE_SECTOR {
let offset = 512 + (u64::from(sector) * sector_size as u64);
reader.seek(SeekFrom::Start(offset))?;
for _ in 0..entries_per_sector {
let mut entry_data = vec![0u8; DIR_ENTRY_SIZE];
reader.read_exact(&mut entry_data)?;
let entry = DirectoryEntry::parse(&entry_data)?;
entries.push(entry);
}
sector = fat.get(sector as usize).copied().unwrap_or(END_OF_CHAIN);
}
Ok(entries)
}
fn read_stream_data<T: Read + Seek>(
reader: &mut T,
header: &Header,
fat: &[u32],
entry: &DirectoryEntry,
) -> Result<Vec<u8>> {
let sector_size = header.sector_size();
let mut data = Vec::new();
let mut sector = entry.starting_sector;
let mut remaining = entry.size as usize;
while sector != END_OF_CHAIN && sector != FREE_SECTOR && remaining > 0 {
let offset = 512 + (u64::from(sector) * sector_size as u64);
reader.seek(SeekFrom::Start(offset))?;
let to_read = remaining.min(sector_size);
let mut buffer = vec![0u8; to_read];
reader.read_exact(&mut buffer)?;
data.extend_from_slice(&buffer);
remaining -= to_read;
sector = fat.get(sector as usize).copied().unwrap_or(END_OF_CHAIN);
}
Ok(data)
}
pub fn header(&self) -> &Header {
&self.header
}
pub fn directory_entries(&self) -> &[DirectoryEntry] {
&self.directory_entries
}
pub fn find_entry(&self, path: &str) -> Option<&DirectoryEntry> {
let parts: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
if parts.is_empty() {
return self.directory_entries.first();
}
let mut current_id = 0u32;
for part in parts {
let children = self.get_children(current_id);
let found = children.iter().find(|&&id| {
self.directory_entries
.get(id as usize)
.is_some_and(|e| e.name == part)
});
if let Some(&id) = found {
current_id = id;
} else {
return None;
}
}
self.directory_entries.get(current_id as usize)
}
fn get_children(&self, parent_id: u32) -> Vec<u32> {
let parent = match self.directory_entries.get(parent_id as usize) {
Some(p) => p,
None => return Vec::new(),
};
let mut children = Vec::new();
if parent.child_id != 0xFFFFFFFF {
self.collect_children(parent.child_id, &mut children);
}
children
}
fn collect_children(&self, entry_id: u32, children: &mut Vec<u32>) {
if entry_id == 0xFFFFFFFF {
return;
}
let entry = match self.directory_entries.get(entry_id as usize) {
Some(e) => e,
None => return,
};
if entry.left_sibling_id != 0xFFFFFFFF {
self.collect_children(entry.left_sibling_id, children);
}
children.push(entry_id);
if entry.right_sibling_id != 0xFFFFFFFF {
self.collect_children(entry.right_sibling_id, children);
}
}
pub fn read_stream(&mut self, entry: &DirectoryEntry) -> Result<Vec<u8>> {
if entry.uses_mini_stream() {
self.read_mini_stream(entry)
} else {
Self::read_stream_data(&mut self.reader, &self.header, &self.fat, entry)
}
}
fn read_mini_stream(&self, entry: &DirectoryEntry) -> Result<Vec<u8>> {
let mini_sector_size = self.header.mini_sector_size();
let mut data = Vec::new();
let mut sector = entry.starting_sector;
let mut remaining = entry.size as usize;
while sector != END_OF_CHAIN && sector != FREE_SECTOR && remaining > 0 {
let offset = sector as usize * mini_sector_size;
let to_read = remaining.min(mini_sector_size);
if offset + to_read <= self.mini_stream_data.len() {
data.extend_from_slice(&self.mini_stream_data[offset..offset + to_read]);
}
remaining -= to_read;
sector = self
.mini_fat
.get(sector as usize)
.copied()
.unwrap_or(END_OF_CHAIN);
}
Ok(data)
}
pub fn read_stream_by_path(&mut self, path: &str) -> Result<Vec<u8>> {
let entry = self
.find_entry(path)
.ok_or_else(|| AafError::ObjectNotFound(path.to_string()))?
.clone();
if !entry.is_stream() {
return Err(AafError::InvalidStructuredStorage(format!(
"Not a stream: {path}"
)));
}
self.read_stream(&entry)
}
}
pub struct StorageWriter<W: Write + Seek> {
writer: W,
header: Header,
fat: Vec<u32>,
mini_fat: Vec<u32>,
directory_entries: Vec<DirectoryEntry>,
mini_stream_data: Vec<u8>,
current_sector: u32,
}
impl<W: Write + Seek> StorageWriter<W> {
pub fn new(writer: W) -> Result<Self> {
let header = Header::new();
let root = DirectoryEntry {
name: "Root Entry".to_string(),
object_type: ObjectType::Root,
color: NodeColor::Black,
left_sibling_id: 0xFFFFFFFF,
right_sibling_id: 0xFFFFFFFF,
child_id: 0xFFFFFFFF,
clsid: [0u8; 16],
state_bits: 0,
creation_time: 0,
modified_time: 0,
starting_sector: END_OF_CHAIN,
size: 0,
};
Ok(Self {
writer,
header,
fat: Vec::new(),
mini_fat: Vec::new(),
directory_entries: vec![root],
mini_stream_data: Vec::new(),
current_sector: 0,
})
}
fn allocate_sector(&mut self) -> u32 {
let sector = self.current_sector;
self.current_sector += 1;
self.fat.push(END_OF_CHAIN);
sector
}
pub fn write_stream(&mut self, name: &str, data: &[u8]) -> Result<u32> {
let entry_id = self.directory_entries.len() as u32;
let (starting_sector, _uses_mini) = if data.len() < 4096 {
let mini_sector = (self.mini_stream_data.len() / MINI_SECTOR_SIZE) as u32;
self.mini_stream_data.extend_from_slice(data);
let padding = (MINI_SECTOR_SIZE - (data.len() % MINI_SECTOR_SIZE)) % MINI_SECTOR_SIZE;
self.mini_stream_data
.resize(self.mini_stream_data.len() + padding, 0);
let sectors_needed = data.len().div_ceil(MINI_SECTOR_SIZE);
for i in 0..sectors_needed {
let sector = mini_sector + i as u32;
if i < sectors_needed - 1 {
self.mini_fat.push(sector + 1);
} else {
self.mini_fat.push(END_OF_CHAIN);
}
}
(mini_sector, true)
} else {
let starting_sector = self.allocate_sector();
let mut current_sector = starting_sector;
let sector_size = self.header.sector_size();
for chunk in data.chunks(sector_size) {
let offset = 512 + (u64::from(current_sector) * sector_size as u64);
self.writer.seek(SeekFrom::Start(offset))?;
self.writer.write_all(chunk)?;
if chunk.len() < sector_size {
let padding = vec![0u8; sector_size - chunk.len()];
self.writer.write_all(&padding)?;
}
if chunk.len() == sector_size && !chunk.is_empty() {
let next_sector = self.allocate_sector();
self.fat[current_sector as usize] = next_sector;
current_sector = next_sector;
}
}
(starting_sector, false)
};
let entry = DirectoryEntry {
name: name.to_string(),
object_type: ObjectType::Stream,
color: NodeColor::Black,
left_sibling_id: 0xFFFFFFFF,
right_sibling_id: 0xFFFFFFFF,
child_id: 0xFFFFFFFF,
clsid: [0u8; 16],
state_bits: 0,
creation_time: 0,
modified_time: 0,
starting_sector,
size: data.len() as u64,
};
self.directory_entries.push(entry);
Ok(entry_id)
}
pub fn create_storage(&mut self, name: &str) -> Result<u32> {
let entry_id = self.directory_entries.len() as u32;
let entry = DirectoryEntry {
name: name.to_string(),
object_type: ObjectType::Storage,
color: NodeColor::Black,
left_sibling_id: 0xFFFFFFFF,
right_sibling_id: 0xFFFFFFFF,
child_id: 0xFFFFFFFF,
clsid: [0u8; 16],
state_bits: 0,
creation_time: 0,
modified_time: 0,
starting_sector: 0,
size: 0,
};
self.directory_entries.push(entry);
Ok(entry_id)
}
pub fn finalize(&mut self) -> Result<()> {
if !self.mini_stream_data.is_empty() {
let mini_stream_sector = self.allocate_sector();
let sector_size = self.header.sector_size();
let mini_stream_data = self.mini_stream_data.clone();
let mut current_sector = mini_stream_sector;
for chunk in mini_stream_data.chunks(sector_size) {
let offset = 512 + (u64::from(current_sector) * sector_size as u64);
self.writer.seek(SeekFrom::Start(offset))?;
self.writer.write_all(chunk)?;
if chunk.len() == sector_size {
let next_sector = self.allocate_sector();
self.fat[current_sector as usize] = next_sector;
current_sector = next_sector;
}
}
self.directory_entries[0].starting_sector = mini_stream_sector;
self.directory_entries[0].size = mini_stream_data.len() as u64;
}
if !self.mini_fat.is_empty() {
self.header.first_mini_fat_sector = self.allocate_sector();
let sector_size = self.header.sector_size();
let entries_per_sector = sector_size / 4;
let mini_fat = self.mini_fat.clone();
let mut current_sector = self.header.first_mini_fat_sector;
for chunk in mini_fat.chunks(entries_per_sector) {
let offset = 512 + (u64::from(current_sector) * sector_size as u64);
self.writer.seek(SeekFrom::Start(offset))?;
for &entry in chunk {
self.writer.write_u32::<LittleEndian>(entry)?;
}
for _ in chunk.len()..entries_per_sector {
self.writer.write_u32::<LittleEndian>(FREE_SECTOR)?;
}
if chunk.len() == entries_per_sector {
let next_sector = self.allocate_sector();
self.fat[current_sector as usize] = next_sector;
current_sector = next_sector;
}
}
self.header.mini_fat_sectors = mini_fat.len().div_ceil(entries_per_sector) as u32;
}
self.write_directory()?;
self.write_fat()?;
self.header.write(&mut self.writer)?;
Ok(())
}
fn write_directory(&mut self) -> Result<()> {
let first_dir_sector = self.allocate_sector();
self.header.first_dir_sector = first_dir_sector;
let sector_size = self.header.sector_size();
let entries_per_sector = sector_size / DIR_ENTRY_SIZE;
let directory_entries = self.directory_entries.clone();
let mut current_sector = first_dir_sector;
for chunk in directory_entries.chunks(entries_per_sector) {
let offset = 512 + (u64::from(current_sector) * sector_size as u64);
self.writer.seek(SeekFrom::Start(offset))?;
for entry in chunk {
let data = entry.serialize()?;
self.writer.write_all(&data)?;
}
for _ in chunk.len()..entries_per_sector {
self.writer.write_all(&[0u8; DIR_ENTRY_SIZE])?;
}
if chunk.len() == entries_per_sector {
let next_sector = self.allocate_sector();
self.fat[current_sector as usize] = next_sector;
current_sector = next_sector;
}
}
Ok(())
}
fn write_fat(&mut self) -> Result<()> {
let sector_size = self.header.sector_size();
let entries_per_sector = sector_size / 4;
let fat_sectors = self.fat.len().div_ceil(entries_per_sector);
for i in 0..fat_sectors.min(HEADER_DIFAT_ENTRIES) {
let sector = self.allocate_sector();
self.header.difat[i] = sector;
self.fat[sector as usize] = FAT_SECTOR;
}
self.header.fat_sectors = fat_sectors as u32;
for (i, chunk) in self.fat.chunks(entries_per_sector).enumerate() {
if i < HEADER_DIFAT_ENTRIES {
let sector = self.header.difat[i];
let offset = 512 + (u64::from(sector) * sector_size as u64);
self.writer.seek(SeekFrom::Start(offset))?;
for &entry in chunk {
self.writer.write_u32::<LittleEndian>(entry)?;
}
for _ in chunk.len()..entries_per_sector {
self.writer.write_u32::<LittleEndian>(FREE_SECTOR)?;
}
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_object_type() {
assert_eq!(ObjectType::from_byte(0), ObjectType::Unknown);
assert_eq!(ObjectType::from_byte(1), ObjectType::Storage);
assert_eq!(ObjectType::from_byte(2), ObjectType::Stream);
assert_eq!(ObjectType::from_byte(5), ObjectType::Root);
assert_eq!(ObjectType::Storage.to_byte(), 1);
}
#[test]
fn test_node_color() {
assert_eq!(NodeColor::from_byte(0), NodeColor::Red);
assert_eq!(NodeColor::from_byte(1), NodeColor::Black);
assert_eq!(NodeColor::Red.to_byte(), 0);
}
#[test]
fn test_header_creation() {
let header = Header::new();
assert_eq!(header.sector_shift, MAX_REG_SECT_SHIFT);
assert_eq!(header.mini_sector_shift, MINI_SECT_SHIFT);
assert_eq!(header.sector_size(), SECTOR_SIZE);
assert_eq!(header.mini_sector_size(), MINI_SECTOR_SIZE);
}
}