use core::ops::Range;
use alloc::vec::Vec;
use core::mem;
use zerocopy::byteorder::LittleEndian;
use zerocopy::{FromBytes, I64, Immutable, KnownLayout, U32, Unaligned};
use crate::attribute_value::NtfsAttributeValue;
use crate::error::{NtfsError, Result};
use crate::helpers::pod_from_prefix;
use crate::index_entry::{IndexNodeEntryRanges, NtfsIndexNodeEntries};
use crate::indexes::NtfsIndexEntryType;
use crate::io::{Read, Seek};
use crate::record::NTFS_BLOCK_SIZE;
use crate::record::Record;
use crate::record::RecordHeader;
use crate::traits::NtfsReadSeek;
use crate::types::{NtfsPosition, Vcn};
const INDEX_RECORD_HEADER_SIZE: u32 = mem::size_of::<IndexRecordHeader>() as u32;
const MIN_INDEX_RECORD_SIZE: u32 = INDEX_RECORD_HEADER_SIZE + INDEX_NODE_HEADER_SIZE as u32;
pub(crate) fn validate_index_record_size(
index_record_size: u32,
position: NtfsPosition,
) -> Result<()> {
if index_record_size < MIN_INDEX_RECORD_SIZE
|| !(index_record_size as usize).is_multiple_of(NTFS_BLOCK_SIZE)
{
return Err(NtfsError::InvalidIndexRecordSize {
position,
minimum: MIN_INDEX_RECORD_SIZE,
block_size: NTFS_BLOCK_SIZE as u32,
actual: index_record_size,
});
}
Ok(())
}
#[derive(Clone, Copy, Debug, FromBytes, Immutable, KnownLayout, Unaligned)]
#[repr(C, packed)]
struct IndexRecordHeader {
record_header: RecordHeader,
vcn: I64<LittleEndian>,
}
pub(crate) const INDEX_NODE_HEADER_SIZE: usize = mem::size_of::<IndexNodeHeader>();
#[derive(Clone, Copy, Debug, FromBytes, Immutable, KnownLayout, Unaligned)]
#[repr(C, packed)]
pub(crate) struct IndexNodeHeader {
pub(crate) entries_offset: U32<LittleEndian>,
pub(crate) index_size: U32<LittleEndian>,
pub(crate) allocated_size: U32<LittleEndian>,
pub(crate) flags: u8,
reserved: [u8; 3],
}
#[derive(Debug)]
pub struct NtfsIndexRecord {
record: Record,
header: IndexRecordHeader,
node_header: IndexNodeHeader,
}
const HAS_SUBNODES_FLAG: u8 = 0x01;
impl NtfsIndexRecord {
pub(crate) fn new<T>(
fs: &mut T,
value: &mut NtfsAttributeValue,
index_record_size: u32,
) -> Result<Self>
where
T: Read + Seek,
{
Self::new_with_buffer(fs, value, index_record_size, Vec::new())
}
pub(crate) fn new_with_buffer<T>(
fs: &mut T,
value: &mut NtfsAttributeValue,
index_record_size: u32,
mut data: Vec<u8>,
) -> Result<Self>
where
T: Read + Seek,
{
let data_position = value.data_position();
validate_index_record_size(index_record_size, data_position)?;
data.resize(index_record_size as usize, 0);
value.read_exact(fs, &mut data)?;
Self::from_data(data, data_position)
}
pub(crate) fn from_data(data: Vec<u8>, data_position: NtfsPosition) -> Result<Self> {
let mut record = Record::new(data, data_position)?;
Self::validate_signature(&record)?;
record.fixup()?;
let header = pod_from_prefix::<IndexRecordHeader, { mem::size_of::<IndexRecordHeader>() }>(
record.data(),
)
.ok_or(NtfsError::BufferTooSmall {
expected: mem::size_of::<IndexRecordHeader>(),
actual: record.data().len(),
})?;
let node_header_start = INDEX_RECORD_HEADER_SIZE as usize;
let node_header = pod_from_prefix::<IndexNodeHeader, INDEX_NODE_HEADER_SIZE>(
&record.data()[node_header_start..],
)
.ok_or(NtfsError::BufferTooSmall {
expected: node_header_start + INDEX_NODE_HEADER_SIZE,
actual: record.data().len(),
})?;
let index_record = Self {
record,
header,
node_header,
};
index_record.validate_sizes()?;
Ok(index_record)
}
pub fn entries<'s, E>(&'s self) -> Result<NtfsIndexNodeEntries<'s, E>>
where
E: NtfsIndexEntryType,
{
let (entries_range, position) = self.entries_range_and_position();
let data = &self.record.data()[entries_range];
Ok(NtfsIndexNodeEntries::new(data, position))
}
fn entries_range_and_position(&self) -> (Range<usize>, NtfsPosition) {
let start = INDEX_RECORD_HEADER_SIZE as usize + self.index_entries_offset() as usize;
let end = INDEX_RECORD_HEADER_SIZE as usize + self.index_data_size() as usize;
let position = self.record.position() + start;
(start..end, position)
}
pub fn has_subnodes(&self) -> bool {
(self.node_header.flags & HAS_SUBNODES_FLAG) != 0
}
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()
}
pub(crate) fn index_entries_offset(&self) -> u32 {
self.node_header.entries_offset.get()
}
pub(crate) fn into_entry_ranges<E>(self) -> IndexNodeEntryRanges<E>
where
E: NtfsIndexEntryType,
{
let (entries_range, position) = self.entries_range_and_position();
IndexNodeEntryRanges::new(self.record.into_data(), entries_range, position)
}
fn validate_signature(record: &Record) -> Result<()> {
let signature = &record.signature();
let expected = b"INDX";
if signature == expected {
Ok(())
} else {
Err(NtfsError::InvalidIndexSignature {
position: record.position(),
expected,
actual: *signature,
})
}
}
fn validate_sizes(&self) -> Result<()> {
let index_record_size = self.record.len();
let total_allocated_size = INDEX_RECORD_HEADER_SIZE + self.index_allocated_size();
if total_allocated_size > index_record_size {
return Err(NtfsError::InvalidIndexAllocatedSize {
position: self.record.position(),
expected: index_record_size,
actual: total_allocated_size,
});
}
let total_data_size = INDEX_RECORD_HEADER_SIZE + self.index_data_size();
if total_data_size > total_allocated_size {
return Err(NtfsError::InvalidIndexUsedSize {
position: self.record.position(),
expected: total_allocated_size,
actual: total_data_size,
});
}
Ok(())
}
pub fn vcn(&self) -> Vcn {
Vcn::from(self.header.vcn.get())
}
}