use crate::error::{NtfsError, Result};
use crate::ntfs::Ntfs;
use alloc::vec::Vec;
use byteorder::{ByteOrder, LittleEndian};
use core::convert::TryInto;
use core::mem;
use memoffset::{offset_of, span_of};
#[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<'n> {
ntfs: &'n Ntfs,
data: Vec<u8>,
position: u64,
}
impl<'n> Record<'n> {
pub(crate) fn new(ntfs: &'n Ntfs, data: Vec<u8>, position: u64) -> Self {
Self {
ntfs,
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 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 mut sector_position = self.ntfs.sector_size() as usize - 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>();
if sector_position_end > self.data.len() {
return Err(NtfsError::UpdateSequenceArrayExceedsRecordSize {
position: self.position,
array_count: self.update_sequence_array_count(),
sector_size: self.ntfs.sector_size(),
record_size: self.data.len(),
});
}
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 as u64,
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 += self.ntfs.sector_size() as usize;
}
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 ntfs(&self) -> &'n Ntfs {
self.ntfs
}
pub(crate) fn position(&self) -> u64 {
self.position
}
pub(crate) fn signature(&self) -> [u8; 4] {
self.data[span_of!(RecordHeader, signature)]
.try_into()
.unwrap()
}
fn update_sequence_array_count(&self) -> u16 {
let start = offset_of!(RecordHeader, update_sequence_count);
let update_sequence_count = LittleEndian::read_u16(&self.data[start..]);
update_sequence_count - mem::size_of::<u16>() as u16
}
fn update_sequence_array_start(&self) -> u16 {
self.update_sequence_offset() + mem::size_of::<u16>() as u16
}
fn update_sequence_number(&self) -> [u8; 2] {
let start = self.update_sequence_offset() as usize;
let end = start + mem::size_of::<u16>();
self.data[start..end].try_into().unwrap()
}
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
}
}