diff options
author | Colin Finck <colin@reactos.org> | 2021-10-03 16:46:54 +0300 |
---|---|---|
committer | Colin Finck <colin@reactos.org> | 2021-10-03 16:46:54 +0300 |
commit | eaf86401ce5ee42153b70963fcbd67799926bf32 (patch) | |
tree | d3a14bc5db067448be77cbc11fadacbe519253d8 /src/file.rs | |
parent | c134deb4335f7234ab2d057e9928845e7710766a (diff) |
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.
Diffstat (limited to 'src/file.rs')
-rw-r--r-- | src/file.rs | 128 |
1 files changed, 91 insertions, 37 deletions
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 <colin@reactos.org> // 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<NtfsAttribute<'n, 'f>> { - 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::<NtfsIndexRoot>()?; + // 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::<NtfsIndexRoot>(Some( + directory_index_name, + ))?; - NtfsIndex::<NtfsFileNameIndex>::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::<NtfsFileNameIndex>::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<S> + 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::<S>() + } + 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<NtfsStandardInformation> { - let attribute = self.attribute_by_ty(NtfsAttributeType::StandardInformation)?; - attribute.resident_structured_value::<NtfsStandardInformation>() + self.find_resident_attribute_structured_value::<NtfsStandardInformation>(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<NtfsFileName> { - let attribute = self.attribute_by_ty(NtfsAttributeType::FileName)?; - attribute.resident_structured_value::<NtfsFileName>() + pub fn name<T>(&self, fs: &mut T) -> Result<NtfsFileName> + 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. |