From d69510820adb71e02448801d30d3827993f30dcb Mon Sep 17 00:00:00 2001 From: Colin Finck Date: Sat, 7 Aug 2021 10:30:57 +0200 Subject: `ntfs_file` -> `file` for consistency --- src/attribute.rs | 2 +- src/file.rs | 156 ++++++++++++++++++++++++++ src/lib.rs | 4 +- src/ntfs.rs | 16 +-- src/ntfs_file.rs | 156 -------------------------- src/structured_values/file_name.rs | 6 +- src/structured_values/standard_information.rs | 6 +- src/upcase_table.rs | 4 +- 8 files changed, 173 insertions(+), 177 deletions(-) create mode 100644 src/file.rs delete mode 100644 src/ntfs_file.rs diff --git a/src/attribute.rs b/src/attribute.rs index 11f19de..5aa3dd9 100644 --- a/src/attribute.rs +++ b/src/attribute.rs @@ -5,7 +5,7 @@ use crate::attribute_value::{ NtfsAttributeValue, NtfsNonResidentAttributeValue, NtfsResidentAttributeValue, }; use crate::error::{NtfsError, Result}; -use crate::ntfs_file::NtfsFile; +use crate::file::NtfsFile; use crate::string::NtfsString; use crate::structured_values::{ NtfsStructuredValueFromNonResidentAttributeValue, NtfsStructuredValueFromSlice, diff --git a/src/file.rs b/src/file.rs new file mode 100644 index 0000000..770ca67 --- /dev/null +++ b/src/file.rs @@ -0,0 +1,156 @@ +// Copyright 2021 Colin Finck +// SPDX-License-Identifier: GPL-2.0-or-later + +use crate::attribute::NtfsAttributes; +use crate::error::{NtfsError, Result}; +use crate::ntfs::Ntfs; +use crate::record::{Record, RecordHeader}; +use binread::io::{Read, Seek, SeekFrom}; +use bitflags::bitflags; +use byteorder::{ByteOrder, LittleEndian}; +use memoffset::offset_of; + +#[repr(u64)] +pub enum KnownNtfsFile { + MFT = 0, + MFTMirr = 1, + LogFile = 2, + Volume = 3, + AttrDef = 4, + RootDirectory = 5, + Bitmap = 6, + Boot = 7, + BadClus = 8, + Secure = 9, + UpCase = 10, + Extend = 11, +} + +#[repr(C, packed)] +struct FileRecordHeader { + record_header: RecordHeader, + sequence_number: u16, + hard_link_count: u16, + first_attribute_offset: u16, + flags: u16, + used_size: u32, + allocated_size: u32, + base_file_record: u64, + next_attribute_number: u16, +} + +bitflags! { + pub struct NtfsFileFlags: u16 { + /// Record is in use. + const IN_USE = 0x0001; + /// Record is a directory. + const IS_DIRECTORY = 0x0002; + } +} + +#[derive(Debug)] +pub struct NtfsFile<'n> { + record: Record<'n>, +} + +impl<'n> NtfsFile<'n> { + pub(crate) fn new(ntfs: &'n Ntfs, fs: &mut T, position: u64) -> Result + where + T: Read + Seek, + { + let mut data = vec![0; ntfs.file_record_size() as usize]; + fs.seek(SeekFrom::Start(position))?; + fs.read_exact(&mut data)?; + + let mut record = Record::new(ntfs, data, position); + record.fixup()?; + + let file = Self { record }; + file.validate_signature()?; + file.validate_sizes()?; + + Ok(file) + } + + pub fn allocated_size(&self) -> u32 { + let start = offset_of!(FileRecordHeader, allocated_size); + LittleEndian::read_u32(&self.record.data()[start..]) + } + + pub fn attributes<'f>(&'f self) -> NtfsAttributes<'n, 'f> { + NtfsAttributes::new(self) + } + + pub(crate) fn first_attribute_offset(&self) -> u16 { + let start = offset_of!(FileRecordHeader, first_attribute_offset); + LittleEndian::read_u16(&self.record.data()[start..]) + } + + /// Returns flags set for this NTFS file as specified by [`NtfsFileFlags`]. + pub fn flags(&self) -> NtfsFileFlags { + let start = offset_of!(FileRecordHeader, flags); + NtfsFileFlags::from_bits_truncate(LittleEndian::read_u16(&self.record.data()[start..])) + } + + pub fn hard_link_count(&self) -> u16 { + let start = offset_of!(FileRecordHeader, hard_link_count); + LittleEndian::read_u16(&self.record.data()[start..]) + } + + pub(crate) fn ntfs(&self) -> &'n Ntfs { + self.record.ntfs() + } + + pub fn position(&self) -> u64 { + self.record.position() + } + + pub(crate) fn record_data(&self) -> &[u8] { + self.record.data() + } + + pub fn sequence_number(&self) -> u16 { + let start = offset_of!(FileRecordHeader, sequence_number); + LittleEndian::read_u16(&self.record.data()[start..]) + } + + pub fn used_size(&self) -> u32 { + let start = offset_of!(FileRecordHeader, used_size); + LittleEndian::read_u32(&self.record.data()[start..]) + } + + fn validate_signature(&self) -> Result<()> { + let signature = &self.record.signature(); + let expected = b"FILE"; + + if signature == expected { + Ok(()) + } else { + Err(NtfsError::InvalidFileSignature { + position: self.record.position(), + expected, + actual: *signature, + }) + } + } + + fn validate_sizes(&self) -> Result<()> { + if self.allocated_size() > self.record.len() { + return Err(NtfsError::InvalidFileAllocatedSize { + position: self.record.position(), + expected: self.allocated_size(), + actual: self.record.len(), + }); + } + + if self.used_size() > self.allocated_size() { + return Err(NtfsError::InvalidFileUsedSize { + position: self.record.position(), + expected: self.used_size(), + actual: self.allocated_size(), + }); + } + + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 70723fb..347932b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ mod attribute; mod attribute_value; mod boot_sector; mod error; +mod file; mod file_reference; mod guid; mod index; @@ -20,7 +21,6 @@ mod index_entry; mod index_record; pub mod indexes; mod ntfs; -mod ntfs_file; mod record; mod string; pub mod structured_values; @@ -32,11 +32,11 @@ mod upcase_table; pub use crate::attribute::*; pub use crate::attribute_value::*; pub use crate::error::*; +pub use crate::file::*; pub use crate::file_reference::*; pub use crate::guid::*; pub use crate::index::*; pub use crate::ntfs::*; -pub use crate::ntfs_file::*; pub use crate::string::*; pub use crate::time::*; pub use crate::traits::*; diff --git a/src/ntfs.rs b/src/ntfs.rs index ee2f7fc..e1fcb0e 100644 --- a/src/ntfs.rs +++ b/src/ntfs.rs @@ -4,7 +4,7 @@ use crate::attribute::NtfsAttributeType; use crate::boot_sector::BootSector; use crate::error::{NtfsError, Result}; -use crate::ntfs_file::{KnownNtfsFile, NtfsFile}; +use crate::file::{KnownNtfsFile, NtfsFile}; use crate::structured_values::{NtfsVolumeInformation, NtfsVolumeName}; use crate::upcase_table::UpcaseTable; use binread::io::{Read, Seek, SeekFrom}; @@ -66,10 +66,6 @@ impl Ntfs { self.cluster_size } - pub fn file_record_size(&self) -> u32 { - self.file_record_size - } - /// Returns the [`NtfsFile`] for the `n`-th NTFS file record. /// /// The first few NTFS files have fixed indexes and contain filesystem @@ -79,7 +75,7 @@ impl Ntfs { /// - Check if `n` can be u32 instead of u64. /// - Check if `n` should be in a newtype, with easier conversion from /// KnownNtfsFile. - pub fn ntfs_file<'n, T>(&'n self, fs: &mut T, n: u64) -> Result> + pub fn file<'n, T>(&'n self, fs: &mut T, n: u64) -> Result> where T: Read + Seek, { @@ -93,6 +89,10 @@ impl Ntfs { NtfsFile::new(&self, fs, position) } + pub fn file_record_size(&self) -> u32 { + self.file_record_size + } + /// Reads the $UpCase file from the filesystem and stores it in this [`Ntfs`] object. /// /// This function only needs to be called if case-insensitive comparisons are later performed @@ -143,7 +143,7 @@ impl Ntfs { where T: Read + Seek, { - let volume_file = self.ntfs_file(fs, KnownNtfsFile::Volume as u64)?; + let volume_file = self.file(fs, KnownNtfsFile::Volume as u64)?; let attribute = volume_file .attributes() .find(|attribute| { @@ -168,7 +168,7 @@ impl Ntfs { where T: Read + Seek, { - let volume_file = iter_try!(self.ntfs_file(fs, KnownNtfsFile::Volume as u64)); + 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 diff --git a/src/ntfs_file.rs b/src/ntfs_file.rs deleted file mode 100644 index 770ca67..0000000 --- a/src/ntfs_file.rs +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2021 Colin Finck -// SPDX-License-Identifier: GPL-2.0-or-later - -use crate::attribute::NtfsAttributes; -use crate::error::{NtfsError, Result}; -use crate::ntfs::Ntfs; -use crate::record::{Record, RecordHeader}; -use binread::io::{Read, Seek, SeekFrom}; -use bitflags::bitflags; -use byteorder::{ByteOrder, LittleEndian}; -use memoffset::offset_of; - -#[repr(u64)] -pub enum KnownNtfsFile { - MFT = 0, - MFTMirr = 1, - LogFile = 2, - Volume = 3, - AttrDef = 4, - RootDirectory = 5, - Bitmap = 6, - Boot = 7, - BadClus = 8, - Secure = 9, - UpCase = 10, - Extend = 11, -} - -#[repr(C, packed)] -struct FileRecordHeader { - record_header: RecordHeader, - sequence_number: u16, - hard_link_count: u16, - first_attribute_offset: u16, - flags: u16, - used_size: u32, - allocated_size: u32, - base_file_record: u64, - next_attribute_number: u16, -} - -bitflags! { - pub struct NtfsFileFlags: u16 { - /// Record is in use. - const IN_USE = 0x0001; - /// Record is a directory. - const IS_DIRECTORY = 0x0002; - } -} - -#[derive(Debug)] -pub struct NtfsFile<'n> { - record: Record<'n>, -} - -impl<'n> NtfsFile<'n> { - pub(crate) fn new(ntfs: &'n Ntfs, fs: &mut T, position: u64) -> Result - where - T: Read + Seek, - { - let mut data = vec![0; ntfs.file_record_size() as usize]; - fs.seek(SeekFrom::Start(position))?; - fs.read_exact(&mut data)?; - - let mut record = Record::new(ntfs, data, position); - record.fixup()?; - - let file = Self { record }; - file.validate_signature()?; - file.validate_sizes()?; - - Ok(file) - } - - pub fn allocated_size(&self) -> u32 { - let start = offset_of!(FileRecordHeader, allocated_size); - LittleEndian::read_u32(&self.record.data()[start..]) - } - - pub fn attributes<'f>(&'f self) -> NtfsAttributes<'n, 'f> { - NtfsAttributes::new(self) - } - - pub(crate) fn first_attribute_offset(&self) -> u16 { - let start = offset_of!(FileRecordHeader, first_attribute_offset); - LittleEndian::read_u16(&self.record.data()[start..]) - } - - /// Returns flags set for this NTFS file as specified by [`NtfsFileFlags`]. - pub fn flags(&self) -> NtfsFileFlags { - let start = offset_of!(FileRecordHeader, flags); - NtfsFileFlags::from_bits_truncate(LittleEndian::read_u16(&self.record.data()[start..])) - } - - pub fn hard_link_count(&self) -> u16 { - let start = offset_of!(FileRecordHeader, hard_link_count); - LittleEndian::read_u16(&self.record.data()[start..]) - } - - pub(crate) fn ntfs(&self) -> &'n Ntfs { - self.record.ntfs() - } - - pub fn position(&self) -> u64 { - self.record.position() - } - - pub(crate) fn record_data(&self) -> &[u8] { - self.record.data() - } - - pub fn sequence_number(&self) -> u16 { - let start = offset_of!(FileRecordHeader, sequence_number); - LittleEndian::read_u16(&self.record.data()[start..]) - } - - pub fn used_size(&self) -> u32 { - let start = offset_of!(FileRecordHeader, used_size); - LittleEndian::read_u32(&self.record.data()[start..]) - } - - fn validate_signature(&self) -> Result<()> { - let signature = &self.record.signature(); - let expected = b"FILE"; - - if signature == expected { - Ok(()) - } else { - Err(NtfsError::InvalidFileSignature { - position: self.record.position(), - expected, - actual: *signature, - }) - } - } - - fn validate_sizes(&self) -> Result<()> { - if self.allocated_size() > self.record.len() { - return Err(NtfsError::InvalidFileAllocatedSize { - position: self.record.position(), - expected: self.allocated_size(), - actual: self.record.len(), - }); - } - - if self.used_size() > self.allocated_size() { - return Err(NtfsError::InvalidFileUsedSize { - position: self.record.position(), - expected: self.used_size(), - actual: self.allocated_size(), - }); - } - - Ok(()) - } -} diff --git a/src/structured_values/file_name.rs b/src/structured_values/file_name.rs index 24dbbde..ad7787e 100644 --- a/src/structured_values/file_name.rs +++ b/src/structured_values/file_name.rs @@ -178,17 +178,15 @@ impl NtfsIndexEntryKey for NtfsFileName { #[cfg(test)] mod tests { use super::*; + use crate::file::KnownNtfsFile; use crate::ntfs::Ntfs; - use crate::ntfs_file::KnownNtfsFile; use crate::time::tests::NT_TIMESTAMP_2021_01_01; #[test] fn test_file_name() { let mut testfs1 = crate::helpers::tests::testfs1(); let ntfs = Ntfs::new(&mut testfs1).unwrap(); - let mft = ntfs - .ntfs_file(&mut testfs1, KnownNtfsFile::MFT as u64) - .unwrap(); + let mft = ntfs.file(&mut testfs1, KnownNtfsFile::MFT as u64).unwrap(); let mut mft_attributes = mft.attributes(); // Check the FileName attribute of the MFT. diff --git a/src/structured_values/standard_information.rs b/src/structured_values/standard_information.rs index bc533c5..3cb9876 100644 --- a/src/structured_values/standard_information.rs +++ b/src/structured_values/standard_information.rs @@ -125,16 +125,14 @@ impl<'s> NtfsStructuredValueFromSlice<'s> for NtfsStandardInformation { #[cfg(test)] mod tests { use super::*; + use crate::file::KnownNtfsFile; use crate::ntfs::Ntfs; - use crate::ntfs_file::KnownNtfsFile; #[test] fn test_standard_information() { let mut testfs1 = crate::helpers::tests::testfs1(); let ntfs = Ntfs::new(&mut testfs1).unwrap(); - let mft = ntfs - .ntfs_file(&mut testfs1, KnownNtfsFile::MFT as u64) - .unwrap(); + let mft = ntfs.file(&mut testfs1, KnownNtfsFile::MFT as u64).unwrap(); let mut mft_attributes = mft.attributes(); // Check the StandardInformation attribute of the MFT. diff --git a/src/upcase_table.rs b/src/upcase_table.rs index b612fa6..51464f7 100644 --- a/src/upcase_table.rs +++ b/src/upcase_table.rs @@ -3,8 +3,8 @@ use crate::attribute::NtfsAttributeType; use crate::error::{NtfsError, Result}; +use crate::file::KnownNtfsFile; use crate::ntfs::Ntfs; -use crate::ntfs_file::KnownNtfsFile; use crate::traits::NtfsReadSeek; use binread::io::{Read, Seek}; use core::convert::TryInto; @@ -35,7 +35,7 @@ impl UpcaseTable { T: Read + Seek, { // Lookup the $UpCase file and its $DATA attribute. - let upcase_file = ntfs.ntfs_file(fs, KnownNtfsFile::UpCase as u64)?; + let upcase_file = ntfs.file(fs, KnownNtfsFile::UpCase as u64)?; let data_attribute = upcase_file .attributes() .find(|attribute| { -- cgit v1.2.3