use core::mem;
use alloc::vec::Vec;
use byteorder::{ByteOrder, LittleEndian};
use memoffset::{offset_of, span_of};
use crate::error::{NtfsError, Result};
use crate::types::NtfsPosition;
const NTFS_BLOCK_SIZE: usize = 512;
#[repr(C, packed)]
pub(crate) struct RecordHeader {
signature: [u8; 4],
update_sequence_offset: u16,
update_sequence_count: u16,
logfile_sequence_number: u64,
}
#[derive(Clone, Debug)]
pub(crate) struct Record {
data: Vec<u8>,
position: NtfsPosition,
}
impl Record {
pub(crate) fn new(data: Vec<u8>, position: NtfsPosition) -> Self {
Self { data, position }
}
pub(crate) fn data(&self) -> &[u8] {
&self.data
}
pub(crate) fn fixup(&mut self) -> Result<()> {
let update_sequence_number = self.update_sequence_number()?;
let array_count = self.update_sequence_array_count()?;
let mut array_position = self.update_sequence_array_start() as usize;
let array_end =
self.update_sequence_offset() as usize + self.update_sequence_size() as usize;
let sectors_end = array_count as usize * NTFS_BLOCK_SIZE;
if array_end > self.data.len() || sectors_end > self.data.len() {
return Err(NtfsError::UpdateSequenceArrayExceedsRecordSize {
position: self.position,
array_count,
record_size: self.data.len(),
});
}
let mut sector_position = NTFS_BLOCK_SIZE - mem::size_of::<u16>();
while array_position < array_end {
let array_position_end = array_position + mem::size_of::<u16>();
let sector_position_end = sector_position + mem::size_of::<u16>();
let new_bytes: [u8; 2] = self.data[array_position..array_position_end]
.try_into()
.unwrap();
let bytes_to_update = &mut self.data[sector_position..sector_position_end];
if bytes_to_update != update_sequence_number {
return Err(NtfsError::UpdateSequenceNumberMismatch {
position: self.position + array_position,
expected: update_sequence_number,
actual: (&*bytes_to_update).try_into().unwrap(),
});
}
bytes_to_update.copy_from_slice(&new_bytes);
array_position += mem::size_of::<u16>();
sector_position += NTFS_BLOCK_SIZE;
}
Ok(())
}
pub(crate) fn into_data(self) -> Vec<u8> {
self.data
}
pub(crate) fn len(&self) -> u32 {
self.data.len() as u32
}
pub(crate) fn position(&self) -> NtfsPosition {
self.position
}
pub(crate) fn signature(&self) -> [u8; 4] {
self.data[span_of!(RecordHeader, signature)]
.try_into()
.unwrap()
}
fn update_sequence_array_count(&self) -> Result<u16> {
let start = offset_of!(RecordHeader, update_sequence_count);
let update_sequence_count = LittleEndian::read_u16(&self.data[start..]);
update_sequence_count
.checked_sub(1)
.ok_or(NtfsError::InvalidUpdateSequenceCount {
position: self.position,
update_sequence_count,
})
}
fn update_sequence_array_start(&self) -> u16 {
self.update_sequence_offset() + mem::size_of::<u16>() as u16
}
fn update_sequence_number(&self) -> Result<[u8; 2]> {
let start = self.update_sequence_offset() as usize;
let end = start + mem::size_of::<u16>();
self.data
.get(start..end)
.and_then(|bytes| bytes.try_into().ok())
.ok_or(NtfsError::InvalidUpdateSequenceNumberRange {
position: self.position,
range: start..end,
size: self.data.len(),
})
}
fn update_sequence_offset(&self) -> u16 {
let start = offset_of!(RecordHeader, update_sequence_offset);
LittleEndian::read_u16(&self.data[start..])
}
pub(crate) fn update_sequence_size(&self) -> u32 {
let start = offset_of!(RecordHeader, update_sequence_count);
let update_sequence_count = LittleEndian::read_u16(&self.data[start..]);
update_sequence_count as u32 * mem::size_of::<u16>() as u32
}
}