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/index_entry.rs')
-rw-r--r--src/index_entry.rs217
1 files changed, 217 insertions, 0 deletions
diff --git a/src/index_entry.rs b/src/index_entry.rs
new file mode 100644
index 0000000..65b24e3
--- /dev/null
+++ b/src/index_entry.rs
@@ -0,0 +1,217 @@
+// Copyright 2021 Colin Finck <colin@reactos.org>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+use crate::attribute_value::NtfsAttributeValue;
+use crate::error::Result;
+use crate::structured_values::NewNtfsStructuredValue;
+use crate::traits::NtfsReadSeek;
+use binread::io::{Read, Seek, SeekFrom};
+use binread::{BinRead, BinReaderExt};
+use bitflags::bitflags;
+use core::iter::FusedIterator;
+use core::marker::PhantomData;
+use core::mem;
+
+/// Size of all [`IndexEntryHeader`] fields plus some reserved bytes.
+const INDEX_ENTRY_HEADER_SIZE: i64 = 16;
+
+#[derive(BinRead, Clone, Debug)]
+struct IndexEntryHeader {
+ file_ref: u64,
+ index_entry_length: u16,
+ key_length: u16,
+ flags: u8,
+}
+
+#[derive(Clone, Debug)]
+pub struct NtfsIndexEntry<'n, K>
+where
+ K: NewNtfsStructuredValue<'n>,
+{
+ header: IndexEntryHeader,
+ value: NtfsAttributeValue<'n>,
+ key_type: PhantomData<K>,
+}
+
+bitflags! {
+ pub struct NtfsIndexEntryFlags: u8 {
+ /// This index entry points to a sub-node.
+ const HAS_SUBNODE = 0x01;
+ /// This is the last index entry in the list.
+ const LAST_ENTRY = 0x02;
+ }
+}
+
+impl<'n, K> NtfsIndexEntry<'n, K>
+where
+ K: NewNtfsStructuredValue<'n>,
+{
+ pub(crate) fn new<T>(fs: &mut T, value: NtfsAttributeValue<'n>) -> Result<Self>
+ where
+ T: Read + Seek,
+ {
+ let mut value_attached = value.clone().attach(fs);
+ let header = value_attached.read_le::<IndexEntryHeader>()?;
+ let key_type = PhantomData;
+
+ let entry = Self {
+ header,
+ value,
+ key_type,
+ };
+
+ Ok(entry)
+ }
+
+ pub fn flags(&self) -> NtfsIndexEntryFlags {
+ NtfsIndexEntryFlags::from_bits_truncate(self.header.flags)
+ }
+
+ pub fn index_entry_length(&self) -> u16 {
+ self.header.index_entry_length
+ }
+
+ pub fn key_length(&self) -> u16 {
+ self.header.key_length
+ }
+
+ /// Returns the structured value of the key of this Index Entry,
+ /// or `None` if this Index Entry has no key.
+ /// The last Index Entry never has a key.
+ pub fn key_structured_value<T>(&self, fs: &mut T) -> Option<Result<K>>
+ where
+ T: Read + Seek,
+ {
+ if self.header.key_length == 0 || self.flags().contains(NtfsIndexEntryFlags::LAST_ENTRY) {
+ return None;
+ }
+
+ let mut value = self.value.clone();
+ iter_try!(value.seek(fs, SeekFrom::Current(INDEX_ENTRY_HEADER_SIZE)));
+ let length = self.header.key_length as u64;
+
+ let structured_value = iter_try!(K::new(fs, value, length));
+ Some(Ok(structured_value))
+ }
+
+ /// Returns the Virtual Cluster Number (VCN) of the subnode of this Index Entry,
+ /// or `None` if this Index Entry has no subnode.
+ pub fn subnode_vcn<T>(&self, fs: &mut T) -> Option<Result<u64>>
+ where
+ T: Read + Seek,
+ {
+ if !self.flags().contains(NtfsIndexEntryFlags::HAS_SUBNODE) {
+ return None;
+ }
+
+ // Read the subnode VCN from the very end of the Index Entry.
+ let mut value_attached = self.value.clone().attach(fs);
+ iter_try!(value_attached.seek(SeekFrom::Current(
+ self.index_entry_length() as i64 - mem::size_of::<u64>() as i64
+ )));
+ let vcn = iter_try!(value_attached.read_le::<u64>());
+
+ Some(Ok(vcn))
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct NtfsIndexEntries<'n, K>
+where
+ K: NewNtfsStructuredValue<'n>,
+{
+ value: NtfsAttributeValue<'n>,
+ end: u64,
+ key_type: PhantomData<K>,
+}
+
+impl<'n, K> NtfsIndexEntries<'n, K>
+where
+ K: NewNtfsStructuredValue<'n>,
+{
+ pub(crate) fn new(value: NtfsAttributeValue<'n>, end: u64) -> Self {
+ debug_assert!(end <= value.len());
+ let key_type = PhantomData;
+
+ Self {
+ value,
+ end,
+ key_type,
+ }
+ }
+
+ pub fn attach<'a, T>(self, fs: &'a mut T) -> NtfsIndexEntriesAttached<'n, 'a, K, T>
+ where
+ T: Read + Seek,
+ {
+ NtfsIndexEntriesAttached::new(fs, self)
+ }
+
+ pub fn next<T>(&mut self, fs: &mut T) -> Option<Result<NtfsIndexEntry<'n, K>>>
+ where
+ T: Read + Seek,
+ {
+ if self.value.stream_position() >= self.end {
+ return None;
+ }
+
+ // Get the current entry.
+ let entry = iter_try!(NtfsIndexEntry::new(fs, self.value.clone()));
+
+ if entry.flags().contains(NtfsIndexEntryFlags::LAST_ENTRY) {
+ // This is the last entry.
+ // Ensure that we don't read any other entries by seeking to the end.
+ iter_try!(self.value.seek(fs, SeekFrom::Start(self.end)));
+ } else {
+ // This is not the last entry.
+ // Advance our iterator to the next entry.
+ iter_try!(self
+ .value
+ .seek(fs, SeekFrom::Current(entry.index_entry_length() as i64)));
+ }
+
+ Some(Ok(entry))
+ }
+}
+
+pub struct NtfsIndexEntriesAttached<'n, 'a, K, T>
+where
+ K: NewNtfsStructuredValue<'n>,
+ T: Read + Seek,
+{
+ fs: &'a mut T,
+ index_entries: NtfsIndexEntries<'n, K>,
+}
+
+impl<'n, 'a, K, T> NtfsIndexEntriesAttached<'n, 'a, K, T>
+where
+ K: NewNtfsStructuredValue<'n>,
+ T: Read + Seek,
+{
+ fn new(fs: &'a mut T, index_entries: NtfsIndexEntries<'n, K>) -> Self {
+ Self { fs, index_entries }
+ }
+
+ pub fn detach(self) -> NtfsIndexEntries<'n, K> {
+ self.index_entries
+ }
+}
+
+impl<'n, 'a, K, T> Iterator for NtfsIndexEntriesAttached<'n, 'a, K, T>
+where
+ K: NewNtfsStructuredValue<'n>,
+ T: Read + Seek,
+{
+ type Item = Result<NtfsIndexEntry<'n, K>>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.index_entries.next(self.fs)
+ }
+}
+
+impl<'n, 'a, K, T> FusedIterator for NtfsIndexEntriesAttached<'n, 'a, K, T>
+where
+ K: NewNtfsStructuredValue<'n>,
+ T: Read + Seek,
+{
+}