From eaf86401ce5ee42153b70963fcbd67799926bf32 Mon Sep 17 00:00:00 2001 From: Colin Finck Date: Sun, 3 Oct 2021 15:46:54 +0200 Subject: Refactor structured values to work on any type of `NtfsValue`. This makes `NtfsFile::directory_index` and `NtfsFile::name` work even if the attributes they are looking for are part of an Attribute List. We keep a fast path for the few attribute types that are always resident. --- src/attribute.rs | 52 +++------ src/error.rs | 17 +-- src/file.rs | 128 +++++++++++++++------ src/index.rs | 38 ++++-- src/index_record.rs | 8 +- src/ntfs.rs | 13 +-- src/structured_values/attribute_list.rs | 42 ++++--- src/structured_values/file_name.rs | 93 +++++++++------ src/structured_values/index_allocation.rs | 54 +++++---- src/structured_values/index_root.rs | 59 +++++++--- src/structured_values/mod.rs | 28 +++-- src/structured_values/object_id.rs | 97 ++++++++++------ src/structured_values/standard_information.rs | 71 ++++++++---- src/structured_values/volume_information.rs | 59 +++++++--- src/structured_values/volume_name.rs | 76 ++++++++---- src/upcase_table.rs | 9 +- src/value/attribute_list_non_resident_attribute.rs | 4 + 17 files changed, 535 insertions(+), 313 deletions(-) diff --git a/src/attribute.rs b/src/attribute.rs index 8de17b6..567acd1 100644 --- a/src/attribute.rs +++ b/src/attribute.rs @@ -5,8 +5,8 @@ use crate::error::{NtfsError, Result}; use crate::file::NtfsFile; use crate::string::NtfsString; use crate::structured_values::{ - NtfsAttributeList, NtfsAttributeListEntries, NtfsStructuredValueFromNonResidentAttributeValue, - NtfsStructuredValueFromSlice, + NtfsAttributeList, NtfsAttributeListEntries, NtfsStructuredValue, + NtfsStructuredValueFromResidentAttributeValue, }; use crate::types::Vcn; use crate::value::attribute_list_non_resident_attribute::NtfsAttributeListNonResidentAttributeValue; @@ -206,28 +206,6 @@ impl<'n, 'f> NtfsAttribute<'n, 'f> { name_length_in_characters as usize * mem::size_of::() } - pub fn non_resident_structured_value(&self, fs: &mut T) -> Result - where - T: Read + Seek, - S: NtfsStructuredValueFromNonResidentAttributeValue<'n, 'f>, - { - let ty = self.ty()?; - if ty != S::TY { - return Err(NtfsError::StructuredValueOfDifferentType { - position: self.position(), - ty, - }); - } - - if self.is_resident() { - return Err(NtfsError::UnexpectedResidentAttribute { - position: self.position(), - }); - } - - S::from_non_resident_attribute_value(fs, self.non_resident_value()?) - } - pub(crate) fn non_resident_value(&self) -> Result> { let (data, position) = self.non_resident_value_data_and_position(); @@ -272,13 +250,14 @@ impl<'n, 'f> NtfsAttribute<'n, 'f> { pub fn resident_structured_value(&self) -> Result where - S: NtfsStructuredValueFromSlice<'f>, + S: NtfsStructuredValueFromResidentAttributeValue<'n, 'f>, { let ty = self.ty()?; if ty != S::TY { - return Err(NtfsError::StructuredValueOfDifferentType { + return Err(NtfsError::AttributeOfDifferentType { position: self.position(), - ty, + expected: S::TY, + actual: ty, }); } @@ -289,7 +268,7 @@ impl<'n, 'f> NtfsAttribute<'n, 'f> { } let resident_value = self.resident_value()?; - S::from_slice(resident_value.data(), self.position()) + S::from_resident_attribute_value(resident_value) } pub(crate) fn resident_value(&self) -> Result> { @@ -318,14 +297,18 @@ impl<'n, 'f> NtfsAttribute<'n, 'f> { pub fn structured_value(&self, fs: &mut T) -> Result where T: Read + Seek, - S: NtfsStructuredValueFromSlice<'f> - + NtfsStructuredValueFromNonResidentAttributeValue<'n, 'f>, + S: NtfsStructuredValue<'n, 'f>, { - if self.is_resident() { - self.resident_structured_value() - } else { - self.non_resident_structured_value(fs) + let ty = self.ty()?; + if ty != S::TY { + return Err(NtfsError::AttributeOfDifferentType { + position: self.position(), + expected: S::TY, + actual: ty, + }); } + + S::from_value(fs, self.value()?) } /// Returns the type of this NTFS attribute, or [`NtfsError::UnsupportedAttributeType`] @@ -514,6 +497,7 @@ impl<'n, 'f> NtfsAttributes<'n, 'f> { } } +#[derive(Clone, Debug)] pub struct NtfsAttributeItem<'n, 'f> { attribute_file: &'f NtfsFile<'n>, attribute_value_file: Option>, diff --git a/src/error.rs b/src/error.rs index 2d752bf..a0b6901 100644 --- a/src/error.rs +++ b/src/error.rs @@ -17,6 +17,12 @@ pub enum NtfsError { position: u64, ty: NtfsAttributeType, }, + /// The NTFS attribute at byte position {position:#010x} should have type {expected:?}, but it actually has type {actual:?} + AttributeOfDifferentType { + position: u64, + expected: NtfsAttributeType, + actual: NtfsAttributeType, + }, /// The given buffer should have at least {expected} bytes, but it only has {actual} bytes BufferTooSmall { expected: usize, actual: usize }, /// The NTFS attribute at byte position {position:#010x} indicates a name length up to offset {expected}, but the attribute only has a size of {actual} bytes @@ -107,8 +113,8 @@ pub enum NtfsError { InvalidStructuredValueSize { position: u64, ty: NtfsAttributeType, - expected: usize, - actual: usize, + expected: u64, + actual: u64, }, /// The given time can't be represented as an NtfsTime InvalidTime, @@ -134,11 +140,8 @@ pub enum NtfsError { MissingIndexAllocation { position: u64 }, /// The NTFS file at byte position {position:#010x} is not a directory. NotADirectory { position: u64 }, - /// The NTFS attribute at byte position {position:#010x} has type {ty:?}, but a different type has been requested - StructuredValueOfDifferentType { - position: u64, - ty: NtfsAttributeType, - }, + /// The NTFS attribute at byte position {position:#010x} should not belong to an Attribute List, but it does + UnexpectedAttributeListAttribute { position: u64 }, /// The NTFS attribute at byte position {position:#010x} should be resident, but it is non-resident UnexpectedNonResidentAttribute { position: u64 }, /// The NTFS attribute at byte position {position:#010x} should be non-resident, but it is resident diff --git a/src/file.rs b/src/file.rs index 147c986..fbece7e 100644 --- a/src/file.rs +++ b/src/file.rs @@ -1,9 +1,7 @@ // Copyright 2021 Colin Finck // SPDX-License-Identifier: GPL-2.0-or-later -use crate::attribute::{ - NtfsAttribute, NtfsAttributeItem, NtfsAttributeType, NtfsAttributes, NtfsAttributesRaw, -}; +use crate::attribute::{NtfsAttributeItem, NtfsAttributeType, NtfsAttributes, NtfsAttributesRaw}; use crate::error::{NtfsError, Result}; use crate::file_reference::NtfsFileReference; use crate::index::NtfsIndex; @@ -11,7 +9,8 @@ use crate::indexes::NtfsFileNameIndex; use crate::ntfs::Ntfs; use crate::record::{Record, RecordHeader}; use crate::structured_values::{ - NtfsFileName, NtfsIndexAllocation, NtfsIndexRoot, NtfsStandardInformation, + NtfsFileName, NtfsIndexRoot, NtfsStandardInformation, + NtfsStructuredValueFromResidentAttributeValue, }; use binread::io::{Read, Seek, SeekFrom}; use bitflags::bitflags; @@ -94,22 +93,6 @@ impl<'n> NtfsFile<'n> { LittleEndian::read_u32(&self.record.data()[start..]) } - /// Returns the first attribute of the given type, or `NtfsError::AttributeNotFound`. - pub(crate) fn attribute_by_ty<'f>( - &'f self, - ty: NtfsAttributeType, - ) -> Result> { - self.attributes_raw() - .find(|attribute| { - // TODO: Replace by attribute.ty().contains() once https://github.com/rust-lang/rust/issues/62358 has landed. - attribute.ty().map(|x| x == ty).unwrap_or(false) - }) - .ok_or(NtfsError::AttributeNotFound { - position: self.position(), - ty, - }) - } - /// This provides a flattened "data-centric" view of the attributes and abstracts away the filesystem details /// to deal with many or large attributes (Attribute Lists and split attributes). /// Use [`NtfsFile::attributes_raw`] to iterate over the plain attributes on the filesystem. @@ -179,20 +162,40 @@ impl<'n> NtfsFile<'n> { }); } - // Get the Index Root attribute that needs to exist. - let index_root = self - .attribute_by_ty(NtfsAttributeType::IndexRoot)? - .resident_structured_value::()?; + // A FILE record may contain multiple indexes, so we have to match the name of the directory index. + let directory_index_name = "$I30"; - // Get the Index Allocation attribute that is only required for large indexes. - let index_allocation_attribute = self.attribute_by_ty(NtfsAttributeType::IndexAllocation); - let index_allocation = if let Ok(attribute) = index_allocation_attribute { - Some(attribute.non_resident_structured_value::<_, NtfsIndexAllocation>(fs)?) - } else { - None - }; + // The IndexRoot attribute is always resident and has to exist for every directory. + let index_root = self.find_resident_attribute_structured_value::(Some( + directory_index_name, + ))?; - NtfsIndex::::new(index_root, index_allocation) + // The IndexAllocation attribute is only required for "large" indexes. + // It is always non-resident and may even be in an AttributeList. + let mut index_allocation_item = None; + if index_root.is_large_index() { + let mut iter = self.attributes(); + + while let Some(item) = iter.next(fs) { + let item = item?; + let attribute = item.to_attribute(); + + let ty = attribute.ty()?; + if ty != NtfsAttributeType::IndexAllocation { + continue; + } + + let name = attribute.name()?; + if name != directory_index_name { + continue; + } + + index_allocation_item = Some(item); + break; + } + } + + NtfsIndex::::new(index_root, index_allocation_item) } /// Returns the NTFS file record number of this file. @@ -203,6 +206,39 @@ impl<'n> NtfsFile<'n> { self.file_record_number } + /// Finds a resident attribute of a specific type, optionally with a specific name, and returns its structured value. + /// Returns `NtfsError::AttributeNotFound` if no such resident attribute could be found. + /// + /// The attribute type is given through the passed structured value type parameter. + pub(crate) fn find_resident_attribute_structured_value<'f, S>( + &'f self, + match_name: Option<&str>, + ) -> Result + where + S: NtfsStructuredValueFromResidentAttributeValue<'n, 'f>, + { + // Resident attributes are always stored on the top-level (we don't have to dig into Attribute Lists). + let attribute = self + .attributes_raw() + .find(|attribute| { + // TODO: Replace by attribute.ty().contains() once https://github.com/rust-lang/rust/issues/62358 has landed. + let ty_matches = attribute.ty().map(|x| x == S::TY).unwrap_or(false); + + let name_matches = if let Some(name) = match_name { + attribute.name().map(|x| x == name).unwrap_or(false) + } else { + true + }; + + ty_matches && name_matches + }) + .ok_or(NtfsError::AttributeNotFound { + position: self.position(), + ty: S::TY, + })?; + attribute.resident_structured_value::() + } + pub(crate) fn first_attribute_offset(&self) -> u16 { let start = offset_of!(FileRecordHeader, first_attribute_offset); LittleEndian::read_u16(&self.record.data()[start..]) @@ -225,8 +261,7 @@ impl<'n> NtfsFile<'n> { /// This internally calls [`NtfsFile::attributes`] to iterate through the file's /// attributes and pick up the first $STANDARD_INFORMATION attribute. pub fn info(&self) -> Result { - let attribute = self.attribute_by_ty(NtfsAttributeType::StandardInformation)?; - attribute.resident_structured_value::() + self.find_resident_attribute_structured_value::(None) } pub fn is_directory(&self) -> bool { @@ -237,9 +272,28 @@ impl<'n> NtfsFile<'n> { /// /// This internally calls [`NtfsFile::attributes`] to iterate through the file's /// attributes and pick up the first $FILE_NAME attribute. - pub fn name(&self) -> Result { - let attribute = self.attribute_by_ty(NtfsAttributeType::FileName)?; - attribute.resident_structured_value::() + pub fn name(&self, fs: &mut T) -> Result + where + T: Read + Seek, + { + let mut iter = self.attributes(); + + while let Some(item) = iter.next(fs) { + let item = item?; + let attribute = item.to_attribute(); + + let ty = attribute.ty()?; + if ty != NtfsAttributeType::FileName { + continue; + } + + return attribute.structured_value::<_, NtfsFileName>(fs); + } + + Err(NtfsError::AttributeNotFound { + position: self.position(), + ty: NtfsAttributeType::FileName, + }) } /// Returns the [`Ntfs`] object associated to this file. diff --git a/src/index.rs b/src/index.rs index 0814ecf..9239371 100644 --- a/src/index.rs +++ b/src/index.rs @@ -1,6 +1,7 @@ // Copyright 2021 Colin Finck // SPDX-License-Identifier: GPL-2.0-or-later +use crate::attribute::{NtfsAttributeItem, NtfsAttributeType}; use crate::error::{NtfsError, Result}; use crate::index_entry::{ IndexEntryRange, IndexNodeEntryRanges, NtfsIndexEntry, NtfsIndexEntryFlags, @@ -18,7 +19,7 @@ where E: NtfsIndexEntryType, { index_root: NtfsIndexRoot<'f>, - index_allocation: Option>, + index_allocation_item: Option>, entry_type: PhantomData, } @@ -28,9 +29,20 @@ where { pub fn new( index_root: NtfsIndexRoot<'f>, - index_allocation: Option>, + index_allocation_item: Option>, ) -> Result { - if index_root.is_large_index() && index_allocation.is_none() { + if let Some(item) = &index_allocation_item { + let attribute = item.to_attribute(); + let ty = attribute.ty()?; + + if ty != NtfsAttributeType::IndexAllocation { + return Err(NtfsError::AttributeOfDifferentType { + position: attribute.position(), + expected: NtfsAttributeType::IndexAllocation, + actual: ty, + }); + } + } else if index_root.is_large_index() { return Err(NtfsError::MissingIndexAllocation { position: index_root.position(), }); @@ -40,7 +52,7 @@ where Ok(Self { index_root, - index_allocation, + index_allocation_item, entry_type, }) } @@ -122,12 +134,17 @@ where // Does this entry have a subnode that needs to be iterated first? if let Some(subnode_vcn) = entry.subnode_vcn() { // Read the subnode from the filesystem and get an iterator for it. - let index_allocation = - iter_try!(self.index.index_allocation.as_ref().ok_or_else(|| { + let index_allocation_item = + iter_try!(self.index.index_allocation_item.as_ref().ok_or_else(|| { NtfsError::MissingIndexAllocation { position: self.index.index_root.position(), } })); + let index_allocation_attribute = index_allocation_item.to_attribute(); + let index_allocation = + iter_try!(index_allocation_attribute + .structured_value::<_, NtfsIndexAllocation>(fs)); + let subnode = iter_try!(index_allocation.record_from_vcn( fs, &self.index.index_root, @@ -241,12 +258,17 @@ where // it comes lexicographically AFTER what we're looking for. // In both cases, we have to continue iterating in the subnode of this entry (if there is any). let subnode_vcn = entry.subnode_vcn()?; - let index_allocation = - iter_try!(self.index.index_allocation.as_ref().ok_or_else(|| { + let index_allocation_item = + iter_try!(self.index.index_allocation_item.as_ref().ok_or_else(|| { NtfsError::MissingIndexAllocation { position: self.index.index_root.position(), } })); + let index_allocation_attribute = index_allocation_item.to_attribute(); + let index_allocation = iter_try!( + index_allocation_attribute.structured_value::<_, NtfsIndexAllocation>(fs) + ); + let subnode = iter_try!(index_allocation.record_from_vcn( fs, &self.index.index_root, diff --git a/src/index_record.rs b/src/index_record.rs index b05b6e4..4c4978a 100644 --- a/src/index_record.rs +++ b/src/index_record.rs @@ -4,11 +4,12 @@ use crate::error::{NtfsError, Result}; use crate::index_entry::{IndexNodeEntryRanges, NtfsIndexNodeEntries}; use crate::indexes::NtfsIndexEntryType; +use crate::ntfs::Ntfs; use crate::record::Record; use crate::record::RecordHeader; use crate::traits::NtfsReadSeek; use crate::types::Vcn; -use crate::value::non_resident_attribute::NtfsNonResidentAttributeValue; +use crate::value::NtfsValue; use binread::io::{Read, Seek}; use byteorder::{ByteOrder, LittleEndian}; use core::ops::Range; @@ -43,8 +44,9 @@ const HAS_SUBNODES_FLAG: u8 = 0x01; impl<'n> NtfsIndexRecord<'n> { pub(crate) fn new( + ntfs: &'n Ntfs, fs: &mut T, - mut value: NtfsNonResidentAttributeValue<'n, '_>, + mut value: NtfsValue<'n, '_>, index_record_size: u32, ) -> Result where @@ -57,7 +59,7 @@ impl<'n> NtfsIndexRecord<'n> { let mut data = vec![0; index_record_size as usize]; value.read_exact(fs, &mut data)?; - let mut record = Record::new(value.ntfs(), data, data_position); + let mut record = Record::new(ntfs, data, data_position); Self::validate_signature(&record)?; record.fixup()?; diff --git a/src/ntfs.rs b/src/ntfs.rs index 9ef8734..78fbd90 100644 --- a/src/ntfs.rs +++ b/src/ntfs.rs @@ -168,8 +168,7 @@ impl Ntfs { T: Read + Seek, { let volume_file = self.file(fs, KnownNtfsFileRecordNumber::Volume as u64)?; - let attribute = volume_file.attribute_by_ty(NtfsAttributeType::VolumeInformation)?; - attribute.resident_structured_value::() + volume_file.find_resident_attribute_structured_value::(None) } /// Returns an [`NtfsVolumeName`] to read the volume name (also called volume label) @@ -181,12 +180,12 @@ impl Ntfs { T: Read + Seek, { let volume_file = iter_try!(self.file(fs, KnownNtfsFileRecordNumber::Volume as u64)); - let attribute = volume_file - .attribute_by_ty(NtfsAttributeType::VolumeName) - .ok()?; - let volume_name = iter_try!(attribute.resident_structured_value::()); - Some(Ok(volume_name)) + match volume_file.find_resident_attribute_structured_value::(None) { + Ok(volume_name) => Some(Ok(volume_name)), + Err(NtfsError::AttributeNotFound { .. }) => None, + Err(e) => Some(Err(e)), + } } } diff --git a/src/structured_values/attribute_list.rs b/src/structured_values/attribute_list.rs index 334b17c..27b502f 100644 --- a/src/structured_values/attribute_list.rs +++ b/src/structured_values/attribute_list.rs @@ -7,13 +7,11 @@ use crate::file::NtfsFile; use crate::file_reference::NtfsFileReference; use crate::ntfs::Ntfs; use crate::string::NtfsString; -use crate::structured_values::{ - NtfsStructuredValue, NtfsStructuredValueFromNonResidentAttributeValue, - NtfsStructuredValueFromSlice, -}; +use crate::structured_values::NtfsStructuredValue; use crate::traits::NtfsReadSeek; use crate::types::Vcn; use crate::value::non_resident_attribute::NtfsNonResidentAttributeValue; +use crate::value::NtfsValue; use arrayvec::ArrayVec; use binread::io::{Cursor, Read, Seek, SeekFrom}; use binread::{BinRead, BinReaderExt}; @@ -66,27 +64,27 @@ impl<'n, 'f> NtfsAttributeList<'n, 'f> { } } -impl<'n, 'f> NtfsStructuredValue for NtfsAttributeList<'n, 'f> { +impl<'n, 'f> NtfsStructuredValue<'n, 'f> for NtfsAttributeList<'n, 'f> { const TY: NtfsAttributeType = NtfsAttributeType::AttributeList; -} -impl<'n, 'f> NtfsStructuredValueFromSlice<'f> for NtfsAttributeList<'n, 'f> { - fn from_slice(slice: &'f [u8], position: u64) -> Result { - Ok(Self::Resident(slice, position)) - } -} - -impl<'n, 'f> NtfsStructuredValueFromNonResidentAttributeValue<'n, 'f> - for NtfsAttributeList<'n, 'f> -{ - fn from_non_resident_attribute_value( - _fs: &mut T, - value: NtfsNonResidentAttributeValue<'n, 'f>, - ) -> Result + fn from_value(_fs: &mut T, value: NtfsValue<'n, 'f>) -> Result where T: Read + Seek, { - Ok(Self::NonResident(value)) + match value { + NtfsValue::Slice(value) => { + let slice = value.data(); + let position = value.data_position().unwrap(); + Ok(Self::Resident(slice, position)) + } + NtfsValue::NonResidentAttribute(value) => Ok(Self::NonResident(value)), + NtfsValue::AttributeListNonResidentAttribute(value) => { + // Attribute Lists are never nested. + // Hence, we must not create this attribute from an attribute that is already part of Attribute List. + let position = value.data_position().unwrap(); + Err(NtfsError::UnexpectedAttributeListAttribute { position }) + } + } } } @@ -270,8 +268,8 @@ impl NtfsAttributeListEntry { return Err(NtfsError::InvalidStructuredValueSize { position: self.position(), ty: NtfsAttributeType::AttributeList, - expected: self.list_entry_length() as usize, - actual: total_size, + expected: self.list_entry_length() as u64, + actual: total_size as u64, }); } diff --git a/src/structured_values/file_name.rs b/src/structured_values/file_name.rs index 22f104e..3628495 100644 --- a/src/structured_values/file_name.rs +++ b/src/structured_values/file_name.rs @@ -6,12 +6,11 @@ use crate::error::{NtfsError, Result}; use crate::file_reference::NtfsFileReference; use crate::indexes::NtfsIndexEntryKey; use crate::string::NtfsString; -use crate::structured_values::{ - NtfsFileAttributeFlags, NtfsStructuredValue, NtfsStructuredValueFromSlice, -}; +use crate::structured_values::{NtfsFileAttributeFlags, NtfsStructuredValue}; use crate::time::NtfsTime; +use crate::value::NtfsValue; use arrayvec::ArrayVec; -use binread::io::Cursor; +use binread::io::{Cursor, Read, Seek}; use binread::{BinRead, BinReaderExt}; use core::mem; use enumn::N; @@ -58,6 +57,32 @@ pub struct NtfsFileName { } impl NtfsFileName { + fn new(r: &mut T, position: u64, value_length: u64) -> Result + where + T: Read + Seek, + { + if value_length < FILE_NAME_MIN_SIZE as u64 { + return Err(NtfsError::InvalidStructuredValueSize { + position, + ty: NtfsAttributeType::FileName, + expected: FILE_NAME_MIN_SIZE as u64, + actual: value_length, + }); + } + + let header = r.read_le::()?; + + let mut file_name = Self { + header, + name: ArrayVec::from([0u8; NAME_MAX_SIZE]), + }; + file_name.validate_name_length(value_length, position)?; + file_name.validate_namespace(position)?; + file_name.read_name(r)?; + + Ok(file_name) + } + pub fn access_time(&self) -> NtfsTime { self.header.access_time } @@ -112,15 +137,21 @@ impl NtfsFileName { self.header.parent_directory_reference } - fn read_name(&mut self, data: &[u8]) { - debug_assert!(self.name.is_empty()); - let start = FILE_NAME_HEADER_SIZE; - let end = start + self.name_length(); - self.name.try_extend_from_slice(&data[start..end]).unwrap(); + fn read_name(&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(()) } - fn validate_name_length(&self, data_size: usize, position: u64) -> Result<()> { - let total_size = FILE_NAME_HEADER_SIZE + self.name_length(); + fn validate_name_length(&self, data_size: u64, position: u64) -> Result<()> { + let total_size = (FILE_NAME_HEADER_SIZE + self.name_length()) as u64; if total_size > data_size { return Err(NtfsError::InvalidStructuredValueSize { @@ -146,40 +177,28 @@ impl NtfsFileName { } } -impl NtfsStructuredValue for NtfsFileName { +impl<'n, 'f> NtfsStructuredValue<'n, 'f> for NtfsFileName { const TY: NtfsAttributeType = NtfsAttributeType::FileName; -} -impl<'s> NtfsStructuredValueFromSlice<'s> for NtfsFileName { - fn from_slice(slice: &'s [u8], position: u64) -> Result { - if slice.len() < FILE_NAME_MIN_SIZE { - return Err(NtfsError::InvalidStructuredValueSize { - position, - ty: NtfsAttributeType::FileName, - expected: FILE_NAME_MIN_SIZE, - actual: slice.len(), - }); - } - - let mut cursor = Cursor::new(slice); - let header = cursor.read_le::()?; + fn from_value(fs: &mut T, value: NtfsValue<'n, 'f>) -> Result + where + T: Read + Seek, + { + let position = value.data_position().unwrap(); + let value_length = value.len(); - let mut file_name = Self { - header, - name: ArrayVec::new(), - }; - file_name.validate_name_length(slice.len(), position)?; - file_name.validate_namespace(position)?; - file_name.read_name(slice); - - Ok(file_name) + let mut value_attached = value.attach(fs); + Self::new(&mut value_attached, position, value_length) } } // `NtfsFileName` is special in the regard that the index entry key has the same structure as the structured value. impl NtfsIndexEntryKey for NtfsFileName { fn key_from_slice(slice: &[u8], position: u64) -> Result { - Self::from_slice(slice, position) + let value_length = slice.len() as u64; + + let mut cursor = Cursor::new(slice); + Self::new(&mut cursor, position, value_length) } } @@ -209,7 +228,7 @@ mod tests { // Check the actual "file name" of the MFT. let file_name = attribute - .resident_structured_value::() + .structured_value::<_, NtfsFileName>(&mut testfs1) .unwrap(); let creation_time = file_name.creation_time(); diff --git a/src/structured_values/index_allocation.rs b/src/structured_values/index_allocation.rs index a062a79..0f6742d 100644 --- a/src/structured_values/index_allocation.rs +++ b/src/structured_values/index_allocation.rs @@ -4,25 +4,25 @@ use crate::attribute::NtfsAttributeType; use crate::error::{NtfsError, Result}; use crate::index_record::NtfsIndexRecord; +use crate::ntfs::Ntfs; use crate::structured_values::index_root::NtfsIndexRoot; -use crate::structured_values::{ - NtfsStructuredValue, NtfsStructuredValueFromNonResidentAttributeValue, -}; +use crate::structured_values::NtfsStructuredValue; use crate::traits::NtfsReadSeek; use crate::types::Vcn; -use crate::value::non_resident_attribute::NtfsNonResidentAttributeValue; +use crate::value::NtfsValue; use binread::io::{Read, Seek, SeekFrom}; use core::iter::FusedIterator; #[derive(Clone, Debug)] pub struct NtfsIndexAllocation<'n, 'f> { - value: NtfsNonResidentAttributeValue<'n, 'f>, + ntfs: &'n Ntfs, + value: NtfsValue<'n, 'f>, } impl<'n, 'f> NtfsIndexAllocation<'n, 'f> { pub fn iter(&self, index_root: &NtfsIndexRoot) -> NtfsIndexRecords<'n, 'f> { let index_record_size = index_root.index_record_size(); - NtfsIndexRecords::new(self.value.clone(), index_record_size) + NtfsIndexRecords::new(self.clone(), index_record_size) } pub fn record_from_vcn( @@ -36,24 +36,24 @@ impl<'n, 'f> NtfsIndexAllocation<'n, 'f> { { // Seek to the byte offset of the given VCN. let mut value = self.value.clone(); - let offset = vcn.offset(self.value.ntfs())?; + let offset = vcn.offset(self.ntfs)?; value.seek(fs, SeekFrom::Current(offset))?; if value.stream_position() >= value.len() { return Err(NtfsError::VcnOutOfBoundsInIndexAllocation { - position: self.value.position(), + position: self.value.data_position().unwrap(), vcn, }); } // Get the record. let index_record_size = index_root.index_record_size(); - let record = NtfsIndexRecord::new(fs, value, index_record_size)?; + let record = NtfsIndexRecord::new(self.ntfs, fs, value, index_record_size)?; // Validate that the VCN in the record is the requested one. if record.vcn() != vcn { return Err(NtfsError::VcnMismatchInIndexAllocation { - position: self.value.position(), + position: self.value.data_position().unwrap(), expected: vcn, actual: record.vcn(), }); @@ -63,34 +63,36 @@ impl<'n, 'f> NtfsIndexAllocation<'n, 'f> { } } -impl<'n, 'f> NtfsStructuredValue for NtfsIndexAllocation<'n, 'f> { +impl<'n, 'f> NtfsStructuredValue<'n, 'f> for NtfsIndexAllocation<'n, 'f> { const TY: NtfsAttributeType = NtfsAttributeType::IndexAllocation; -} -impl<'n, 'f> NtfsStructuredValueFromNonResidentAttributeValue<'n, 'f> - for NtfsIndexAllocation<'n, 'f> -{ - fn from_non_resident_attribute_value( - _fs: &mut T, - value: NtfsNonResidentAttributeValue<'n, 'f>, - ) -> Result + fn from_value(_fs: &mut T, value: NtfsValue<'n, 'f>) -> Result where T: Read + Seek, { - Ok(Self { value }) + let ntfs = match &value { + NtfsValue::AttributeListNonResidentAttribute(value) => value.ntfs(), + NtfsValue::NonResidentAttribute(value) => value.ntfs(), + NtfsValue::Slice(_) => { + let position = value.data_position().unwrap(); + return Err(NtfsError::UnexpectedResidentAttribute { position }); + } + }; + + Ok(Self { ntfs, value }) } } #[derive(Clone, Debug)] pub struct NtfsIndexRecords<'n, 'f> { - value: NtfsNonResidentAttributeValue<'n, 'f>, + index_allocation: NtfsIndexAllocation<'n, 'f>, index_record_size: u32, } impl<'n, 'f> NtfsIndexRecords<'n, 'f> { - fn new(value: NtfsNonResidentAttributeValue<'n, 'f>, index_record_size: u32) -> Self { + fn new(index_allocation: NtfsIndexAllocation<'n, 'f>, index_record_size: u32) -> Self { Self { - value, + index_allocation, index_record_size, } } @@ -106,19 +108,21 @@ impl<'n, 'f> NtfsIndexRecords<'n, 'f> { where T: Read + Seek, { - if self.value.stream_position() >= self.value.len() { + if self.index_allocation.value.stream_position() >= self.index_allocation.value.len() { return None; } // Get the current record. let record = iter_try!(NtfsIndexRecord::new( + self.index_allocation.ntfs, fs, - self.value.clone(), + self.index_allocation.value.clone(), self.index_record_size )); // Advance our iterator to the next record. iter_try!(self + .index_allocation .value .seek(fs, SeekFrom::Current(self.index_record_size as i64))); diff --git a/src/structured_values/index_root.rs b/src/structured_values/index_root.rs index cae761a..9c1cb2f 100644 --- a/src/structured_values/index_root.rs +++ b/src/structured_values/index_root.rs @@ -6,7 +6,12 @@ use crate::error::{NtfsError, Result}; use crate::index_entry::{IndexNodeEntryRanges, NtfsIndexNodeEntries}; use crate::index_record::{IndexNodeHeader, INDEX_NODE_HEADER_SIZE}; use crate::indexes::NtfsIndexEntryType; -use crate::structured_values::{NtfsStructuredValue, NtfsStructuredValueFromSlice}; +use crate::structured_values::{ + NtfsStructuredValue, NtfsStructuredValueFromResidentAttributeValue, +}; +use crate::value::slice::NtfsSliceValue; +use crate::value::NtfsValue; +use binread::io::{Read, Seek}; use byteorder::{ByteOrder, LittleEndian}; use core::ops::Range; use memoffset::offset_of; @@ -31,6 +36,22 @@ pub struct NtfsIndexRoot<'f> { const LARGE_INDEX_FLAG: u8 = 0x01; impl<'f> NtfsIndexRoot<'f> { + fn new(slice: &'f [u8], position: u64) -> Result { + 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 index_root = Self { slice, position }; + index_root.validate_sizes()?; + + Ok(index_root) + } + pub fn entries(&self) -> Result> where E: NtfsIndexEntryType, @@ -115,24 +136,28 @@ impl<'f> NtfsIndexRoot<'f> { } } -impl<'f> NtfsStructuredValue for NtfsIndexRoot<'f> { +impl<'n, 'f> NtfsStructuredValue<'n, 'f> for NtfsIndexRoot<'f> { const TY: NtfsAttributeType = NtfsAttributeType::IndexRoot; -} -impl<'f> NtfsStructuredValueFromSlice<'f> for NtfsIndexRoot<'f> { - fn from_slice(slice: &'f [u8], position: u64) -> Result { - if slice.len() < INDEX_ROOT_HEADER_SIZE + INDEX_NODE_HEADER_SIZE { - return Err(NtfsError::InvalidStructuredValueSize { - position, - ty: NtfsAttributeType::IndexRoot, - expected: INDEX_ROOT_HEADER_SIZE, - actual: slice.len(), - }); - } - - let index_root = Self { slice, position }; - index_root.validate_sizes()?; + fn from_value(_fs: &mut T, value: NtfsValue<'n, 'f>) -> Result + where + T: Read + Seek, + { + let slice_value = match value { + NtfsValue::Slice(slice_value) => slice_value, + _ => { + let position = value.data_position().unwrap(); + return Err(NtfsError::UnexpectedNonResidentAttribute { position }); + } + }; + + let position = slice_value.data_position().unwrap(); + Self::new(slice_value.data(), position) + } +} - Ok(index_root) +impl<'n, 'f> NtfsStructuredValueFromResidentAttributeValue<'n, 'f> for NtfsIndexRoot<'f> { + fn from_resident_attribute_value(value: NtfsSliceValue<'f>) -> Result { + Self::new(value.data(), value.data_position().unwrap()) } } diff --git a/src/structured_values/mod.rs b/src/structured_values/mod.rs index 68971f4..c965516 100644 --- a/src/structured_values/mod.rs +++ b/src/structured_values/mod.rs @@ -23,7 +23,8 @@ pub use volume_name::*; use crate::attribute::NtfsAttributeType; use crate::error::Result; -use crate::value::non_resident_attribute::NtfsNonResidentAttributeValue; +use crate::value::slice::NtfsSliceValue; +use crate::value::NtfsValue; use binread::io::{Read, Seek}; use bitflags::bitflags; @@ -46,22 +47,19 @@ bitflags! { } } -pub trait NtfsStructuredValue: Sized { +pub trait NtfsStructuredValue<'n, 'f>: Sized { const TY: NtfsAttributeType; -} - -/// Create a structured value from an arbitrary data slice. -/// This handles Resident Attributes of File Records AND Keys of Index Records (when an attribute is indexed). -pub trait NtfsStructuredValueFromSlice<'s>: NtfsStructuredValue { - fn from_slice(slice: &'s [u8], position: u64) -> Result; -} -/// Create a structured value from a Non-Resident Attribute Value. -pub trait NtfsStructuredValueFromNonResidentAttributeValue<'n, 'f>: NtfsStructuredValue { - fn from_non_resident_attribute_value( - fs: &mut T, - value: NtfsNonResidentAttributeValue<'n, 'f>, - ) -> Result + /// Create a structured value from an arbitrary `NtfsValue`. + fn from_value(fs: &mut T, value: NtfsValue<'n, 'f>) -> Result where T: Read + Seek; } + +/// Create a structured value from an arbitrary data slice. +/// This is a fast path for the few structured values that are always in resident attributes. +pub trait NtfsStructuredValueFromResidentAttributeValue<'n, 'f>: + NtfsStructuredValue<'n, 'f> +{ + fn from_resident_attribute_value(value: NtfsSliceValue<'f>) -> Result; +} diff --git a/src/structured_values/object_id.rs b/src/structured_values/object_id.rs index 1a327f3..1efec3a 100644 --- a/src/structured_values/object_id.rs +++ b/src/structured_values/object_id.rs @@ -4,8 +4,12 @@ use crate::attribute::NtfsAttributeType; use crate::error::{NtfsError, Result}; use crate::guid::{NtfsGuid, GUID_SIZE}; -use crate::structured_values::{NtfsStructuredValue, NtfsStructuredValueFromSlice}; -use binread::io::Cursor; +use crate::structured_values::{ + NtfsStructuredValue, NtfsStructuredValueFromResidentAttributeValue, +}; +use crate::value::slice::NtfsSliceValue; +use crate::value::NtfsValue; +use binread::io::{Cursor, Read, Seek}; use binread::BinReaderExt; #[derive(Clone, Debug)] @@ -17,54 +21,34 @@ pub struct NtfsObjectId { } impl NtfsObjectId { - pub fn birth_object_id(&self) -> Option<&NtfsGuid> { - self.birth_object_id.as_ref() - } - - pub fn birth_volume_id(&self) -> Option<&NtfsGuid> { - self.birth_volume_id.as_ref() - } - - pub fn domain_id(&self) -> Option<&NtfsGuid> { - self.domain_id.as_ref() - } - - pub fn object_id(&self) -> &NtfsGuid { - &self.object_id - } -} - -impl NtfsStructuredValue for NtfsObjectId { - const TY: NtfsAttributeType = NtfsAttributeType::ObjectId; -} - -impl<'s> NtfsStructuredValueFromSlice<'s> for NtfsObjectId { - fn from_slice(slice: &'s [u8], position: u64) -> Result { - if slice.len() < GUID_SIZE { + fn new(r: &mut T, position: u64, value_length: u64) -> Result + where + T: Read + Seek, + { + if value_length < GUID_SIZE as u64 { return Err(NtfsError::InvalidStructuredValueSize { position, ty: NtfsAttributeType::ObjectId, - expected: GUID_SIZE, - actual: slice.len(), + expected: GUID_SIZE as u64, + actual: value_length, }); } - let mut cursor = Cursor::new(slice); - let object_id = cursor.read_le::()?; + let object_id = r.read_le::()?; let mut birth_volume_id = None; - if slice.len() >= 2 * GUID_SIZE { - birth_volume_id = Some(cursor.read_le::()?); + if value_length >= 2 * GUID_SIZE as u64 { + birth_volume_id = Some(r.read_le::()?); } let mut birth_object_id = None; - if slice.len() >= 3 * GUID_SIZE { - birth_object_id = Some(cursor.read_le::()?); + if value_length >= 3 * GUID_SIZE as u64 { + birth_object_id = Some(r.read_le::()?); } let mut domain_id = None; - if slice.len() >= 4 * GUID_SIZE { - domain_id = Some(cursor.read_le::()?); + if value_length >= 4 * GUID_SIZE as u64 { + domain_id = Some(r.read_le::()?); } Ok(Self { @@ -74,4 +58,45 @@ impl<'s> NtfsStructuredValueFromSlice<'s> for NtfsObjectId { domain_id, }) } + + pub fn birth_object_id(&self) -> Option<&NtfsGuid> { + self.birth_object_id.as_ref() + } + + pub fn birth_volume_id(&self) -> Option<&NtfsGuid> { + self.birth_volume_id.as_ref() + } + + pub fn domain_id(&self) -> Option<&NtfsGuid> { + self.domain_id.as_ref() + } + + pub fn object_id(&self) -> &NtfsGuid { + &self.object_id + } +} + +impl<'n, 'f> NtfsStructuredValue<'n, 'f> for NtfsObjectId { + const TY: NtfsAttributeType = NtfsAttributeType::ObjectId; + + fn from_value(fs: &mut T, value: NtfsValue<'n, 'f>) -> Result + where + T: Read + Seek, + { + let position = value.data_position().unwrap(); + let value_length = value.len(); + + let mut value_attached = value.attach(fs); + Self::new(&mut value_attached, position, value_length) + } +} + +impl<'n, 'f> NtfsStructuredValueFromResidentAttributeValue<'n, 'f> for NtfsObjectId { + fn from_resident_attribute_value(value: NtfsSliceValue<'f>) -> Result { + let position = value.data_position().unwrap(); + let value_length = value.len(); + + let mut cursor = Cursor::new(value.data()); + Self::new(&mut cursor, position, value_length) + } } diff --git a/src/structured_values/standard_information.rs b/src/structured_values/standard_information.rs index bcd1cee..01a1771 100644 --- a/src/structured_values/standard_information.rs +++ b/src/structured_values/standard_information.rs @@ -4,10 +4,12 @@ use crate::attribute::NtfsAttributeType; use crate::error::{NtfsError, Result}; use crate::structured_values::{ - NtfsFileAttributeFlags, NtfsStructuredValue, NtfsStructuredValueFromSlice, + NtfsFileAttributeFlags, NtfsStructuredValue, NtfsStructuredValueFromResidentAttributeValue, }; use crate::time::NtfsTime; -use binread::io::Cursor; +use crate::value::slice::NtfsSliceValue; +use crate::value::NtfsValue; +use binread::io::{Cursor, Read, Seek}; use binread::{BinRead, BinReaderExt}; /// Size of all [`StandardInformationData`] fields plus some reserved bytes. @@ -43,6 +45,32 @@ pub struct NtfsStandardInformation { } impl NtfsStandardInformation { + fn new(r: &mut T, position: u64, value_length: u64) -> Result + where + T: Read + Seek, + { + if value_length < STANDARD_INFORMATION_SIZE_NTFS1 as u64 { + return Err(NtfsError::InvalidStructuredValueSize { + position, + ty: NtfsAttributeType::StandardInformation, + expected: STANDARD_INFORMATION_SIZE_NTFS1 as u64, + actual: value_length, + }); + } + + let ntfs1_data = r.read_le::()?; + + let mut ntfs3_data = None; + if value_length >= STANDARD_INFORMATION_SIZE_NTFS3 as u64 { + ntfs3_data = Some(r.read_le::()?); + } + + Ok(Self { + ntfs1_data, + ntfs3_data, + }) + } + pub fn access_time(&self) -> NtfsTime { self.ntfs1_data.access_time } @@ -92,33 +120,28 @@ impl NtfsStandardInformation { } } -impl NtfsStructuredValue for NtfsStandardInformation { +impl<'n, 'f> NtfsStructuredValue<'n, 'f> for NtfsStandardInformation { const TY: NtfsAttributeType = NtfsAttributeType::StandardInformation; -} -impl<'s> NtfsStructuredValueFromSlice<'s> for NtfsStandardInformation { - fn from_slice(slice: &'s [u8], position: u64) -> Result { - if slice.len() < STANDARD_INFORMATION_SIZE_NTFS1 { - return Err(NtfsError::InvalidStructuredValueSize { - position, - ty: NtfsAttributeType::StandardInformation, - expected: STANDARD_INFORMATION_SIZE_NTFS1, - actual: slice.len(), - }); - } + fn from_value(fs: &mut T, value: NtfsValue<'n, 'f>) -> Result + where + T: Read + Seek, + { + let position = value.data_position().unwrap(); + let value_length = value.len(); - let mut cursor = Cursor::new(slice); - let ntfs1_data = cursor.read_le::()?; + let mut value_attached = value.attach(fs); + Self::new(&mut value_attached, position, value_length) + } +} - let mut ntfs3_data = None; - if slice.len() >= STANDARD_INFORMATION_SIZE_NTFS3 { - ntfs3_data = Some(cursor.read_le::()?); - } +impl<'n, 'f> NtfsStructuredValueFromResidentAttributeValue<'n, 'f> for NtfsStandardInformation { + fn from_resident_attribute_value(value: NtfsSliceValue<'f>) -> Result { + let position = value.data_position().unwrap(); + let value_length = value.len(); - Ok(Self { - ntfs1_data, - ntfs3_data, - }) + let mut cursor = Cursor::new(value.data()); + Self::new(&mut cursor, position, value_length) } } diff --git a/src/structured_values/volume_information.rs b/src/structured_values/volume_information.rs index 4550bc1..ea235ac 100644 --- a/src/structured_values/volume_information.rs +++ b/src/structured_values/volume_information.rs @@ -3,8 +3,12 @@ use crate::attribute::NtfsAttributeType; use crate::error::{NtfsError, Result}; -use crate::structured_values::{NtfsStructuredValue, NtfsStructuredValueFromSlice}; -use binread::io::Cursor; +use crate::structured_values::{ + NtfsStructuredValue, NtfsStructuredValueFromResidentAttributeValue, +}; +use crate::value::slice::NtfsSliceValue; +use crate::value::NtfsValue; +use binread::io::{Cursor, Read, Seek}; use binread::{BinRead, BinReaderExt}; use bitflags::bitflags; @@ -39,6 +43,24 @@ pub struct NtfsVolumeInformation { } impl NtfsVolumeInformation { + fn new(r: &mut T, position: u64, value_length: u64) -> Result + where + T: Read + Seek, + { + if value_length < VOLUME_INFORMATION_SIZE as u64 { + return Err(NtfsError::InvalidStructuredValueSize { + position, + ty: NtfsAttributeType::StandardInformation, + expected: VOLUME_INFORMATION_SIZE as u64, + actual: value_length, + }); + } + + let info = r.read_le::()?; + + Ok(Self { info }) + } + pub fn flags(&self) -> NtfsVolumeFlags { NtfsVolumeFlags::from_bits_truncate(self.info.flags) } @@ -52,24 +74,27 @@ impl NtfsVolumeInformation { } } -impl NtfsStructuredValue for NtfsVolumeInformation { +impl<'n, 'f> NtfsStructuredValue<'n, 'f> for NtfsVolumeInformation { const TY: NtfsAttributeType = NtfsAttributeType::VolumeInformation; -} -impl<'s> NtfsStructuredValueFromSlice<'s> for NtfsVolumeInformation { - fn from_slice(slice: &'s [u8], position: u64) -> Result { - if slice.len() < VOLUME_INFORMATION_SIZE { - return Err(NtfsError::InvalidStructuredValueSize { - position, - ty: NtfsAttributeType::StandardInformation, - expected: VOLUME_INFORMATION_SIZE, - actual: slice.len(), - }); - } + fn from_value(fs: &mut T, value: NtfsValue<'n, 'f>) -> Result + where + T: Read + Seek, + { + let position = value.data_position().unwrap(); + let value_length = value.len(); - let mut cursor = Cursor::new(slice); - let info = cursor.read_le::()?; + let mut value_attached = value.attach(fs); + Self::new(&mut value_attached, position, value_length) + } +} - Ok(Self { info }) +impl<'n, 'f> NtfsStructuredValueFromResidentAttributeValue<'n, 'f> for NtfsVolumeInformation { + fn from_resident_attribute_value(value: NtfsSliceValue<'f>) -> Result { + let position = value.data_position().unwrap(); + let value_length = value.len(); + + let mut cursor = Cursor::new(value.data()); + Self::new(&mut cursor, position, value_length) } } diff --git a/src/structured_values/volume_name.rs b/src/structured_values/volume_name.rs index 91ca1a2..ce51386 100644 --- a/src/structured_values/volume_name.rs +++ b/src/structured_values/volume_name.rs @@ -4,8 +4,13 @@ use crate::attribute::NtfsAttributeType; use crate::error::{NtfsError, Result}; use crate::string::NtfsString; -use crate::structured_values::{NtfsStructuredValue, NtfsStructuredValueFromSlice}; +use crate::structured_values::{ + NtfsStructuredValue, NtfsStructuredValueFromResidentAttributeValue, +}; +use crate::value::slice::NtfsSliceValue; +use crate::value::NtfsValue; use arrayvec::ArrayVec; +use binread::io::{Cursor, Read, Seek}; use core::mem; /// The smallest VolumeName attribute has a name containing just a single character. @@ -20,6 +25,35 @@ pub struct NtfsVolumeName { } impl NtfsVolumeName { + fn new(r: &mut T, position: u64, value_length: u64) -> Result + where + T: Read + Seek, + { + if value_length < VOLUME_NAME_MIN_SIZE as u64 { + return Err(NtfsError::InvalidStructuredValueSize { + position, + ty: NtfsAttributeType::VolumeName, + expected: VOLUME_NAME_MIN_SIZE as u64, + actual: value_length, + }); + } else if value_length > VOLUME_NAME_MAX_SIZE as u64 { + return Err(NtfsError::InvalidStructuredValueSize { + position, + ty: NtfsAttributeType::VolumeName, + expected: VOLUME_NAME_MAX_SIZE as u64, + actual: value_length, + }); + } + + let value_length = value_length as usize; + + let mut name = ArrayVec::from([0u8; VOLUME_NAME_MAX_SIZE]); + r.read_exact(&mut name[..value_length])?; + name.truncate(value_length); + + Ok(Self { name }) + } + /// Gets the file name and returns it wrapped in an [`NtfsString`]. pub fn name<'s>(&'s self) -> NtfsString<'s> { NtfsString(&self.name) @@ -33,31 +67,27 @@ impl NtfsVolumeName { } } -impl NtfsStructuredValue for NtfsVolumeName { +impl<'n, 'f> NtfsStructuredValue<'n, 'f> for NtfsVolumeName { const TY: NtfsAttributeType = NtfsAttributeType::VolumeName; -} -impl<'s> NtfsStructuredValueFromSlice<'s> for NtfsVolumeName { - fn from_slice(slice: &'s [u8], position: u64) -> Result { - if slice.len() < VOLUME_NAME_MIN_SIZE { - return Err(NtfsError::InvalidStructuredValueSize { - position, - ty: NtfsAttributeType::VolumeName, - expected: VOLUME_NAME_MIN_SIZE, - actual: slice.len(), - }); - } else if slice.len() > VOLUME_NAME_MAX_SIZE { - return Err(NtfsError::InvalidStructuredValueSize { - position, - ty: NtfsAttributeType::VolumeName, - expected: VOLUME_NAME_MAX_SIZE, - actual: slice.len(), - }); - } + fn from_value(fs: &mut T, value: NtfsValue<'n, 'f>) -> Result + where + T: Read + Seek, + { + let position = value.data_position().unwrap(); + let value_length = value.len(); - let mut name = ArrayVec::new(); - name.try_extend_from_slice(slice).unwrap(); + let mut value_attached = value.attach(fs); + Self::new(&mut value_attached, position, value_length) + } +} - Ok(Self { name }) +impl<'n, 'f> NtfsStructuredValueFromResidentAttributeValue<'n, 'f> for NtfsVolumeName { + fn from_resident_attribute_value(value: NtfsSliceValue<'f>) -> Result { + let position = value.data_position().unwrap(); + let value_length = value.len(); + + let mut cursor = Cursor::new(value.data()); + Self::new(&mut cursor, position, value_length) } } diff --git a/src/upcase_table.rs b/src/upcase_table.rs index 29796c7..5451656 100644 --- a/src/upcase_table.rs +++ b/src/upcase_table.rs @@ -36,7 +36,14 @@ impl UpcaseTable { { // Lookup the $UpCase file and its $DATA attribute. let upcase_file = ntfs.file(fs, KnownNtfsFileRecordNumber::UpCase as u64)?; - let data_attribute = upcase_file.attribute_by_ty(NtfsAttributeType::Data)?; + let data_item = upcase_file + .data(fs, "") + .ok_or(NtfsError::AttributeNotFound { + position: upcase_file.position(), + ty: NtfsAttributeType::Data, + })??; + + let data_attribute = data_item.to_attribute(); if data_attribute.value_length() != UPCASE_TABLE_SIZE { return Err(NtfsError::InvalidUpcaseTableSize { expected: UPCASE_TABLE_SIZE, diff --git a/src/value/attribute_list_non_resident_attribute.rs b/src/value/attribute_list_non_resident_attribute.rs index 730b094..8e75724 100644 --- a/src/value/attribute_list_non_resident_attribute.rs +++ b/src/value/attribute_list_non_resident_attribute.rs @@ -150,6 +150,10 @@ impl<'n, 'f> NtfsAttributeListNonResidentAttributeValue<'n, 'f> { Ok(true) } + + pub fn ntfs(&self) -> &'n Ntfs { + self.ntfs + } } impl<'n, 'f> NtfsReadSeek for NtfsAttributeListNonResidentAttributeValue<'n, 'f> { -- cgit v1.2.3