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-09-17 21:02:11 +0300
committerColin Finck <colin@reactos.org>2021-09-17 21:02:11 +0300
commitfd4ed7e7ed12567f82a80cdca2f075f72612e79f (patch)
tree719ee4e3bb94f11d19215bd9bb94e38adf01b368
parent60547dab12688c7ee73c6e748eed24d017fbe194 (diff)
Implement the `AttributeList` structured value.
This one can be resident or non-resident, hence this commit also adds some related support code.
-rw-r--r--src/attribute.rs13
-rw-r--r--src/attribute_value.rs54
-rw-r--r--src/structured_values/attribute_list.rs245
3 files changed, 312 insertions, 0 deletions
diff --git a/src/attribute.rs b/src/attribute.rs
index 42762a1..a7a1fc1 100644
--- a/src/attribute.rs
+++ b/src/attribute.rs
@@ -292,6 +292,19 @@ impl<'n, 'f> NtfsAttribute<'n, 'f> {
LittleEndian::read_u16(&self.file.record_data()[start..])
}
+ pub fn structured_value<T, S>(&self, fs: &mut T) -> Result<S>
+ where
+ T: Read + Seek,
+ S: NtfsStructuredValueFromSlice<'f>
+ + NtfsStructuredValueFromNonResidentAttributeValue<'n, 'f>,
+ {
+ if self.is_resident() {
+ self.resident_structured_value()
+ } else {
+ self.non_resident_structured_value(fs)
+ }
+ }
+
/// Returns the type of this NTFS attribute, or [`NtfsError::UnsupportedAttributeType`]
/// if it's an unknown type.
pub fn ty(&self) -> Result<NtfsAttributeType> {
diff --git a/src/attribute_value.rs b/src/attribute_value.rs
index 6f4f75f..522e22b 100644
--- a/src/attribute_value.rs
+++ b/src/attribute_value.rs
@@ -365,6 +365,16 @@ impl<'n, 'f> NtfsNonResidentAttributeValue<'n, 'f> {
})
}
+ pub fn attach<'a, T>(
+ self,
+ fs: &'a mut T,
+ ) -> NtfsNonResidentAttributeValueAttached<'n, 'f, 'a, T>
+ where
+ T: Read + Seek,
+ {
+ NtfsNonResidentAttributeValueAttached::new(fs, self)
+ }
+
/// Returns the absolute current data seek position within the filesystem, in bytes.
/// This may be `None` if:
/// * The current seek position is outside the valid range, or
@@ -543,6 +553,50 @@ impl<'n, 'f> NtfsReadSeek for NtfsNonResidentAttributeValue<'n, 'f> {
}
}
+pub struct NtfsNonResidentAttributeValueAttached<'n, 'f, 'a, T: Read + Seek> {
+ fs: &'a mut T,
+ value: NtfsNonResidentAttributeValue<'n, 'f>,
+}
+
+impl<'n, 'f, 'a, T> NtfsNonResidentAttributeValueAttached<'n, 'f, 'a, T>
+where
+ T: Read + Seek,
+{
+ fn new(fs: &'a mut T, value: NtfsNonResidentAttributeValue<'n, 'f>) -> Self {
+ Self { fs, value }
+ }
+
+ pub fn data_position(&self) -> Option<u64> {
+ self.value.data_position()
+ }
+
+ pub fn detach(self) -> NtfsNonResidentAttributeValue<'n, 'f> {
+ self.value
+ }
+
+ pub fn len(&self) -> u64 {
+ self.value.len()
+ }
+}
+
+impl<'n, 'f, 'a, T> Read for NtfsNonResidentAttributeValueAttached<'n, 'f, 'a, T>
+where
+ T: Read + Seek,
+{
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ self.value.read(self.fs, buf).map_err(io::Error::from)
+ }
+}
+
+impl<'n, 'f, 'a, T> Seek for NtfsNonResidentAttributeValueAttached<'n, 'f, 'a, T>
+where
+ T: Read + Seek,
+{
+ fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
+ self.value.seek(self.fs, pos).map_err(io::Error::from)
+ }
+}
+
#[derive(Clone, Debug)]
pub struct NtfsResidentAttributeValue<'f> {
data: &'f [u8],
diff --git a/src/structured_values/attribute_list.rs b/src/structured_values/attribute_list.rs
index 53769a7..da1f891 100644
--- a/src/structured_values/attribute_list.rs
+++ b/src/structured_values/attribute_list.rs
@@ -1,2 +1,247 @@
// Copyright 2021 Colin Finck <colin@reactos.org>
// SPDX-License-Identifier: GPL-2.0-or-later
+
+use crate::attribute::NtfsAttributeType;
+use crate::attribute_value::NtfsNonResidentAttributeValue;
+use crate::error::{NtfsError, Result};
+use crate::file_reference::NtfsFileReference;
+use crate::string::NtfsString;
+use crate::structured_values::{
+ NtfsStructuredValue, NtfsStructuredValueFromNonResidentAttributeValue,
+ NtfsStructuredValueFromSlice,
+};
+use crate::traits::NtfsReadSeek;
+use crate::types::Vcn;
+use arrayvec::ArrayVec;
+use binread::io::{Cursor, Read, Seek, SeekFrom};
+use binread::{BinRead, BinReaderExt};
+use core::mem;
+
+/// Size of all [`AttributeListEntryHeader`] fields.
+const ATTRIBUTE_LIST_ENTRY_HEADER_SIZE: usize = 26;
+
+/// [`AttributeListEntryHeader::name_length`] is an `u8` length field specifying the number of UTF-16 code points.
+/// Hence, the name occupies up to 510 bytes.
+const NAME_MAX_SIZE: usize = (u8::MAX as usize) * mem::size_of::<u16>();
+
+#[allow(unused)]
+#[derive(BinRead, Clone, Debug)]
+struct AttributeListEntryHeader {
+ /// Type of the attribute, known types are in [`NtfsAttributeType`].
+ ty: u32,
+ /// Length of this attribute list entry, in bytes.
+ list_entry_length: u16,
+ /// Length of the name, in UTF-16 code points (every code point is 2 bytes).
+ name_length: u8,
+ /// Offset to the beginning of the name, in bytes from the beginning of this header.
+ name_offset: u8,
+ /// 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.
+ lowest_vcn: Vcn,
+ /// Reference to the [`NtfsFile`] record where this attribute is stored.
+ base_file_reference: NtfsFileReference,
+ /// Identifier of this attribute that is unique within the [`NtfsFile`].
+ instance: u16,
+}
+
+#[derive(Clone, Debug)]
+pub enum NtfsAttributeList<'n, 'f> {
+ Resident(&'f [u8], u64),
+ NonResident(NtfsNonResidentAttributeValue<'n, 'f>),
+}
+
+impl<'n, 'f> NtfsAttributeList<'n, 'f> {
+ pub fn iter(&self) -> NtfsAttributeListEntries<'n, 'f> {
+ NtfsAttributeListEntries::new(self.clone())
+ }
+
+ pub fn position(&self) -> u64 {
+ match self {
+ Self::Resident(_slice, position) => *position,
+ Self::NonResident(value) => value.data_position().unwrap(),
+ }
+ }
+}
+
+impl<'n, 'f> NtfsStructuredValue 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>
+ where
+ T: Read + Seek,
+ {
+ Ok(Self::NonResident(value))
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct NtfsAttributeListEntries<'n, 'f> {
+ attribute_list: NtfsAttributeList<'n, 'f>,
+}
+
+impl<'n, 'f> NtfsAttributeListEntries<'n, 'f> {
+ fn new(attribute_list: NtfsAttributeList<'n, 'f>) -> Self {
+ Self { attribute_list }
+ }
+
+ pub fn next<T>(&mut self, fs: &mut T) -> Option<Result<NtfsAttributeListEntry>>
+ where
+ T: Read + Seek,
+ {
+ match &mut self.attribute_list {
+ NtfsAttributeList::Resident(slice, position) => Self::next_resident(slice, position),
+ NtfsAttributeList::NonResident(value) => Self::next_non_resident(fs, value),
+ }
+ }
+
+ pub fn next_non_resident<T>(
+ fs: &mut T,
+ value: &mut NtfsNonResidentAttributeValue<'n, 'f>,
+ ) -> Option<Result<NtfsAttributeListEntry>>
+ where
+ T: Read + Seek,
+ {
+ if value.stream_position() >= value.len() {
+ return None;
+ }
+
+ // Get the current entry.
+ let mut value_attached = value.clone().attach(fs);
+ let position = value.data_position().unwrap();
+ let entry = iter_try!(NtfsAttributeListEntry::new(&mut value_attached, position));
+
+ // Advance our iterator to the next entry.
+ iter_try!(value.seek(fs, SeekFrom::Current(entry.list_entry_length() as i64)));
+
+ Some(Ok(entry))
+ }
+
+ pub fn next_resident(
+ slice: &mut &'f [u8],
+ position: &mut u64,
+ ) -> Option<Result<NtfsAttributeListEntry>> {
+ if slice.is_empty() {
+ return None;
+ }
+
+ // Get the current entry.
+ let mut cursor = Cursor::new(*slice);
+ let entry = iter_try!(NtfsAttributeListEntry::new(&mut cursor, *position));
+
+ // Advance our iterator to the next entry.
+ let bytes_to_advance = entry.list_entry_length() as usize;
+ *slice = &slice[bytes_to_advance..];
+ *position += bytes_to_advance as u64;
+
+ Some(Ok(entry))
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct NtfsAttributeListEntry {
+ header: AttributeListEntryHeader,
+ name: ArrayVec<u8, NAME_MAX_SIZE>,
+ position: u64,
+}
+
+impl NtfsAttributeListEntry {
+ fn new<T>(r: &mut T, position: u64) -> Result<Self>
+ where
+ T: Read + Seek,
+ {
+ let header = r.read_le::<AttributeListEntryHeader>()?;
+
+ let mut entry = Self {
+ header,
+ name: ArrayVec::from([0u8; NAME_MAX_SIZE]),
+ position,
+ };
+ entry.validate_name_length()?;
+ entry.read_name(r)?;
+
+ Ok(entry)
+ }
+
+ pub fn base_file_reference(&self) -> NtfsFileReference {
+ self.header.base_file_reference
+ }
+
+ pub fn instance(&self) -> u16 {
+ self.header.instance
+ }
+
+ pub fn list_entry_length(&self) -> u16 {
+ self.header.list_entry_length
+ }
+
+ pub fn lowest_vcn(&self) -> Vcn {
+ self.header.lowest_vcn
+ }
+
+ /// Gets the attribute name and returns it wrapped in an [`NtfsString`].
+ pub fn name<'s>(&'s self) -> NtfsString<'s> {
+ NtfsString(&self.name)
+ }
+
+ /// Returns the file name length, in bytes.
+ ///
+ /// A file name has a maximum length of 255 UTF-16 code points (510 bytes).
+ pub fn name_length(&self) -> usize {
+ self.header.name_length as usize * mem::size_of::<u16>()
+ }
+
+ pub fn position(&self) -> u64 {
+ self.position
+ }
+
+ fn read_name<T>(&mut self, r: &mut T) -> Result<()>
+ where
+ T: Read + Seek,
+ {
+ debug_assert_eq!(self.name.len(), NAME_MAX_SIZE);
+
+ let name_length = self.name_length();
+ r.read_exact(&mut self.name[..name_length])?;
+ self.name.truncate(name_length);
+
+ Ok(())
+ }
+
+ /// Returns the type of this NTFS attribute, or [`NtfsError::UnsupportedAttributeType`]
+ /// if it's an unknown type.
+ pub fn ty(&self) -> Result<NtfsAttributeType> {
+ NtfsAttributeType::n(self.header.ty).ok_or(NtfsError::UnsupportedAttributeType {
+ position: self.position(),
+ actual: self.header.ty,
+ })
+ }
+
+ fn validate_name_length(&self) -> Result<()> {
+ let total_size = ATTRIBUTE_LIST_ENTRY_HEADER_SIZE + self.name_length();
+
+ if total_size > self.list_entry_length() as usize {
+ return Err(NtfsError::InvalidStructuredValueSize {
+ position: self.position(),
+ ty: NtfsAttributeType::AttributeList,
+ expected: self.list_entry_length() as usize,
+ actual: total_size,
+ });
+ }
+
+ Ok(())
+ }
+}