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-08-09 23:56:21 +0300
committerColin Finck <colin@reactos.org>2021-08-09 23:56:21 +0300
commit62da6dc109d669409c5d33d8f101c4a235940166 (patch)
tree11f88077852801d80ac99610d9bc87426ea73d11
parent749398cf3247e2d63f794ce6d02217f8aa07d811 (diff)
Add a few convenience functions to simplify working with the crate.
-rw-r--r--src/error.rs2
-rw-r--r--src/file.rs126
-rw-r--r--src/file_reference.rs11
-rw-r--r--src/index_entry.rs15
-rw-r--r--src/ntfs.rs35
-rw-r--r--src/upcase_table.rs14
6 files changed, 165 insertions, 38 deletions
diff --git a/src/error.rs b/src/error.rs
index 85b6d3e..04f93df 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -132,6 +132,8 @@ pub enum NtfsError {
LcnTooBig { lcn: Lcn },
/// The index root at byte position {position:#010x} is a large index, but no matching index allocation attribute was provided
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,
diff --git a/src/file.rs b/src/file.rs
index 770ca67..c5f25a4 100644
--- a/src/file.rs
+++ b/src/file.rs
@@ -1,10 +1,15 @@
// Copyright 2021 Colin Finck <colin@reactos.org>
// SPDX-License-Identifier: GPL-2.0-or-later
-use crate::attribute::NtfsAttributes;
+use crate::attribute::{NtfsAttribute, NtfsAttributeType, NtfsAttributes};
use crate::error::{NtfsError, Result};
+use crate::index::NtfsIndex;
+use crate::indexes::NtfsFileNameIndex;
use crate::ntfs::Ntfs;
use crate::record::{Record, RecordHeader};
+use crate::structured_values::{
+ NtfsFileName, NtfsIndexAllocation, NtfsIndexRoot, NtfsStandardInformation,
+};
use binread::io::{Read, Seek, SeekFrom};
use bitflags::bitflags;
use byteorder::{ByteOrder, LittleEndian};
@@ -77,10 +82,110 @@ 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()
+ .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,
+ })
+ }
+
pub fn attributes<'f>(&'f self) -> NtfsAttributes<'n, 'f> {
NtfsAttributes::new(self)
}
+ /// Convenience function to get a $DATA attribute of this file.
+ ///
+ /// As NTFS supports multiple data streams per file, you can optionally specify a data stream
+ /// name and NTFS will look up the corresponding $DATA attribute.
+ /// If you specify `None` for `data_stream_name`, the default unnamed $DATA attribute will be looked
+ /// up (commonly known as the "file data").
+ ///
+ /// If you need more control over which $DATA attribute is available and picked up,
+ /// you can use [`NtfsFile::attributes`] to iterate over all attributes of this file.
+ pub fn data<'f>(
+ &'f self,
+ data_stream_name: Option<&str>,
+ ) -> Option<Result<NtfsAttribute<'n, 'f>>> {
+ // Create an iterator that emits all $DATA attributes.
+ let iter = self.attributes().filter(|attribute| {
+ // TODO: Replace by attribute.ty().contains() once https://github.com/rust-lang/rust/issues/62358 has landed.
+ attribute
+ .ty()
+ .map(|ty| ty == NtfsAttributeType::Data)
+ .unwrap_or(false)
+ });
+
+ for attribute in iter {
+ match (attribute.name(), data_stream_name) {
+ (None, None) => {
+ // We found the unnamed $DATA attribute and are looking for the unnamed $DATA attribute.
+ return Some(Ok(attribute));
+ }
+ (Some(Ok(name)), Some(data_stream_name)) => {
+ // We found a named $DATA attribute and are looking for a named $DATA attribute.
+ if data_stream_name == name {
+ return Some(Ok(attribute));
+ }
+ }
+ (Some(Err(e)), _) => {
+ // We hit an error while fetching the $DATA attribute name.
+ return Some(Err(e));
+ }
+ _ => {
+ // In any other case, we didn't find what we are looking for.
+ continue;
+ }
+ }
+ }
+
+ None
+ }
+
+ /// Convenience function to return an [`NtfsIndex`] if this file is a directory.
+ ///
+ /// Apart from any propagated error, this function may return [`NtfsError::NotADirectory`]
+ /// if this [`NtfsFile`] is not a directory.
+ ///
+ /// If you need more control over the picked up $INDEX_ROOT and $INDEX_ALLOCATION attributes
+ /// you can use [`NtfsFile::attributes`] to iterate over all attributes of this file.
+ pub fn directory_index<'f, T>(
+ &'f self,
+ fs: &mut T,
+ ) -> Result<NtfsIndex<'n, 'f, NtfsFileNameIndex>>
+ where
+ T: Read + Seek,
+ {
+ if !self.flags().contains(NtfsFileFlags::IS_DIRECTORY) {
+ return Err(NtfsError::NotADirectory {
+ position: self.position(),
+ });
+ }
+
+ // Get the Index Root attribute that needs to exist.
+ let index_root = self
+ .attribute_by_ty(NtfsAttributeType::IndexRoot)?
+ .resident_structured_value::<NtfsIndexRoot>()?;
+
+ // 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
+ };
+
+ NtfsIndex::<NtfsFileNameIndex>::new(index_root, index_allocation)
+ }
+
pub(crate) fn first_attribute_offset(&self) -> u16 {
let start = offset_of!(FileRecordHeader, first_attribute_offset);
LittleEndian::read_u16(&self.record.data()[start..])
@@ -97,6 +202,25 @@ impl<'n> NtfsFile<'n> {
LittleEndian::read_u16(&self.record.data()[start..])
}
+ /// Convenience function to get the $STANDARD_INFORMATION attribute of this file
+ /// (see [`NtfsStandardInformation`]).
+ ///
+ /// 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>()
+ }
+
+ /// Convenience function to get the $FILE_NAME attribute of this file (see [`NtfsFileName`]).
+ ///
+ /// 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(crate) fn ntfs(&self) -> &'n Ntfs {
self.record.ntfs()
}
diff --git a/src/file_reference.rs b/src/file_reference.rs
index aa18764..836bfbf 100644
--- a/src/file_reference.rs
+++ b/src/file_reference.rs
@@ -1,6 +1,9 @@
// Copyright 2021 Colin Finck <colin@reactos.org>
// SPDX-License-Identifier: GPL-2.0-or-later
+use crate::error::Result;
+use crate::file::NtfsFile;
+use crate::ntfs::Ntfs;
use binread::io::{Read, Seek};
use binread::BinRead;
@@ -19,4 +22,12 @@ impl NtfsFileReference {
pub fn sequence_number(&self) -> u16 {
(u64::from_le_bytes(self.0) >> 48) as u16
}
+
+ /// Returns an [`NtfsFile`] for the file referenced by this object.
+ pub fn to_file<'n, T>(&self, ntfs: &'n Ntfs, fs: &mut T) -> Result<NtfsFile<'n>>
+ where
+ T: Read + Seek,
+ {
+ ntfs.file(fs, self.file_record_number())
+ }
}
diff --git a/src/index_entry.rs b/src/index_entry.rs
index 804a5d6..c84b67f 100644
--- a/src/index_entry.rs
+++ b/src/index_entry.rs
@@ -2,14 +2,18 @@
// SPDX-License-Identifier: GPL-2.0-or-later
use crate::error::Result;
+use crate::file::NtfsFile;
use crate::file_reference::NtfsFileReference;
use crate::indexes::{
NtfsIndexEntryData, NtfsIndexEntryHasData, NtfsIndexEntryHasFileReference, NtfsIndexEntryKey,
NtfsIndexEntryType,
};
+use crate::ntfs::Ntfs;
use crate::types::Vcn;
+use binread::io::{Read, Seek};
use bitflags::bitflags;
use byteorder::{ByteOrder, LittleEndian};
+use core::convert::TryInto;
use core::iter::FusedIterator;
use core::marker::PhantomData;
use core::mem;
@@ -43,6 +47,7 @@ bitflags! {
}
}
+#[derive(Clone, Debug)]
pub(crate) struct IndexEntryRange<E>
where
E: NtfsIndexEntryType,
@@ -128,6 +133,7 @@ where
LittleEndian::read_u16(&self.slice[start..])
}
+ /// Returns an [`NtfsFileReference`] for the file referenced by this index entry.
pub fn file_reference(&self) -> NtfsFileReference
where
E: NtfsIndexEntryHasFileReference,
@@ -186,6 +192,15 @@ where
Some(vcn)
}
+
+ /// Returns an [`NtfsFile`] for the file referenced by this index entry.
+ pub fn to_file<'n, T>(&self, ntfs: &'n Ntfs, fs: &mut T) -> Result<NtfsFile<'n>>
+ where
+ E: NtfsIndexEntryHasFileReference,
+ T: Read + Seek,
+ {
+ self.file_reference().to_file(ntfs, fs)
+ }
}
pub(crate) struct IndexNodeEntryRanges<E>
diff --git a/src/ntfs.rs b/src/ntfs.rs
index e1fcb0e..22b7306 100644
--- a/src/ntfs.rs
+++ b/src/ntfs.rs
@@ -106,9 +106,12 @@ impl Ntfs {
Ok(())
}
- /// Returns the root [`Dir`] of this NTFS volume.
- pub fn root_dir(&self) -> ! {
- panic!("TODO")
+ /// Returns the root directory of this NTFS volume as an [`NtfsFile`].
+ pub fn root_directory<'n, T>(&'n self, fs: &mut T) -> Result<NtfsFile<'n>>
+ where
+ T: Read + Seek,
+ {
+ self.file(fs, KnownNtfsFile::RootDirectory as u64)
}
/// Returns the size of a single sector in bytes.
@@ -144,19 +147,7 @@ impl Ntfs {
T: Read + Seek,
{
let volume_file = self.file(fs, KnownNtfsFile::Volume as u64)?;
- let attribute = volume_file
- .attributes()
- .find(|attribute| {
- // TODO: Replace by attribute.ty().contains() once https://github.com/rust-lang/rust/issues/62358 has landed.
- attribute
- .ty()
- .map(|ty| ty == NtfsAttributeType::VolumeInformation)
- .unwrap_or(false)
- })
- .ok_or(NtfsError::AttributeNotFound {
- position: volume_file.position(),
- ty: NtfsAttributeType::VolumeName,
- })?;
+ let attribute = volume_file.attribute_by_ty(NtfsAttributeType::VolumeInformation)?;
attribute.resident_structured_value::<NtfsVolumeInformation>()
}
@@ -169,13 +160,9 @@ impl Ntfs {
T: Read + Seek,
{
let volume_file = iter_try!(self.file(fs, KnownNtfsFile::Volume as u64));
- let attribute = volume_file.attributes().find(|attribute| {
- // TODO: Replace by attribute.ty().contains() once https://github.com/rust-lang/rust/issues/62358 has landed.
- attribute
- .ty()
- .map(|ty| ty == NtfsAttributeType::VolumeName)
- .unwrap_or(false)
- })?;
+ let attribute = volume_file
+ .attribute_by_ty(NtfsAttributeType::VolumeName)
+ .ok()?;
let volume_name = iter_try!(attribute.resident_structured_value::<NtfsVolumeName>());
Some(Ok(volume_name))
@@ -192,7 +179,7 @@ mod tests {
let ntfs = Ntfs::new(&mut testfs1).unwrap();
assert_eq!(ntfs.cluster_size(), 512);
assert_eq!(ntfs.sector_size(), 512);
- assert_eq!(ntfs.size(), 1049088);
+ assert_eq!(ntfs.size(), 2096640);
}
#[test]
diff --git a/src/upcase_table.rs b/src/upcase_table.rs
index 51464f7..9ee5883 100644
--- a/src/upcase_table.rs
+++ b/src/upcase_table.rs
@@ -36,19 +36,7 @@ impl UpcaseTable {
{
// Lookup the $UpCase file and its $DATA attribute.
let upcase_file = ntfs.file(fs, KnownNtfsFile::UpCase as u64)?;
- let data_attribute = upcase_file
- .attributes()
- .find(|attribute| {
- // TODO: Replace by attribute.ty().contains() once https://github.com/rust-lang/rust/issues/62358 has landed.
- attribute
- .ty()
- .map(|ty| ty == NtfsAttributeType::Data)
- .unwrap_or(false)
- })
- .ok_or(NtfsError::AttributeNotFound {
- position: upcase_file.position(),
- ty: NtfsAttributeType::VolumeName,
- })?;
+ let data_attribute = upcase_file.attribute_by_ty(NtfsAttributeType::Data)?;
if data_attribute.value_length() != UPCASE_TABLE_SIZE {
return Err(NtfsError::InvalidUpcaseTableSize {
expected: UPCASE_TABLE_SIZE,