use core::cmp::Ordering;
use core::iter::FusedIterator;
use core::marker::PhantomData;
use core::ops::Range;
use core::{fmt, mem};
use alloc::vec::Vec;
use bitflags::bitflags;
use zerocopy::byteorder::LittleEndian;
use zerocopy::{FromBytes, Immutable, KnownLayout, U16, U32, Unaligned};
use crate::error::{NtfsError, Result};
use crate::file::NtfsFile;
use crate::file_reference::NtfsFileReference;
use crate::helpers::pod_from_prefix;
use crate::indexes::{
NtfsIndexEntryData, NtfsIndexEntryHasData, NtfsIndexEntryHasFileReference, NtfsIndexEntryKey,
NtfsIndexEntryType,
};
use crate::io::{Read, Seek};
use crate::ntfs::Ntfs;
use crate::types::NtfsPosition;
use crate::types::Vcn;
const INDEX_ENTRY_HEADER_SIZE: usize = 16;
#[derive(Clone, Copy, Debug, FromBytes, Immutable, KnownLayout, Unaligned)]
#[repr(C, packed)]
struct IndexEntryHeader {
data_offset: U16<LittleEndian>,
data_length: U16<LittleEndian>,
padding: U32<LittleEndian>,
index_entry_length: U16<LittleEndian>,
key_length: U16<LittleEndian>,
flags: u8,
reserved: [u8; 3],
}
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>,
header: IndexEntryHeader,
position: NtfsPosition,
entry_type: PhantomData<E>,
}
impl<E> IndexEntryRange<E>
where
E: NtfsIndexEntryType,
{
fn new(range: Range<usize>, header: IndexEntryHeader, position: NtfsPosition) -> Self {
let entry_type = PhantomData;
Self {
range,
header,
position,
entry_type,
}
}
pub(crate) fn to_entry<'s>(&self, slice: &'s [u8]) -> Result<NtfsIndexEntry<'s, E>> {
let slice = slice
.get(self.range.clone())
.ok_or(NtfsError::InvalidIndexEntrySize {
position: self.position,
expected: self.header.index_entry_length.get(),
actual: slice.len() as u16,
})?;
Ok(NtfsIndexEntry::from_validated(
slice,
self.header,
self.position,
))
}
}
#[derive(Clone, Debug)]
pub struct NtfsIndexEntry<'s, E>
where
E: NtfsIndexEntryType,
{
slice: &'s [u8],
header: IndexEntryHeader,
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 header = pod_from_prefix::<IndexEntryHeader, INDEX_ENTRY_HEADER_SIZE>(slice).ok_or(
NtfsError::InvalidIndexEntrySize {
position,
expected: INDEX_ENTRY_HEADER_SIZE as u16,
actual: slice.len() as u16,
},
)?;
let entry_type = PhantomData;
let mut entry = Self {
slice,
header,
position,
entry_type,
};
entry.validate_size()?;
entry.slice = &entry.slice[..entry.index_entry_length() as usize];
Ok(entry)
}
fn from_validated(slice: &'s [u8], header: IndexEntryHeader, position: NtfsPosition) -> Self {
debug_assert_eq!(slice.len(), header.index_entry_length.get() as usize);
Self {
slice,
header,
position,
entry_type: PhantomData,
}
}
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,
{
self.header.data_offset.get()
}
pub fn data_length(&self) -> u16
where
E: NtfsIndexEntryHasData,
{
self.header.data_length.get()
}
pub fn file_reference(&self) -> NtfsFileReference
where
E: NtfsIndexEntryHasFileReference,
{
pod_from_prefix::<NtfsFileReference, { mem::size_of::<NtfsFileReference>() }>(self.slice)
.expect("the index entry header has a validated size")
}
pub fn flags(&self) -> NtfsIndexEntryFlags {
NtfsIndexEntryFlags::from_bits_truncate(self.header.flags)
}
pub fn index_entry_length(&self) -> u16 {
self.header.index_entry_length.get()
}
pub fn key(&self) -> Option<Result<E::KeyType>> {
let (slice, position) = iter_try!(self.key_slice()?);
let key = iter_try!(E::KeyType::key_from_slice(slice, position));
Some(Ok(key))
}
pub(crate) fn key_slice(&self) -> Option<Result<(&'s [u8], NtfsPosition)>> {
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
}));
Some(Ok((slice, position)))
}
pub fn key_length(&self) -> u16 {
self.header.key_length.get()
}
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 = pod_from_prefix::<Vcn, { mem::size_of::<Vcn>() }>(slice)
.expect("the subnode VCN slice has a validated size");
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<()> {
let index_entry_length = self.index_entry_length();
if (index_entry_length as usize) < INDEX_ENTRY_HEADER_SIZE {
return Err(NtfsError::InvalidIndexEntrySize {
position: self.position,
expected: INDEX_ENTRY_HEADER_SIZE as u16,
actual: index_entry_length,
});
}
if index_entry_length as usize > self.slice.len() {
return Err(NtfsError::InvalidIndexEntrySize {
position: self.position,
expected: 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>,
}
#[derive(Clone, Debug)]
pub(crate) struct IndexNodeEntryIndex<E>
where
E: NtfsIndexEntryType,
{
entries: Vec<IndexEntryRange<E>>,
}
impl<E> IndexNodeEntryIndex<E>
where
E: NtfsIndexEntryType,
{
pub(crate) fn find_by<F>(
&self,
data: &[u8],
cmp: &F,
) -> Option<Result<IndexNodeSearchResult<E>>>
where
F: Fn(&[u8], NtfsPosition) -> Result<Ordering>,
{
let last_entry_index = self.entries.len().checked_sub(1)?;
let mut left = 0usize;
let mut right = last_entry_index;
while left < right {
let middle = left + (right - left) / 2;
let entry_range = &self.entries[middle];
let entry = iter_try!(entry_range.to_entry(data));
let (key, key_position) = iter_try!(
entry
.key_slice()
.expect("only the last index entry may omit its key")
);
match iter_try!(cmp(key, key_position)) {
Ordering::Equal => {
return Some(Ok(IndexNodeSearchResult::Found(entry_range.clone())));
}
Ordering::Less => right = middle,
Ordering::Greater => left = middle + 1,
}
}
let entry_range = &self.entries[left];
let entry = iter_try!(entry_range.to_entry(data));
if let Some(key) = entry.key_slice() {
let (key, key_position) = iter_try!(key);
if iter_try!(cmp(key, key_position)) == Ordering::Equal {
return Some(Ok(IndexNodeSearchResult::Found(entry_range.clone())));
}
}
let vcn = iter_try!(entry.subnode_vcn()?);
Some(Ok(IndexNodeSearchResult::Subnode {
vcn,
position: entry.position(),
}))
}
}
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
}
pub(crate) fn into_data(self) -> Vec<u8> {
self.data
}
pub(crate) fn entry_index(&self) -> Result<IndexNodeEntryIndex<E>> {
let mut count = 0usize;
let mut range = self.range.clone();
let mut position = self.position;
while !range.is_empty() {
let entry = NtfsIndexEntry::<E>::new(&self.data[range.start..], position)?;
count += 1;
if entry.flags().contains(NtfsIndexEntryFlags::LAST_ENTRY) {
break;
}
let entry_length = entry.index_entry_length() as usize;
range.start += entry_length;
position += entry_length;
}
let mut entries = Vec::with_capacity(count);
let mut range = self.range.clone();
let mut position = self.position;
while entries.len() < count {
let start = range.start;
let entry = NtfsIndexEntry::<E>::new(&self.data[start..], position)?;
let entry_length = entry.index_entry_length() as usize;
entries.push(IndexEntryRange::new(
start..start + entry_length,
entry.header,
position,
));
range.start += entry_length;
position += entry_length;
}
Ok(IndexNodeEntryIndex { entries })
}
}
pub(crate) enum IndexNodeSearchResult<E>
where
E: NtfsIndexEntryType,
{
Found(IndexEntryRange<E>),
Subnode { vcn: Vcn, position: NtfsPosition },
}
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, entry.header, 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 {}