ntfs/structured_values/
attribute_list.rs

1// Copyright 2021-2023 Colin Finck <colin@reactos.org>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use core::mem;
5
6use arrayvec::ArrayVec;
7use binrw::io::{Cursor, Read, Seek, SeekFrom};
8use binrw::{BinRead, BinReaderExt};
9use nt_string::u16strle::U16StrLe;
10
11use crate::attribute::{NtfsAttribute, NtfsAttributeType};
12use crate::attribute_value::{NtfsAttributeValue, NtfsNonResidentAttributeValue};
13use crate::error::{NtfsError, Result};
14use crate::file::NtfsFile;
15use crate::file_reference::NtfsFileReference;
16use crate::ntfs::Ntfs;
17use crate::structured_values::NtfsStructuredValue;
18use crate::traits::NtfsReadSeek;
19use crate::types::{NtfsPosition, Vcn};
20
21/// Size of all [`AttributeListEntryHeader`] fields.
22const ATTRIBUTE_LIST_ENTRY_HEADER_SIZE: usize = 26;
23
24/// [`AttributeListEntryHeader::name_length`] is an `u8` length field specifying the number of UTF-16 code points.
25/// Hence, the name occupies up to 510 bytes.
26const NAME_MAX_SIZE: usize = (u8::MAX as usize) * mem::size_of::<u16>();
27
28#[allow(unused)]
29#[derive(BinRead, Clone, Debug)]
30struct AttributeListEntryHeader {
31    /// Type of the attribute, known types are in [`NtfsAttributeType`].
32    ty: u32,
33    /// Length of this attribute list entry, in bytes.
34    list_entry_length: u16,
35    /// Length of the name, in UTF-16 code points (every code point is 2 bytes).
36    name_length: u8,
37    /// Offset to the beginning of the name, in bytes from the beginning of this header.
38    name_offset: u8,
39    /// Lower boundary of Virtual Cluster Numbers (VCNs) referenced by this attribute.
40    /// This becomes relevant when file data is split over multiple attributes.
41    /// Otherwise, it's zero.
42    lowest_vcn: Vcn,
43    /// Reference to the File Record where this attribute is stored.
44    base_file_reference: NtfsFileReference,
45    /// Identifier of this attribute that is unique within the [`NtfsFile`].
46    instance: u16,
47}
48
49/// Structure of an $ATTRIBUTE_LIST attribute.
50///
51/// When a File Record lacks space to incorporate further attributes, NTFS creates an additional File Record,
52/// moves all or some of the existing attributes there, and references them via a resident $ATTRIBUTE_LIST attribute
53/// in the original File Record.
54/// When you add even more attributes, NTFS may turn the resident $ATTRIBUTE_LIST into a non-resident one to
55/// make up the required space.
56///
57/// An $ATTRIBUTE_LIST attribute can hence be resident or non-resident.
58///
59/// Reference: <https://flatcap.github.io/linux-ntfs/ntfs/attributes/attribute_list.html>
60#[derive(Clone, Debug)]
61pub enum NtfsAttributeList<'n, 'f> {
62    /// A resident $ATTRIBUTE_LIST attribute.
63    Resident(&'f [u8], NtfsPosition),
64    /// A non-resident $ATTRIBUTE_LIST attribute.
65    NonResident(NtfsNonResidentAttributeValue<'n, 'f>),
66}
67
68impl<'n, 'f> NtfsAttributeList<'n, 'f> {
69    /// Returns an iterator over all entries of this $ATTRIBUTE_LIST attribute (cf. [`NtfsAttributeListEntry`]).
70    pub fn entries(&self) -> NtfsAttributeListEntries<'n, 'f> {
71        NtfsAttributeListEntries::new(self.clone())
72    }
73
74    /// Returns the absolute position of this $ATTRIBUTE_LIST attribute value within the filesystem, in bytes.
75    pub fn position(&self) -> NtfsPosition {
76        match self {
77            Self::Resident(_slice, position) => *position,
78            Self::NonResident(value) => value.data_position(),
79        }
80    }
81}
82
83impl<'n, 'f> NtfsStructuredValue<'n, 'f> for NtfsAttributeList<'n, 'f> {
84    const TY: NtfsAttributeType = NtfsAttributeType::AttributeList;
85
86    fn from_attribute_value<T>(_fs: &mut T, value: NtfsAttributeValue<'n, 'f>) -> Result<Self>
87    where
88        T: Read + Seek,
89    {
90        match value {
91            NtfsAttributeValue::Resident(value) => {
92                let slice = value.data();
93                let position = value.data_position();
94                Ok(Self::Resident(slice, position))
95            }
96            NtfsAttributeValue::NonResident(value) => Ok(Self::NonResident(value)),
97            NtfsAttributeValue::AttributeListNonResident(value) => {
98                // Attribute Lists are never nested.
99                // Hence, we must not create this attribute from an attribute that is already part of Attribute List.
100                let position = value.data_position();
101                Err(NtfsError::UnexpectedAttributeListAttribute { position })
102            }
103        }
104    }
105}
106
107/// Iterator over
108///   all entries of an [`NtfsAttributeList`] attribute,
109///   returning an [`NtfsAttributeListEntry`] for each entry.
110///
111/// This iterator is returned from the [`NtfsAttributeList::entries`] function.
112#[derive(Clone, Debug)]
113pub struct NtfsAttributeListEntries<'n, 'f> {
114    attribute_list: NtfsAttributeList<'n, 'f>,
115}
116
117impl<'n, 'f> NtfsAttributeListEntries<'n, 'f> {
118    fn new(attribute_list: NtfsAttributeList<'n, 'f>) -> Self {
119        Self { attribute_list }
120    }
121
122    /// See [`Iterator::next`].
123    pub fn next<T>(&mut self, fs: &mut T) -> Option<Result<NtfsAttributeListEntry>>
124    where
125        T: Read + Seek,
126    {
127        match &mut self.attribute_list {
128            NtfsAttributeList::Resident(slice, position) => Self::next_resident(slice, position),
129            NtfsAttributeList::NonResident(value) => Self::next_non_resident(fs, value),
130        }
131    }
132
133    fn next_non_resident<T>(
134        fs: &mut T,
135        value: &mut NtfsNonResidentAttributeValue<'n, 'f>,
136    ) -> Option<Result<NtfsAttributeListEntry>>
137    where
138        T: Read + Seek,
139    {
140        if value.stream_position() >= value.len() {
141            return None;
142        }
143
144        // Get the current entry.
145        let mut value_attached = value.clone().attach(fs);
146        let position = value.data_position();
147        let entry = iter_try!(NtfsAttributeListEntry::new(&mut value_attached, position));
148
149        // Advance our iterator to the next entry.
150        iter_try!(value.seek(fs, SeekFrom::Current(entry.list_entry_length() as i64)));
151
152        Some(Ok(entry))
153    }
154
155    fn next_resident(
156        slice: &mut &'f [u8],
157        position: &mut NtfsPosition,
158    ) -> Option<Result<NtfsAttributeListEntry>> {
159        if slice.is_empty() {
160            return None;
161        }
162
163        // Get the current entry.
164        let mut cursor = Cursor::new(*slice);
165        let entry = iter_try!(NtfsAttributeListEntry::new(&mut cursor, *position));
166
167        // Advance our iterator to the next entry.
168        let bytes_to_advance = entry.list_entry_length() as usize;
169        *slice = slice.get(bytes_to_advance..)?;
170        *position += bytes_to_advance;
171        Some(Ok(entry))
172    }
173}
174
175/// A single entry of an [`NtfsAttributeList`] attribute.
176#[derive(Clone, Debug)]
177pub struct NtfsAttributeListEntry {
178    header: AttributeListEntryHeader,
179    name: ArrayVec<u8, NAME_MAX_SIZE>,
180    position: NtfsPosition,
181}
182
183impl NtfsAttributeListEntry {
184    fn new<T>(r: &mut T, position: NtfsPosition) -> Result<Self>
185    where
186        T: Read + Seek,
187    {
188        let header = r.read_le::<AttributeListEntryHeader>()?;
189
190        let mut entry = Self {
191            header,
192            name: ArrayVec::from([0u8; NAME_MAX_SIZE]),
193            position,
194        };
195        entry.validate_entry_and_name_length()?;
196        entry.read_name(r)?;
197
198        Ok(entry)
199    }
200
201    /// Returns a reference to the File Record where the attribute is stored.
202    pub fn base_file_reference(&self) -> NtfsFileReference {
203        self.header.base_file_reference
204    }
205
206    /// Returns the instance number of this attribute list entry.
207    ///
208    /// An instance number is unique within a single NTFS File Record.
209    ///
210    /// Multiple entries of the same type and instance number form a connected attribute,
211    /// meaning an attribute whose value is stretched over multiple attributes.
212    pub fn instance(&self) -> u16 {
213        self.header.instance
214    }
215
216    /// Returns the length of this attribute list entry, in bytes.
217    pub fn list_entry_length(&self) -> u16 {
218        self.header.list_entry_length
219    }
220
221    /// Returns the offset of this attribute's value data as a Virtual Cluster Number (VCN).
222    ///
223    /// This is zero for all unconnected attributes and for the first attribute of a connected attribute.
224    /// For subsequent attributes of a connected attribute, this value is nonzero.
225    ///
226    /// The lowest_vcn + data length of one attribute equal the lowest_vcn of its following connected attribute.
227    pub fn lowest_vcn(&self) -> Vcn {
228        self.header.lowest_vcn
229    }
230
231    /// Gets the attribute name and returns it wrapped in a [`U16StrLe`].
232    pub fn name(&self) -> U16StrLe {
233        U16StrLe(&self.name)
234    }
235
236    /// Returns the file name length, in bytes.
237    ///
238    /// A file name has a maximum length of 255 UTF-16 code points (510 bytes).
239    pub fn name_length(&self) -> usize {
240        self.header.name_length as usize * mem::size_of::<u16>()
241    }
242
243    /// Returns the absolute position of this attribute list entry within the filesystem, in bytes.
244    pub fn position(&self) -> NtfsPosition {
245        self.position
246    }
247
248    fn read_name<T>(&mut self, r: &mut T) -> Result<()>
249    where
250        T: Read + Seek,
251    {
252        debug_assert_eq!(self.name.len(), NAME_MAX_SIZE);
253
254        let name_length = self.name_length();
255        r.read_exact(&mut self.name[..name_length])?;
256        self.name.truncate(name_length);
257
258        Ok(())
259    }
260
261    /// Returns an [`NtfsAttribute`] for the attribute described by this list entry.
262    ///
263    /// Use [`NtfsAttributeListEntry::to_file`] first to get the required File Record.
264    ///
265    /// # Panics
266    ///
267    /// Panics if a wrong File Record has been passed.
268    pub fn to_attribute<'n, 'f>(&self, file: &'f NtfsFile<'n>) -> Result<NtfsAttribute<'n, 'f>> {
269        let file_record_number = self.base_file_reference().file_record_number();
270        assert_eq!(
271            file.file_record_number(),
272            file_record_number,
273            "The given NtfsFile's record number does not match the expected record number. \
274            Always use NtfsAttributeListEntry::to_file to retrieve the correct NtfsFile."
275        );
276
277        let instance = self.instance();
278        let ty = self.ty()?;
279
280        file.find_resident_attribute(ty, None, Some(instance))
281    }
282
283    /// Reads the entire File Record referenced by this attribute and returns it.
284    pub fn to_file<'n, T>(&self, ntfs: &'n Ntfs, fs: &mut T) -> Result<NtfsFile<'n>>
285    where
286        T: Read + Seek,
287    {
288        let file_record_number = self.base_file_reference().file_record_number();
289        ntfs.file(fs, file_record_number)
290    }
291
292    /// Returns the type of this NTFS Attribute, or [`NtfsError::UnsupportedAttributeType`]
293    /// if it's an unknown type.
294    pub fn ty(&self) -> Result<NtfsAttributeType> {
295        NtfsAttributeType::n(self.header.ty).ok_or(NtfsError::UnsupportedAttributeType {
296            position: self.position(),
297            actual: self.header.ty,
298        })
299    }
300
301    fn validate_entry_and_name_length(&self) -> Result<()> {
302        let total_size = ATTRIBUTE_LIST_ENTRY_HEADER_SIZE + self.name_length();
303
304        if total_size > self.list_entry_length() as usize {
305            return Err(NtfsError::InvalidStructuredValueSize {
306                position: self.position(),
307                ty: NtfsAttributeType::AttributeList,
308                expected: self.list_entry_length() as u64,
309                actual: total_size as u64,
310            });
311        }
312
313        Ok(())
314    }
315}