use core::mem;
use arrayvec::ArrayVec;
use binrw::io::{Cursor, Read, Seek, SeekFrom};
use binrw::{BinRead, BinReaderExt};
use nt_string::u16strle::U16StrLe;
use crate::attribute::{NtfsAttribute, NtfsAttributeType};
use crate::attribute_value::{NtfsAttributeValue, NtfsNonResidentAttributeValue};
use crate::error::{NtfsError, Result};
use crate::file::NtfsFile;
use crate::file_reference::NtfsFileReference;
use crate::ntfs::Ntfs;
use crate::structured_values::NtfsStructuredValue;
use crate::traits::NtfsReadSeek;
use crate::types::{NtfsPosition, Vcn};
const ATTRIBUTE_LIST_ENTRY_HEADER_SIZE: usize = 26;
const NAME_MAX_SIZE: usize = (u8::MAX as usize) * mem::size_of::<u16>();
#[allow(unused)]
#[derive(BinRead, Clone, Debug)]
struct AttributeListEntryHeader {
ty: u32,
list_entry_length: u16,
name_length: u8,
name_offset: u8,
lowest_vcn: Vcn,
base_file_reference: NtfsFileReference,
instance: u16,
}
#[derive(Clone, Debug)]
pub enum NtfsAttributeList<'n, 'f> {
Resident(&'f [u8], NtfsPosition),
NonResident(NtfsNonResidentAttributeValue<'n, 'f>),
}
impl<'n, 'f> NtfsAttributeList<'n, 'f> {
pub fn entries(&self) -> NtfsAttributeListEntries<'n, 'f> {
NtfsAttributeListEntries::new(self.clone())
}
pub fn position(&self) -> NtfsPosition {
match self {
Self::Resident(_slice, position) => *position,
Self::NonResident(value) => value.data_position(),
}
}
}
impl<'n, 'f> NtfsStructuredValue<'n, 'f> for NtfsAttributeList<'n, 'f> {
const TY: NtfsAttributeType = NtfsAttributeType::AttributeList;
fn from_attribute_value<T>(_fs: &mut T, value: NtfsAttributeValue<'n, 'f>) -> Result<Self>
where
T: Read + Seek,
{
match value {
NtfsAttributeValue::Resident(value) => {
let slice = value.data();
let position = value.data_position();
Ok(Self::Resident(slice, position))
}
NtfsAttributeValue::NonResident(value) => Ok(Self::NonResident(value)),
NtfsAttributeValue::AttributeListNonResident(value) => {
let position = value.data_position();
Err(NtfsError::UnexpectedAttributeListAttribute { position })
}
}
}
}
#[derive(Clone, Debug)]
pub struct NtfsAttributeListEntries<'n, 'f> {
attribute_list: NtfsAttributeList<'n, 'f>,
}
impl<'n, 'f> NtfsAttributeListEntries<'n, 'f> {
fn new(attribute_list: NtfsAttributeList<'n, 'f>) -> Self {
Self { attribute_list }
}
pub fn next<T>(&mut self, fs: &mut T) -> Option<Result<NtfsAttributeListEntry>>
where
T: Read + Seek,
{
match &mut self.attribute_list {
NtfsAttributeList::Resident(slice, position) => Self::next_resident(slice, position),
NtfsAttributeList::NonResident(value) => Self::next_non_resident(fs, value),
}
}
fn next_non_resident<T>(
fs: &mut T,
value: &mut NtfsNonResidentAttributeValue<'n, 'f>,
) -> Option<Result<NtfsAttributeListEntry>>
where
T: Read + Seek,
{
if value.stream_position() >= value.len() {
return None;
}
let mut value_attached = value.clone().attach(fs);
let position = value.data_position();
let entry = iter_try!(NtfsAttributeListEntry::new(&mut value_attached, position));
iter_try!(value.seek(fs, SeekFrom::Current(entry.list_entry_length() as i64)));
Some(Ok(entry))
}
fn next_resident(
slice: &mut &'f [u8],
position: &mut NtfsPosition,
) -> Option<Result<NtfsAttributeListEntry>> {
if slice.is_empty() {
return None;
}
let mut cursor = Cursor::new(*slice);
let entry = iter_try!(NtfsAttributeListEntry::new(&mut cursor, *position));
let bytes_to_advance = entry.list_entry_length() as usize;
*slice = slice.get(bytes_to_advance..)?;
*position += bytes_to_advance;
Some(Ok(entry))
}
}
#[derive(Clone, Debug)]
pub struct NtfsAttributeListEntry {
header: AttributeListEntryHeader,
name: ArrayVec<u8, NAME_MAX_SIZE>,
position: NtfsPosition,
}
impl NtfsAttributeListEntry {
fn new<T>(r: &mut T, position: NtfsPosition) -> Result<Self>
where
T: Read + Seek,
{
let header = r.read_le::<AttributeListEntryHeader>()?;
let mut entry = Self {
header,
name: ArrayVec::from([0u8; NAME_MAX_SIZE]),
position,
};
entry.validate_entry_and_name_length()?;
entry.read_name(r)?;
Ok(entry)
}
pub fn base_file_reference(&self) -> NtfsFileReference {
self.header.base_file_reference
}
pub fn instance(&self) -> u16 {
self.header.instance
}
pub fn list_entry_length(&self) -> u16 {
self.header.list_entry_length
}
pub fn lowest_vcn(&self) -> Vcn {
self.header.lowest_vcn
}
pub fn name(&self) -> U16StrLe {
U16StrLe(&self.name)
}
pub fn name_length(&self) -> usize {
self.header.name_length as usize * mem::size_of::<u16>()
}
pub fn position(&self) -> NtfsPosition {
self.position
}
fn read_name<T>(&mut self, r: &mut T) -> Result<()>
where
T: Read + Seek,
{
debug_assert_eq!(self.name.len(), NAME_MAX_SIZE);
let name_length = self.name_length();
r.read_exact(&mut self.name[..name_length])?;
self.name.truncate(name_length);
Ok(())
}
pub fn to_attribute<'n, 'f>(&self, file: &'f NtfsFile<'n>) -> Result<NtfsAttribute<'n, 'f>> {
let file_record_number = self.base_file_reference().file_record_number();
assert_eq!(
file.file_record_number(),
file_record_number,
"The given NtfsFile's record number does not match the expected record number. \
Always use NtfsAttributeListEntry::to_file to retrieve the correct NtfsFile."
);
let instance = self.instance();
let ty = self.ty()?;
file.find_resident_attribute(ty, None, Some(instance))
}
pub fn to_file<'n, T>(&self, ntfs: &'n Ntfs, fs: &mut T) -> Result<NtfsFile<'n>>
where
T: Read + Seek,
{
let file_record_number = self.base_file_reference().file_record_number();
ntfs.file(fs, file_record_number)
}
pub fn ty(&self) -> Result<NtfsAttributeType> {
NtfsAttributeType::n(self.header.ty).ok_or(NtfsError::UnsupportedAttributeType {
position: self.position(),
actual: self.header.ty,
})
}
fn validate_entry_and_name_length(&self) -> Result<()> {
let total_size = ATTRIBUTE_LIST_ENTRY_HEADER_SIZE + self.name_length();
if total_size > self.list_entry_length() as usize {
return Err(NtfsError::InvalidStructuredValueSize {
position: self.position(),
ty: NtfsAttributeType::AttributeList,
expected: self.list_entry_length() as u64,
actual: total_size as u64,
});
}
Ok(())
}
}