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:
Diffstat (limited to 'src/attribute.rs')
-rw-r--r--src/attribute.rs456
1 files changed, 223 insertions, 233 deletions
diff --git a/src/attribute.rs b/src/attribute.rs
index bb28ce7..6655adb 100644
--- a/src/attribute.rs
+++ b/src/attribute.rs
@@ -1,27 +1,27 @@
// Copyright 2021 Colin Finck <colin@reactos.org>
// SPDX-License-Identifier: GPL-2.0-or-later
-use crate::attribute_value::{NtfsAttributeNonResidentValue, NtfsAttributeValue, NtfsDataRun};
+use crate::attribute_value::{
+ NtfsAttributeValue, NtfsNonResidentAttributeValue, NtfsResidentAttributeValue,
+};
use crate::error::{NtfsError, Result};
-use crate::ntfs::Ntfs;
use crate::ntfs_file::NtfsFile;
use crate::string::NtfsString;
use crate::structured_values::{
- NewNtfsStructuredValue, NtfsFileName, NtfsIndexAllocation, NtfsIndexRoot, NtfsObjectId,
- NtfsStandardInformation, NtfsStructuredValue, NtfsVolumeInformation, NtfsVolumeName,
+ NtfsStructuredValueFromData, NtfsStructuredValueFromNonResidentAttributeValue,
};
use crate::types::Vcn;
-use binread::io::{Read, Seek, SeekFrom};
-use binread::{BinRead, BinReaderExt};
+use binread::io::{Read, Seek};
use bitflags::bitflags;
+use byteorder::{ByteOrder, LittleEndian};
use core::iter::FusedIterator;
use core::mem;
use core::ops::Range;
use enumn::N;
+use memoffset::offset_of;
/// On-disk structure of the generic header of an NTFS attribute.
-#[allow(unused)]
-#[derive(BinRead, Debug)]
+#[repr(C, packed)]
struct NtfsAttributeHeader {
/// Type of the attribute, known types are in [`NtfsAttributeType`].
ty: u32,
@@ -39,12 +39,6 @@ struct NtfsAttributeHeader {
instance: u16,
}
-impl NtfsAttributeHeader {
- fn is_resident(&self) -> bool {
- self.is_non_resident == 0
- }
-}
-
bitflags! {
pub struct NtfsAttributeFlags: u16 {
/// The attribute value is compressed.
@@ -57,9 +51,9 @@ bitflags! {
}
/// On-disk structure of the extra header of an NTFS attribute that has a resident value.
-#[allow(unused)]
-#[derive(BinRead, Debug)]
-struct NtfsAttributeResidentHeader {
+#[repr(C, packed)]
+struct NtfsResidentAttributeHeader {
+ attribute_header: NtfsAttributeHeader,
/// Length of the value, in bytes.
value_length: u32,
/// Offset to the beginning of the value, in bytes from the beginning of the [`NtfsAttributeHeader`].
@@ -69,9 +63,9 @@ struct NtfsAttributeResidentHeader {
}
/// On-disk structure of the extra header of an NTFS attribute that has a non-resident value.
-#[allow(unused)]
-#[derive(BinRead, Debug)]
-struct NtfsAttributeNonResidentHeader {
+#[repr(C, packed)]
+struct NtfsNonResidentAttributeHeader {
+ attribute_header: NtfsAttributeHeader,
/// Lower boundary of Virtual Cluster Numbers (VCNs) referenced by this attribute.
/// This becomes relevant when file data is split over multiple attributes.
/// Otherwise, it's zero.
@@ -122,59 +116,14 @@ pub enum NtfsAttributeType {
}
#[derive(Debug)]
-enum NtfsAttributeExtraHeader {
- Resident(NtfsAttributeResidentHeader),
- NonResident(NtfsAttributeNonResidentHeader),
-}
-
-impl NtfsAttributeExtraHeader {
- fn new<T>(fs: &mut T, header: &NtfsAttributeHeader) -> Result<Self>
- where
- T: Read + Seek,
- {
- if header.is_resident() {
- // Read the resident header.
- let resident_header = fs.read_le::<NtfsAttributeResidentHeader>()?;
- Ok(Self::Resident(resident_header))
- } else {
- // Read the non-resident header.
- let non_resident_header = fs.read_le::<NtfsAttributeNonResidentHeader>()?;
- Ok(Self::NonResident(non_resident_header))
- }
- }
-}
-
-#[derive(Debug)]
-pub struct NtfsAttribute<'n> {
- ntfs: &'n Ntfs,
- position: u64,
- header: NtfsAttributeHeader,
- extra_header: NtfsAttributeExtraHeader,
+pub struct NtfsAttribute<'n, 'f> {
+ file: &'f NtfsFile<'n>,
+ offset: usize,
}
-impl<'n> NtfsAttribute<'n> {
- fn new<T>(ntfs: &'n Ntfs, fs: &mut T, position: u64) -> Result<Self>
- where
- T: Read + Seek,
- {
- // Read the common header for resident and non-resident attributes.
- fs.seek(SeekFrom::Start(position))?;
- let header = fs.read_le::<NtfsAttributeHeader>()?;
-
- // This must be a real attribute and not an end marker!
- // The caller must have already checked for potential end markers.
- debug_assert!(header.ty != NtfsAttributeType::End as u32);
-
- // Read the extra header specific to the attribute type.
- let extra_header = NtfsAttributeExtraHeader::new(fs, &header)?;
-
- let attribute = Self {
- ntfs,
- position,
- header,
- extra_header,
- };
- Ok(attribute)
+impl<'n, 'f> NtfsAttribute<'n, 'f> {
+ fn new(file: &'f NtfsFile<'n>, offset: usize) -> Self {
+ Self { file, offset }
}
/// Returns the length of this NTFS attribute, in bytes.
@@ -183,18 +132,47 @@ impl<'n> NtfsAttribute<'n> {
/// Apart from various headers, this structure also includes the name and,
/// for resident attributes, the actual value.
pub fn attribute_length(&self) -> u32 {
- self.header.length
+ let start = self.offset + offset_of!(NtfsAttributeHeader, length);
+ LittleEndian::read_u32(&self.file.record_data()[start..])
}
/// Returns flags set for this attribute as specified by [`NtfsAttributeFlags`].
pub fn flags(&self) -> NtfsAttributeFlags {
- NtfsAttributeFlags::from_bits_truncate(self.header.flags)
+ let start = self.offset + offset_of!(NtfsAttributeHeader, flags);
+ NtfsAttributeFlags::from_bits_truncate(LittleEndian::read_u16(
+ &self.file.record_data()[start..],
+ ))
}
/// Returns `true` if this is a resident attribute, i.e. one where its value
/// is part of the attribute structure.
pub fn is_resident(&self) -> bool {
- self.header.is_resident()
+ let start = self.offset + offset_of!(NtfsAttributeHeader, is_non_resident);
+ let is_non_resident = self.file.record_data()[start];
+ is_non_resident == 0
+ }
+
+ /// Gets the name of this NTFS attribute (if any) and returns it wrapped in an [`NtfsString`].
+ ///
+ /// Note that most NTFS attributes have no name and are distinguished by their types.
+ /// Use [`NtfsAttribute::ty`] to get the attribute type.
+ pub fn name(&self) -> Option<Result<NtfsString<'f>>> {
+ if self.name_offset() == 0 || self.name_length() == 0 {
+ return None;
+ }
+
+ iter_try!(self.validate_name_sizes());
+
+ let start = self.offset + self.name_offset() as usize;
+ let end = start + self.name_length();
+ let string = NtfsString(&self.file.record_data()[start..end]);
+
+ Some(Ok(string))
+ }
+
+ fn name_offset(&self) -> u16 {
+ let start = self.offset + offset_of!(NtfsAttributeHeader, name_offset);
+ LittleEndian::read_u16(&self.file.record_data()[start..])
}
/// Returns the length of the name of this NTFS attribute, in bytes.
@@ -203,213 +181,225 @@ impl<'n> NtfsAttribute<'n> {
/// It is always part of the attribute itself and hence also of the length
/// returned by [`NtfsAttribute::attribute_length`].
pub fn name_length(&self) -> usize {
- self.header.name_length as usize * mem::size_of::<u16>()
- }
-
- /// Returns the absolute position of this NTFS attribute within the filesystem, in bytes.
- pub fn position(&self) -> u64 {
- self.position
+ let start = self.offset + offset_of!(NtfsAttributeHeader, name_length);
+ let name_length_in_characters = self.file.record_data()[start];
+ name_length_in_characters as usize * mem::size_of::<u16>()
}
- /// Reads the name of this NTFS attribute into the given buffer, and returns an
- /// [`NtfsString`] wrapping that buffer.
- ///
- /// Note that most NTFS attributes have no name and are distinguished by their types.
- /// Use [`NtfsAttribute::ty`] to get the attribute type.
- pub fn read_name<'a, T>(&self, fs: &mut T, buf: &'a mut [u8]) -> Result<NtfsString<'a>>
+ pub fn non_resident_structured_value<T, S>(&self, fs: &mut T) -> Result<S>
where
T: Read + Seek,
+ S: NtfsStructuredValueFromNonResidentAttributeValue<'n, 'f>,
{
- let name_position = self.position + self.header.name_offset as u64;
- fs.seek(SeekFrom::Start(name_position))?;
- NtfsString::from_reader(fs, self.name_length(), buf)
+ 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()?)
+ }
+
+ fn non_resident_value(&self) -> Result<NtfsNonResidentAttributeValue<'n, 'f>> {
+ debug_assert!(!self.is_resident());
+ let start = self.offset + self.non_resident_value_data_runs_offset() as usize;
+ let end = start + self.attribute_length() as usize;
+ let data = &self.file.record_data()[start..end];
+ let position = self.file.position() + start as u64;
+
+ NtfsNonResidentAttributeValue::new(
+ self.file.ntfs(),
+ data,
+ position,
+ self.non_resident_value_data_size(),
+ )
+ }
+
+ fn non_resident_value_data_size(&self) -> u64 {
+ debug_assert!(!self.is_resident());
+ let start = self.offset + offset_of!(NtfsNonResidentAttributeHeader, data_size);
+ LittleEndian::read_u64(&self.file.record_data()[start..])
}
- pub fn structured_value<T>(&self, fs: &mut T) -> Result<NtfsStructuredValue<'n>>
+ fn non_resident_value_data_runs_offset(&self) -> u16 {
+ debug_assert!(!self.is_resident());
+ let start = self.offset + offset_of!(NtfsNonResidentAttributeHeader, data_runs_offset);
+ LittleEndian::read_u16(&self.file.record_data()[start..])
+ }
+
+ /// Returns the absolute position of this NTFS attribute within the filesystem, in bytes.
+ pub fn position(&self) -> u64 {
+ self.file.position() + self.offset as u64
+ }
+
+ pub fn resident_structured_value<S>(&self) -> Result<S>
where
- T: Read + Seek,
+ S: NtfsStructuredValueFromData<'f>,
{
- let value = self.value(fs)?;
- let length = value.len();
-
- match self.ty()? {
- NtfsAttributeType::StandardInformation => {
- let inner = NtfsStandardInformation::new(self.ntfs, fs, value, length)?;
- Ok(NtfsStructuredValue::StandardInformation(inner))
- }
- NtfsAttributeType::AttributeList => panic!("TODO"),
- NtfsAttributeType::FileName => {
- let inner = NtfsFileName::new(self.ntfs, fs, value, length)?;
- Ok(NtfsStructuredValue::FileName(inner))
- }
- NtfsAttributeType::ObjectId => {
- let inner = NtfsObjectId::new(self.ntfs, fs, value, length)?;
- Ok(NtfsStructuredValue::ObjectId(inner))
- }
- NtfsAttributeType::SecurityDescriptor => panic!("TODO"),
- NtfsAttributeType::VolumeName => {
- let inner = NtfsVolumeName::new(self.ntfs, fs, value, length)?;
- Ok(NtfsStructuredValue::VolumeName(inner))
- }
- NtfsAttributeType::VolumeInformation => {
- let inner = NtfsVolumeInformation::new(self.ntfs, fs, value, length)?;
- Ok(NtfsStructuredValue::VolumeInformation(inner))
- }
- NtfsAttributeType::IndexRoot => {
- let inner = NtfsIndexRoot::new(self.ntfs, fs, value, length)?;
- Ok(NtfsStructuredValue::IndexRoot(inner))
- }
- NtfsAttributeType::IndexAllocation => {
- let inner = NtfsIndexAllocation::new(self.ntfs, fs, value, length)?;
- Ok(NtfsStructuredValue::IndexAllocation(inner))
- }
- ty => Err(NtfsError::UnsupportedStructuredValue {
- position: self.position,
+ let ty = self.ty()?;
+ if ty != S::TY {
+ return Err(NtfsError::StructuredValueOfDifferentType {
+ position: self.position(),
ty,
- }),
+ });
}
+
+ if !self.is_resident() {
+ return Err(NtfsError::UnexpectedNonResidentAttribute {
+ position: self.position(),
+ });
+ }
+
+ let resident_value = self.resident_value()?;
+ S::from_data(resident_value.data(), self.position())
+ }
+
+ pub(crate) fn resident_value(&self) -> Result<NtfsResidentAttributeValue<'f>> {
+ debug_assert!(self.is_resident());
+ self.validate_resident_value_sizes()?;
+
+ let start = self.offset + self.resident_value_offset() as usize;
+ let end = start + self.resident_value_length() as usize;
+ let data = &self.file.record_data()[start..end];
+
+ Ok(NtfsResidentAttributeValue::new(data, self.position()))
+ }
+
+ fn resident_value_length(&self) -> u32 {
+ debug_assert!(self.is_resident());
+ let start = self.offset + offset_of!(NtfsResidentAttributeHeader, value_length);
+ LittleEndian::read_u32(&self.file.record_data()[start..])
+ }
+
+ fn resident_value_offset(&self) -> u16 {
+ debug_assert!(self.is_resident());
+ let start = self.offset + offset_of!(NtfsResidentAttributeHeader, value_offset);
+ LittleEndian::read_u16(&self.file.record_data()[start..])
}
/// Returns the type of this NTFS attribute, or [`NtfsError::UnsupportedNtfsAttributeType`]
/// if it's an unknown type.
pub fn ty(&self) -> Result<NtfsAttributeType> {
- NtfsAttributeType::n(self.header.ty).ok_or(NtfsError::UnsupportedNtfsAttributeType {
- position: self.position,
- actual: self.header.ty,
+ let start = self.offset + offset_of!(NtfsAttributeHeader, ty);
+ let ty = LittleEndian::read_u32(&self.file.record_data()[start..]);
+
+ NtfsAttributeType::n(ty).ok_or(NtfsError::UnsupportedNtfsAttributeType {
+ position: self.position(),
+ actual: ty,
})
}
+ fn validate_name_sizes(&self) -> Result<()> {
+ let start = self.name_offset();
+ if start as u32 >= self.attribute_length() {
+ return Err(NtfsError::InvalidNtfsAttributeNameOffset {
+ position: self.position(),
+ expected: start,
+ actual: self.attribute_length(),
+ });
+ }
+
+ let end = start as usize + self.name_length();
+ if end > self.attribute_length() as usize {
+ return Err(NtfsError::InvalidNtfsAttributeNameLength {
+ position: self.position(),
+ expected: end,
+ actual: self.attribute_length(),
+ });
+ }
+
+ Ok(())
+ }
+
+ fn validate_resident_value_sizes(&self) -> Result<()> {
+ debug_assert!(self.is_resident());
+
+ let start = self.resident_value_offset();
+ if start as u32 >= self.attribute_length() {
+ return Err(NtfsError::InvalidNtfsResidentAttributeValueOffset {
+ position: self.position(),
+ expected: start,
+ actual: self.attribute_length(),
+ });
+ }
+
+ let end = start as u32 + self.resident_value_length();
+ if end > self.attribute_length() {
+ return Err(NtfsError::InvalidNtfsResidentAttributeValueLength {
+ position: self.position(),
+ expected: end,
+ actual: self.attribute_length(),
+ });
+ }
+
+ Ok(())
+ }
+
/// Returns an [`NtfsAttributeValue`] structure to read the value of this NTFS attribute.
- pub fn value<T>(&self, fs: &mut T) -> Result<NtfsAttributeValue<'n>>
- where
- T: Read + Seek,
- {
- match &self.extra_header {
- NtfsAttributeExtraHeader::Resident(resident_header) => {
- let value_position = self.position + resident_header.value_offset as u64;
- let value_length = resident_header.value_length as u64;
- let value = NtfsDataRun::from_byte_info(value_position, value_length);
- Ok(NtfsAttributeValue::Resident(value))
- }
- NtfsAttributeExtraHeader::NonResident(non_resident_header) => {
- let start = self.position + non_resident_header.data_runs_offset as u64;
- let end = self.position + self.header.length as u64;
- let value = NtfsAttributeNonResidentValue::new(
- &self.ntfs,
- fs,
- start..end,
- non_resident_header.data_size,
- )?;
- Ok(NtfsAttributeValue::NonResident(value))
- }
+ pub fn value(&self) -> Result<NtfsAttributeValue<'n, 'f>> {
+ if self.is_resident() {
+ let resident_value = self.resident_value()?;
+ Ok(NtfsAttributeValue::Resident(resident_value))
+ } else {
+ let non_resident_value = self.non_resident_value()?;
+ Ok(NtfsAttributeValue::NonResident(non_resident_value))
}
}
/// Returns the length of the value of this NTFS attribute, in bytes.
pub fn value_length(&self) -> u64 {
- match &self.extra_header {
- NtfsAttributeExtraHeader::Resident(resident_header) => {
- resident_header.value_length as u64
- }
- NtfsAttributeExtraHeader::NonResident(non_resident_header) => {
- non_resident_header.data_size
- }
+ if self.is_resident() {
+ self.resident_value_length() as u64
+ } else {
+ self.non_resident_value_data_size()
}
}
}
-pub struct NtfsAttributes<'n> {
- ntfs: &'n Ntfs,
- items_range: Range<u64>,
+pub struct NtfsAttributes<'n, 'a> {
+ file: &'a NtfsFile<'n>,
+ items_range: Range<usize>,
}
-impl<'n> NtfsAttributes<'n> {
- pub(crate) fn new(ntfs: &'n Ntfs, file: &NtfsFile) -> Self {
- let start = file.position() + file.first_attribute_offset() as u64;
- let end = file.position() + file.used_size() as u64;
+impl<'n, 'a> NtfsAttributes<'n, 'a> {
+ pub(crate) fn new(file: &'a NtfsFile<'n>) -> Self {
+ let start = file.first_attribute_offset() as usize;
+ let end = file.used_size() as usize;
let items_range = start..end;
- Self { ntfs, items_range }
- }
-
- pub fn attach<'a, T>(self, fs: &'a mut T) -> NtfsAttributesAttached<'n, 'a, T>
- where
- T: Read + Seek,
- {
- NtfsAttributesAttached::new(fs, self)
+ Self { file, items_range }
}
+}
- pub(crate) fn find_first_by_ty<T>(
- &mut self,
- fs: &mut T,
- ty: NtfsAttributeType,
- ) -> Option<Result<NtfsAttribute<'n>>>
- where
- T: Read + Seek,
- {
- while let Some(attribute) = self.next(fs) {
- let attribute = iter_try!(attribute);
- let attribute_ty = iter_try!(attribute.ty());
- if attribute_ty == ty {
- return Some(Ok(attribute));
- }
- }
-
- None
- }
+impl<'n, 'a> Iterator for NtfsAttributes<'n, 'a> {
+ type Item = NtfsAttribute<'n, 'a>;
- pub fn next<T>(&mut self, fs: &mut T) -> Option<Result<NtfsAttribute<'n>>>
- where
- T: Read + Seek,
- {
+ fn next(&mut self) -> Option<Self::Item> {
if self.items_range.is_empty() {
return None;
}
// This may be an entire attribute or just the 4-byte end marker.
// Check if this marks the end of the attribute list.
- let position = self.items_range.start;
- iter_try!(fs.seek(SeekFrom::Start(position)));
- let ty = iter_try!(fs.read_le::<u32>());
+ let ty = LittleEndian::read_u32(&self.file.record_data()[self.items_range.start..]);
if ty == NtfsAttributeType::End as u32 {
return None;
}
// It's a real attribute.
- let attribute = iter_try!(NtfsAttribute::new(self.ntfs, fs, position));
- self.items_range.start += attribute.attribute_length() as u64;
-
- Some(Ok(attribute))
- }
-}
-
-pub struct NtfsAttributesAttached<'n, 'a, T: Read + Seek> {
- fs: &'a mut T,
- attributes: NtfsAttributes<'n>,
-}
+ let attribute = NtfsAttribute::new(self.file, self.items_range.start);
+ self.items_range.start += attribute.attribute_length() as usize;
-impl<'n, 'a, T> NtfsAttributesAttached<'n, 'a, T>
-where
- T: Read + Seek,
-{
- fn new(fs: &'a mut T, attributes: NtfsAttributes<'n>) -> Self {
- Self { fs, attributes }
- }
-
- pub fn detach(self) -> NtfsAttributes<'n> {
- self.attributes
- }
-}
-
-impl<'n, 'a, T> Iterator for NtfsAttributesAttached<'n, 'a, T>
-where
- T: Read + Seek,
-{
- type Item = Result<NtfsAttribute<'n>>;
-
- fn next(&mut self) -> Option<Self::Item> {
- self.attributes.next(self.fs)
+ Some(attribute)
}
}
-impl<'n, 'a, T> FusedIterator for NtfsAttributesAttached<'n, 'a, T> where T: Read + Seek {}
+impl<'n, 'a> FusedIterator for NtfsAttributes<'n, 'a> {}