use core::iter::FusedIterator;
use core::marker::PhantomData;
use core::ops::Range;
use core::{fmt, mem};
use alloc::vec::Vec;
use binrw::io::{Read, Seek};
use bitflags::bitflags;
use byteorder::{ByteOrder, LittleEndian};
use memoffset::offset_of;
use crate::error::{NtfsError, Result};
use crate::file::NtfsFile;
use crate::file_reference::NtfsFileReference;
use crate::indexes::{
NtfsIndexEntryData, NtfsIndexEntryHasData, NtfsIndexEntryHasFileReference, NtfsIndexEntryKey,
NtfsIndexEntryType,
};
use crate::ntfs::Ntfs;
use crate::types::NtfsPosition;
use crate::types::Vcn;
const INDEX_ENTRY_HEADER_SIZE: usize = 16;
#[repr(C, packed)]
struct IndexEntryHeader {
data_offset: u16,
data_length: u16,
padding: u32,
index_entry_length: u16,
key_length: u16,
flags: u8,
}
bitflags! {
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct NtfsIndexEntryFlags: u8 {
const HAS_SUBNODE = 0x01;
const LAST_ENTRY = 0x02;
}
}
impl fmt::Display for NtfsIndexEntryFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
#[derive(Clone, Debug)]
pub(crate) struct IndexEntryRange<E>
where
E: NtfsIndexEntryType,
{
range: Range<usize>,
position: NtfsPosition,
entry_type: PhantomData<E>,
}
impl<E> IndexEntryRange<E>
where
E: NtfsIndexEntryType,
{
pub(crate) fn new(range: Range<usize>, position: NtfsPosition) -> Self {
let entry_type = PhantomData;
Self {
range,
position,
entry_type,
}
}
pub(crate) fn to_entry<'s>(&self, slice: &'s [u8]) -> Result<NtfsIndexEntry<'s, E>> {
NtfsIndexEntry::new(&slice[self.range.clone()], self.position)
}
}
#[derive(Clone, Debug)]
pub struct NtfsIndexEntry<'s, E>
where
E: NtfsIndexEntryType,
{
slice: &'s [u8],
position: NtfsPosition,
entry_type: PhantomData<E>,
}
impl<'s, E> NtfsIndexEntry<'s, E>
where
E: NtfsIndexEntryType,
{
pub(crate) fn new(slice: &'s [u8], position: NtfsPosition) -> Result<Self> {
let entry_type = PhantomData;
let mut entry = Self {
slice,
position,
entry_type,
};
entry.validate_size()?;
entry.slice = &entry.slice[..entry.index_entry_length() as usize];
Ok(entry)
}
pub fn data(&self) -> Option<Result<E::DataType>>
where
E: NtfsIndexEntryHasData,
{
if self.data_offset() == 0 || self.data_length() == 0 {
return None;
}
let start = self.data_offset() as usize;
let end = start + self.data_length() as usize;
let position = self.position + start;
let slice = self.slice.get(start..end);
let slice = iter_try!(slice.ok_or(NtfsError::InvalidIndexEntryDataRange {
position: self.position,
range: start..end,
size: self.slice.len() as u16
}));
let data = iter_try!(E::DataType::data_from_slice(slice, position));
Some(Ok(data))
}
fn data_offset(&self) -> u16
where
E: NtfsIndexEntryHasData,
{
let start = offset_of!(IndexEntryHeader, data_offset);
LittleEndian::read_u16(&self.slice[start..])
}
pub fn data_length(&self) -> u16
where
E: NtfsIndexEntryHasData,
{
let start = offset_of!(IndexEntryHeader, data_length);
LittleEndian::read_u16(&self.slice[start..])
}
pub fn file_reference(&self) -> NtfsFileReference
where
E: NtfsIndexEntryHasFileReference,
{
NtfsFileReference::new(self.slice[..mem::size_of::<u64>()].try_into().unwrap())
}
pub fn flags(&self) -> NtfsIndexEntryFlags {
let flags = self.slice[offset_of!(IndexEntryHeader, flags)];
NtfsIndexEntryFlags::from_bits_truncate(flags)
}
pub fn index_entry_length(&self) -> u16 {
let start = offset_of!(IndexEntryHeader, index_entry_length);
LittleEndian::read_u16(&self.slice[start..])
}
pub fn key(&self) -> Option<Result<E::KeyType>> {
if self.key_length() == 0 || self.flags().contains(NtfsIndexEntryFlags::LAST_ENTRY) {
return None;
}
let start = INDEX_ENTRY_HEADER_SIZE;
let end = start + self.key_length() as usize;
let position = self.position + start;
let slice = self.slice.get(start..end);
let slice = iter_try!(slice.ok_or(NtfsError::InvalidIndexEntryDataRange {
position: self.position,
range: start..end,
size: self.slice.len() as u16
}));
let key = iter_try!(E::KeyType::key_from_slice(slice, position));
Some(Ok(key))
}
pub fn key_length(&self) -> u16 {
let start = offset_of!(IndexEntryHeader, key_length);
LittleEndian::read_u16(&self.slice[start..])
}
pub fn position(&self) -> NtfsPosition {
self.position
}
pub fn subnode_vcn(&self) -> Option<Result<Vcn>> {
if !self.flags().contains(NtfsIndexEntryFlags::HAS_SUBNODE) {
return None;
}
let start = usize::max(
self.index_entry_length() as usize - mem::size_of::<Vcn>(),
INDEX_ENTRY_HEADER_SIZE,
);
let end = start + mem::size_of::<Vcn>();
let slice = self.slice.get(start..end);
let slice = iter_try!(slice.ok_or(NtfsError::InvalidIndexEntryDataRange {
position: self.position,
range: start..end,
size: self.slice.len() as u16
}));
let vcn = Vcn::from(LittleEndian::read_i64(slice));
Some(Ok(vcn))
}
pub fn to_file<'n, T>(&self, ntfs: &'n Ntfs, fs: &mut T) -> Result<NtfsFile<'n>>
where
E: NtfsIndexEntryHasFileReference,
T: Read + Seek,
{
self.file_reference().to_file(ntfs, fs)
}
fn validate_size(&self) -> Result<()> {
if self.slice.len() < INDEX_ENTRY_HEADER_SIZE {
return Err(NtfsError::InvalidIndexEntrySize {
position: self.position,
expected: INDEX_ENTRY_HEADER_SIZE as u16,
actual: self.slice.len() as u16,
});
}
if self.index_entry_length() as usize > self.slice.len() {
return Err(NtfsError::InvalidIndexEntrySize {
position: self.position,
expected: self.index_entry_length(),
actual: self.slice.len() as u16,
});
}
Ok(())
}
}
#[derive(Clone, Debug)]
pub(crate) struct IndexNodeEntryRanges<E>
where
E: NtfsIndexEntryType,
{
data: Vec<u8>,
range: Range<usize>,
position: NtfsPosition,
entry_type: PhantomData<E>,
}
impl<E> IndexNodeEntryRanges<E>
where
E: NtfsIndexEntryType,
{
pub(crate) fn new(data: Vec<u8>, range: Range<usize>, position: NtfsPosition) -> Self {
debug_assert!(range.end <= data.len());
let entry_type = PhantomData;
Self {
data,
range,
position,
entry_type,
}
}
pub(crate) fn data(&self) -> &[u8] {
&self.data
}
}
impl<E> Iterator for IndexNodeEntryRanges<E>
where
E: NtfsIndexEntryType,
{
type Item = Result<IndexEntryRange<E>>;
fn next(&mut self) -> Option<Self::Item> {
if self.range.is_empty() {
return None;
}
let start = self.range.start;
let position = self.position;
let entry = iter_try!(NtfsIndexEntry::<E>::new(&self.data[start..], position));
let end = start + entry.index_entry_length() as usize;
if entry.flags().contains(NtfsIndexEntryFlags::LAST_ENTRY) {
self.range.start = self.data.len();
} else {
self.range.start = end;
self.position += entry.index_entry_length();
}
Some(Ok(IndexEntryRange::new(start..end, position)))
}
}
impl<E> FusedIterator for IndexNodeEntryRanges<E> where E: NtfsIndexEntryType {}
#[derive(Clone, Debug)]
pub struct NtfsIndexNodeEntries<'s, E>
where
E: NtfsIndexEntryType,
{
slice: &'s [u8],
position: NtfsPosition,
entry_type: PhantomData<E>,
}
impl<'s, E> NtfsIndexNodeEntries<'s, E>
where
E: NtfsIndexEntryType,
{
pub(crate) fn new(slice: &'s [u8], position: NtfsPosition) -> Self {
let entry_type = PhantomData;
Self {
slice,
position,
entry_type,
}
}
}
impl<'s, E> Iterator for NtfsIndexNodeEntries<'s, E>
where
E: NtfsIndexEntryType,
{
type Item = Result<NtfsIndexEntry<'s, E>>;
fn next(&mut self) -> Option<Self::Item> {
if self.slice.is_empty() {
return None;
}
let entry = iter_try!(NtfsIndexEntry::new(self.slice, self.position));
if entry.flags().contains(NtfsIndexEntryFlags::LAST_ENTRY) {
self.slice = &[];
} else {
let bytes_to_advance = entry.index_entry_length() as usize;
self.slice = &self.slice[bytes_to_advance..];
self.position += bytes_to_advance;
}
Some(Ok(entry))
}
}
impl<'s, E> FusedIterator for NtfsIndexNodeEntries<'s, E> where E: NtfsIndexEntryType {}