use core::mem;
use core::ops::Range;
use zerocopy::byteorder::LittleEndian;
use zerocopy::{FromBytes, Immutable, KnownLayout, U32, Unaligned};
use crate::attribute::NtfsAttributeType;
use crate::attribute_value::{NtfsAttributeValue, NtfsResidentAttributeValue};
use crate::error::{NtfsError, Result};
use crate::helpers::pod_from_prefix;
use crate::index_entry::{IndexNodeEntryRanges, NtfsIndexNodeEntries};
use crate::index_record::{INDEX_NODE_HEADER_SIZE, IndexNodeHeader};
use crate::indexes::NtfsIndexEntryType;
use crate::io::{Read, Seek};
use crate::structured_values::{
NtfsStructuredValue, NtfsStructuredValueFromResidentAttributeValue,
};
use crate::types::NtfsPosition;
const INDEX_ROOT_HEADER_SIZE: usize = mem::size_of::<IndexRootHeader>();
#[derive(Clone, Copy, Debug, FromBytes, Immutable, KnownLayout, Unaligned)]
#[repr(C, packed)]
struct IndexRootHeader {
ty: U32<LittleEndian>,
collation_rule: U32<LittleEndian>,
index_record_size: U32<LittleEndian>,
clusters_per_index_record: i8,
reserved: [u8; 3],
}
#[derive(Clone, Debug)]
pub struct NtfsIndexRoot<'f> {
slice: &'f [u8],
header: IndexRootHeader,
node_header: IndexNodeHeader,
position: NtfsPosition,
}
const LARGE_INDEX_FLAG: u8 = 0x01;
impl<'f> NtfsIndexRoot<'f> {
fn new(slice: &'f [u8], position: NtfsPosition) -> Result<Self> {
if slice.len() < INDEX_ROOT_HEADER_SIZE + INDEX_NODE_HEADER_SIZE {
return Err(NtfsError::InvalidStructuredValueSize {
position,
ty: NtfsAttributeType::IndexRoot,
expected: INDEX_ROOT_HEADER_SIZE as u64,
actual: slice.len() as u64,
});
}
let header = pod_from_prefix::<IndexRootHeader, INDEX_ROOT_HEADER_SIZE>(slice).unwrap();
let node_header = pod_from_prefix::<IndexNodeHeader, INDEX_NODE_HEADER_SIZE>(
&slice[INDEX_ROOT_HEADER_SIZE..],
)
.unwrap();
let index_root = Self {
slice,
header,
node_header,
position,
};
index_root.validate_sizes()?;
Ok(index_root)
}
pub fn entries<E>(&self) -> Result<NtfsIndexNodeEntries<'f, E>>
where
E: NtfsIndexEntryType,
{
let (entries_range, position) = self.entries_range_and_position();
let slice = &self.slice[entries_range];
Ok(NtfsIndexNodeEntries::new(slice, position))
}
fn entries_range_and_position(&self) -> (Range<usize>, NtfsPosition) {
let start = INDEX_ROOT_HEADER_SIZE + self.index_entries_offset() as usize;
let end = INDEX_ROOT_HEADER_SIZE + self.index_data_size() as usize;
let position = self.position + start;
(start..end, position)
}
pub(crate) fn entry_ranges<E>(&self) -> IndexNodeEntryRanges<E>
where
E: NtfsIndexEntryType,
{
let (entries_range, position) = self.entries_range_and_position();
let entries_data = self.slice[entries_range].to_vec();
let range = 0..entries_data.len();
IndexNodeEntryRanges::new(entries_data, range, position)
}
pub fn index_allocated_size(&self) -> u32 {
self.node_header.allocated_size.get()
}
pub fn index_data_size(&self) -> u32 {
self.node_header.index_size.get()
}
fn index_entries_offset(&self) -> u32 {
self.node_header.entries_offset.get()
}
pub fn index_record_size(&self) -> u32 {
self.header.index_record_size.get()
}
pub fn is_large_index(&self) -> bool {
(self.node_header.flags & LARGE_INDEX_FLAG) != 0
}
pub fn position(&self) -> NtfsPosition {
self.position
}
fn validate_sizes(&self) -> Result<()> {
let (entries_range, _position) = self.entries_range_and_position();
if entries_range.start >= self.slice.len() {
return Err(NtfsError::InvalidIndexRootEntriesOffset {
position: self.position,
expected: entries_range.start,
actual: self.slice.len(),
});
}
if entries_range.end > self.slice.len() {
return Err(NtfsError::InvalidIndexRootUsedSize {
position: self.position,
expected: entries_range.end,
actual: self.slice.len(),
});
}
Ok(())
}
}
impl<'n, 'f> NtfsStructuredValue<'n, 'f> for NtfsIndexRoot<'f> {
const TY: NtfsAttributeType = NtfsAttributeType::IndexRoot;
fn from_attribute_value<T>(_fs: &mut T, value: NtfsAttributeValue<'n, 'f>) -> Result<Self>
where
T: Read + Seek,
{
let position = value.data_position();
let resident_value = match value {
NtfsAttributeValue::Resident(resident_value) => resident_value,
_ => return Err(NtfsError::UnexpectedNonResidentAttribute { position }),
};
Self::new(resident_value.data(), position)
}
}
impl<'n, 'f> NtfsStructuredValueFromResidentAttributeValue<'n, 'f> for NtfsIndexRoot<'f> {
fn from_resident_attribute_value(value: NtfsResidentAttributeValue<'f>) -> Result<Self> {
Self::new(value.data(), value.data_position())
}
}