Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/windirstat/ntfs.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Finck <colin@reactos.org>2021-10-03 16:46:54 +0300
committerColin Finck <colin@reactos.org>2021-10-03 16:46:54 +0300
commiteaf86401ce5ee42153b70963fcbd67799926bf32 (patch)
treed3a14bc5db067448be77cbc11fadacbe519253d8
parentc134deb4335f7234ab2d057e9928845e7710766a (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.
-rw-r--r--src/attribute.rs52
-rw-r--r--src/error.rs17
-rw-r--r--src/file.rs128
-rw-r--r--src/index.rs38
-rw-r--r--src/index_record.rs8
-rw-r--r--src/ntfs.rs13
-rw-r--r--src/structured_values/attribute_list.rs42
-rw-r--r--src/structured_values/file_name.rs93
-rw-r--r--src/structured_values/index_allocation.rs54
-rw-r--r--src/structured_values/index_root.rs59
-rw-r--r--src/structured_values/mod.rs28
-rw-r--r--src/structured_values/object_id.rs97
-rw-r--r--src/structured_values/standard_information.rs71
-rw-r--r--src/structured_values/volume_information.rs59
-rw-r--r--src/structured_values/volume_name.rs76
-rw-r--r--src/upcase_table.rs9
-rw-r--r--src/value/attribute_list_non_resident_attribute.rs4
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::<u16>()
}
- pub fn non_resident_structured_value<T, S>(&self, fs: &mut T) -> Result<S>
- 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<NtfsNonResidentAttributeValue<'n, 'f>> {
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<S>(&self) -> Result<S>
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<NtfsSliceValue<'f>> {
@@ -318,14 +297,18 @@ impl<'n, 'f> NtfsAttribute<'n, 'f> {
pub fn structured_value<T, S>(&self, fs: &mut T) -> Result<S>
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<NtfsFile<'n>>,
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 <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.
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 <colin@reactos.org>
// 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<NtfsIndexAllocation<'n, 'f>>,
+ index_allocation_item: Option<NtfsAttributeItem<'n, 'f>>,
entry_type: PhantomData<E>,
}
@@ -28,9 +29,20 @@ where
{
pub fn new(
index_root: NtfsIndexRoot<'f>,
- index_allocation: Option<NtfsIndexAllocation<'n, 'f>>,
+ index_allocation_item: Option<NtfsAttributeItem<'n, 'f>>,
) -> Result<Self> {
- 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<T>(
+ ntfs: &'n Ntfs,
fs: &mut T,
- mut value: NtfsNonResidentAttributeValue<'n, '_>,
+ mut value: NtfsValue<'n, '_>,
index_record_size: u32,
) -> Result<Self>
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::<NtfsVolumeInformation>()
+ volume_file.find_resident_attribute_structured_value::<NtfsVolumeInformation>(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::<NtfsVolumeName>());
- Some(Ok(volume_name))
+ match volume_file.find_resident_attribute_structured_value::<NtfsVolumeName>(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<Self> {
- Ok(Self::Resident(slice, position))
- }
-}
-
-impl<'n, 'f> NtfsStructuredValueFromNonResidentAttributeValue<'n, 'f>
- for NtfsAttributeList<'n, 'f>
-{
- fn from_non_resident_attribute_value<T>(
- _fs: &mut T,
- value: NtfsNonResidentAttributeValue<'n, 'f>,
- ) -> Result<Self>
+ fn from_value<T>(_fs: &mut T, value: NtfsValue<'n, 'f>) -> Result<Self>
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<T>(r: &mut T, position: u64, value_length: u64) -> Result<Self>
+ 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::<FileNameHeader>()?;
+
+ 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<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(())
}
- 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<Self> {
- 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::<FileNameHeader>()?;
+ fn from_value<T>(fs: &mut T, value: NtfsValue<'n, 'f>) -> Result<Self>
+ 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> {
- 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::<NtfsFileName>()
+ .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<T>(
@@ -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<T>(
- _fs: &mut T,
- value: NtfsNonResidentAttributeValue<'n, 'f>,
- ) -> Result<Self>
+ fn from_value<T>(_fs: &mut T, value: NtfsValue<'n, 'f>) -> Result<Self>
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<Self> {
+ 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<E>(&self) -> Result<NtfsIndexNodeEntries<'f, E>>
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<Self> {
- 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<T>(_fs: &mut T, value: NtfsValue<'n, 'f>) -> Result<Self>
+ 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> {
+ 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<Self>;
-}
-/// Create a structured value from a Non-Resident Attribute Value.
-pub trait NtfsStructuredValueFromNonResidentAttributeValue<'n, 'f>: NtfsStructuredValue {
- fn from_non_resident_attribute_value<T>(
- fs: &mut T,
- value: NtfsNonResidentAttributeValue<'n, 'f>,
- ) -> Result<Self>
+ /// Create a structured value from an arbitrary `NtfsValue`.
+ fn from_value<T>(fs: &mut T, value: NtfsValue<'n, 'f>) -> Result<Self>
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<Self>;
+}
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<Self> {
- if slice.len() < GUID_SIZE {
+ fn new<T>(r: &mut T, position: u64, value_length: u64) -> Result<Self>
+ 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::<NtfsGuid>()?;
+ let object_id = r.read_le::<NtfsGuid>()?;
let mut birth_volume_id = None;
- if slice.len() >= 2 * GUID_SIZE {
- birth_volume_id = Some(cursor.read_le::<NtfsGuid>()?);
+ if value_length >= 2 * GUID_SIZE as u64 {
+ birth_volume_id = Some(r.read_le::<NtfsGuid>()?);
}
let mut birth_object_id = None;
- if slice.len() >= 3 * GUID_SIZE {
- birth_object_id = Some(cursor.read_le::<NtfsGuid>()?);
+ if value_length >= 3 * GUID_SIZE as u64 {
+ birth_object_id = Some(r.read_le::<NtfsGuid>()?);
}
let mut domain_id = None;
- if slice.len() >= 4 * GUID_SIZE {
- domain_id = Some(cursor.read_le::<NtfsGuid>()?);
+ if value_length >= 4 * GUID_SIZE as u64 {
+ domain_id = Some(r.read_le::<NtfsGuid>()?);
}
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<T>(fs: &mut T, value: NtfsValue<'n, 'f>) -> Result<Self>
+ 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<Self> {
+ 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<T>(r: &mut T, position: u64, value_length: u64) -> Result<Self>
+ 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::<StandardInformationDataNtfs1>()?;
+
+ let mut ntfs3_data = None;
+ if value_length >= STANDARD_INFORMATION_SIZE_NTFS3 as u64 {
+ ntfs3_data = Some(r.read_le::<StandardInformationDataNtfs3>()?);
+ }
+
+ 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<Self> {
- 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<T>(fs: &mut T, value: NtfsValue<'n, 'f>) -> Result<Self>
+ 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::<StandardInformationDataNtfs1>()?;
+ 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::<StandardInformationDataNtfs3>()?);
- }
+impl<'n, 'f> NtfsStructuredValueFromResidentAttributeValue<'n, 'f> for NtfsStandardInformation {
+ fn from_resident_attribute_value(value: NtfsSliceValue<'f>) -> Result<Self> {
+ 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<T>(r: &mut T, position: u64, value_length: u64) -> Result<Self>
+ 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::<VolumeInformationData>()?;
+
+ 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<Self> {
- if slice.len() < VOLUME_INFORMATION_SIZE {
- return Err(NtfsError::InvalidStructuredValueSize {
- position,
- ty: NtfsAttributeType::StandardInformation,
- expected: VOLUME_INFORMATION_SIZE,
- actual: slice.len(),
- });
- }
+ fn from_value<T>(fs: &mut T, value: NtfsValue<'n, 'f>) -> Result<Self>
+ 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::<VolumeInformationData>()?;
+ 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<Self> {
+ 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<T>(r: &mut T, position: u64, value_length: u64) -> Result<Self>
+ 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<Self> {
- 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<T>(fs: &mut T, value: NtfsValue<'n, 'f>) -> Result<Self>
+ 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<Self> {
+ 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> {