diff options
author | Colin Finck <colin@reactos.org> | 2021-06-05 17:01:57 +0300 |
---|---|---|
committer | Colin Finck <colin@reactos.org> | 2021-06-05 17:01:57 +0300 |
commit | 6d174c7910237a592cf811ab5d406599130401d6 (patch) | |
tree | 3e6126a3d03b4a010aa43013e9f12351091a608e /src | |
parent | 4d4357d22497943e535fe16ca299d2ff6644f431 (diff) |
Add `Lcn`, `Vcn` to simplify cluster calculations and make them typesafe
Diffstat (limited to 'src')
-rw-r--r-- | src/attribute.rs | 5 | ||||
-rw-r--r-- | src/attribute_value.rs | 59 | ||||
-rw-r--r-- | src/boot_sector.rs | 7 | ||||
-rw-r--r-- | src/error.rs | 17 | ||||
-rw-r--r-- | src/index_entry.rs | 7 | ||||
-rw-r--r-- | src/index_record.rs | 7 | ||||
-rw-r--r-- | src/lib.rs | 2 | ||||
-rw-r--r-- | src/ntfs.rs | 9 | ||||
-rw-r--r-- | src/types.rs | 63 |
9 files changed, 131 insertions, 45 deletions
diff --git a/src/attribute.rs b/src/attribute.rs index 1b82a36..bb28ce7 100644 --- a/src/attribute.rs +++ b/src/attribute.rs @@ -10,6 +10,7 @@ use crate::structured_values::{ NewNtfsStructuredValue, NtfsFileName, NtfsIndexAllocation, NtfsIndexRoot, NtfsObjectId, NtfsStandardInformation, NtfsStructuredValue, NtfsVolumeInformation, NtfsVolumeName, }; +use crate::types::Vcn; use binread::io::{Read, Seek, SeekFrom}; use binread::{BinRead, BinReaderExt}; use bitflags::bitflags; @@ -74,11 +75,11 @@ struct NtfsAttributeNonResidentHeader { /// 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: i64, + lowest_vcn: Vcn, /// Upper 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 (or even -1 for zero-length files according to NTFS-3G). - highest_vcn: i64, + highest_vcn: Vcn, /// Offset to the beginning of the value data runs. data_runs_offset: u16, /// Binary exponent denoting the number of clusters in a compression unit. diff --git a/src/attribute_value.rs b/src/attribute_value.rs index 9516eaa..a41f1ca 100644 --- a/src/attribute_value.rs +++ b/src/attribute_value.rs @@ -4,6 +4,7 @@ use crate::error::{NtfsError, Result}; use crate::ntfs::Ntfs; use crate::traits::NtfsReadSeek; +use crate::types::Lcn; use binread::io; use binread::io::{Read, Seek, SeekFrom}; use binread::BinReaderExt; @@ -132,12 +133,13 @@ impl NtfsDataRun { } } - pub(crate) fn from_lcn_info(ntfs: &Ntfs, lcn_position: i64, lcn_count: u64) -> Self { - debug_assert!(lcn_position >= 0); - let position = lcn_position as u64 * ntfs.cluster_size() as u64; - let length = lcn_count * ntfs.cluster_size() as u64; + pub(crate) fn from_lcn_info(ntfs: &Ntfs, lcn: Lcn, cluster_count: u64) -> Result<Self> { + let position = lcn.position(ntfs)?; + let length = cluster_count + .checked_mul(ntfs.cluster_size() as u64) + .ok_or(NtfsError::InvalidClusterCount { cluster_count })?; - Self::from_byte_info(position, length) + Ok(Self::from_byte_info(position, length)) } pub fn data_position(&self) -> u64 { @@ -220,7 +222,7 @@ impl NtfsReadSeek for NtfsDataRun { pub struct NtfsDataRuns<'n> { ntfs: &'n Ntfs, data_runs_range: Range<u64>, - previous_lcn_position: i64, + previous_lcn: Lcn, } impl<'n> NtfsDataRuns<'n> { @@ -228,7 +230,7 @@ impl<'n> NtfsDataRuns<'n> { Self { ntfs, data_runs_range, - previous_lcn_position: 0, + previous_lcn: Lcn::from(0), } } @@ -259,35 +261,32 @@ impl<'n> NtfsDataRuns<'n> { return None; } - // The lower nibble indicates the length of the following LCN count variable length integer. - let lcn_count_byte_count = header & 0x0f; - let lcn_count = - iter_try!(self.read_variable_length_unsigned_integer(fs, lcn_count_byte_count)); - size += lcn_count_byte_count as u64; - - // The upper nibble indicates the length of the following LCN offset variable length integer. - let lcn_offset_byte_count = (header & 0xf0) >> 4; - let lcn_offset = - iter_try!(self.read_variable_length_signed_integer(fs, lcn_offset_byte_count)); - size += lcn_offset_byte_count as u64; - - // The read LCN offset is relative to the previous one. - // Turn it into an absolute one. - let lcn_position = self.previous_lcn_position + lcn_offset; - if lcn_position < 0 { - return Some(Err(NtfsError::InvalidLcnPositionInDataRunHeader { - position: self.data_runs_range.start, - lcn_position, - })); - } + // The lower nibble indicates the length of the following cluster count variable length integer. + let cluster_count_byte_count = header & 0x0f; + let cluster_count = + iter_try!(self.read_variable_length_unsigned_integer(fs, cluster_count_byte_count)); + size += cluster_count_byte_count as u64; + + // The upper nibble indicates the length of the following VCN variable length integer. + let vcn_byte_count = (header & 0xf0) >> 4; + let vcn = iter_try!(self.read_variable_length_signed_integer(fs, vcn_byte_count)).into(); + size += vcn_byte_count as u64; - self.previous_lcn_position = lcn_position; + // Turn the read VCN into an absolute LCN. + let lcn = iter_try!(self.previous_lcn.checked_add(vcn).ok_or({ + NtfsError::InvalidVcnInDataRunHeader { + position: self.data_runs_range.start, + vcn, + previous_lcn: self.previous_lcn, + } + })); + self.previous_lcn = lcn; // Only increment `self.data_runs_range.start` after having checked for success. // In case of an error, a subsequent call shall output the same error again. self.data_runs_range.start += size; - let data_run = NtfsDataRun::from_lcn_info(self.ntfs, lcn_position, lcn_count); + let data_run = iter_try!(NtfsDataRun::from_lcn_info(self.ntfs, lcn, cluster_count)); Some(Ok(data_run)) } diff --git a/src/boot_sector.rs b/src/boot_sector.rs index e9bf0b9..0132d9e 100644 --- a/src/boot_sector.rs +++ b/src/boot_sector.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later use crate::error::{NtfsError, Result}; +use crate::types::Lcn; use binread::BinRead; use memoffset::offset_of; @@ -27,8 +28,8 @@ pub(crate) struct BiosParameterBlock { extended_boot_signature: u8, reserved: u8, total_sectors: u64, - mft_lcn: u64, - mft_mirror_lcn: u64, + mft_lcn: Lcn, + mft_mirror_lcn: Lcn, file_record_size_info: i8, zeros_4: [u8; 3], index_record_size_info: i8, @@ -60,7 +61,7 @@ impl BiosParameterBlock { } /// Returns the Logical Cluster Number (LCN) to the beginning of the Master File Table (MFT). - pub(crate) fn mft_lcn(&self) -> u64 { + pub(crate) fn mft_lcn(&self) -> Lcn { self.mft_lcn } diff --git a/src/error.rs b/src/error.rs index 99a4e61..98923f5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later use crate::attribute::NtfsAttributeType; +use crate::types::{Lcn, Vcn}; use displaydoc::Display; /// Central result type of ntfs. @@ -24,9 +25,8 @@ pub enum NtfsError { expected: u8, actual: u8, }, - /// An invalid LCN position {lcn_position} was calculated from the NTFS data run header at - /// byte position {position:#010x} (and previous data runs) - InvalidLcnPositionInDataRunHeader { position: u64, lcn_position: i64 }, + /// The cluster count {cluster_count} is too big + InvalidClusterCount { cluster_count: u64 }, /// The requested NTFS file {n} is invalid InvalidNtfsFile { n: u64 }, /// The NTFS file record at byte position {position:#010x} should have signature {expected:?}, but it has signature {actual:?} @@ -64,8 +64,17 @@ pub enum NtfsError { expected: &'static [u8], actual: [u8; 2], }, + /// The VCN {vcn} read from the NTFS data run header at byte position {position:#010x} + /// cannot be added to the LCN {previous_lcn} calculated from previous data runs + InvalidVcnInDataRunHeader { + position: u64, + vcn: Vcn, + previous_lcn: Lcn, + }, /// I/O error: {0:?} Io(binread::io::Error), + /// The Logical Cluster Number (LCN) {lcn} is too big to be processed + LcnTooBig { lcn: Lcn }, /// The cluster size is {actual} bytes, but the maximum supported one is {expected} UnsupportedClusterSize { expected: u32, actual: u32 }, /// The type of the NTFS attribute at byte position {position:#010x} is {actual:#010x}, which is not supported @@ -77,6 +86,8 @@ pub enum NtfsError { position: u64, ty: NtfsAttributeType, }, + /// The Virtual Cluster Number (VCN) {vcn} is too big to be processed + VcnTooBig { vcn: Vcn }, } impl From<binread::error::Error> for NtfsError { diff --git a/src/index_entry.rs b/src/index_entry.rs index f1a3ad3..98c2c7e 100644 --- a/src/index_entry.rs +++ b/src/index_entry.rs @@ -6,6 +6,7 @@ use crate::error::Result; use crate::ntfs::Ntfs; use crate::structured_values::NewNtfsStructuredValue; use crate::traits::NtfsReadSeek; +use crate::types::Vcn; use binread::io::{Read, Seek, SeekFrom}; use binread::{BinRead, BinReaderExt}; use bitflags::bitflags; @@ -99,7 +100,7 @@ where /// 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>> + pub fn subnode_vcn<T>(&self, fs: &mut T) -> Option<Result<Vcn>> where T: Read + Seek, { @@ -110,9 +111,9 @@ where // 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 + self.index_entry_length() as i64 - mem::size_of::<Vcn>() as i64 ))); - let vcn = iter_try!(value_attached.read_le::<u64>()); + let vcn = iter_try!(value_attached.read_le::<Vcn>()); Some(Ok(vcn)) } diff --git a/src/index_record.rs b/src/index_record.rs index 2a49cb9..e5906e3 100644 --- a/src/index_record.rs +++ b/src/index_record.rs @@ -8,6 +8,7 @@ use crate::ntfs::Ntfs; use crate::record::RecordHeader; use crate::structured_values::NewNtfsStructuredValue; use crate::traits::NtfsReadSeek; +use crate::types::Vcn; use binread::io::{Read, Seek, SeekFrom}; use binread::{BinRead, BinReaderExt}; @@ -18,7 +19,7 @@ const INDEX_RECORD_HEADER_SIZE: u32 = 24; #[derive(BinRead, Clone, Debug)] struct IndexRecordHeader { record_header: RecordHeader, - vcn: u64, + vcn: Vcn, } /// Size of all [`IndexNodeHeader`] fields plus some reserved bytes. @@ -142,4 +143,8 @@ impl<'n> NtfsIndexRecord<'n> { Ok(()) } + + pub fn vcn(&self) -> Vcn { + self.index_record_header.vcn + } } @@ -23,6 +23,7 @@ mod string; pub mod structured_values; mod time; mod traits; +mod types; pub use crate::attribute::*; pub use crate::attribute_value::*; @@ -34,3 +35,4 @@ pub use crate::string::*; pub use crate::structured_values::*; pub use crate::time::*; pub use crate::traits::*; +pub use crate::types::*; diff --git a/src/ntfs.rs b/src/ntfs.rs index c14dc1f..ccd6ae9 100644 --- a/src/ntfs.rs +++ b/src/ntfs.rs @@ -40,18 +40,21 @@ impl Ntfs { let cluster_size = bpb.cluster_size()?; let sector_size = bpb.sector_size(); let size = bpb.total_sectors() * sector_size as u64; - let mft_position = bpb.mft_lcn() * cluster_size as u64; + let mft_position = 0; let file_record_size = bpb.file_record_size()?; let serial_number = bpb.serial_number(); - Ok(Self { + let mut ntfs = Self { cluster_size, sector_size, size, mft_position, file_record_size, serial_number, - }) + }; + ntfs.mft_position = bpb.mft_lcn().position(&ntfs)?; + + Ok(ntfs) } /// Returns the size of a single cluster, in bytes. diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..d327484 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,63 @@ +// Copyright 2021 Colin Finck <colin@reactos.org> +// SPDX-License-Identifier: GPL-2.0-or-later + +use crate::error::{NtfsError, Result}; +use crate::ntfs::Ntfs; +use binread::BinRead; +use core::fmt; + +#[derive(BinRead, Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct Lcn(u64); + +impl Lcn { + pub fn checked_add(&self, vcn: Vcn) -> Option<Lcn> { + if vcn.0 >= 0 { + self.0.checked_add(vcn.0 as u64).map(Into::into) + } else { + self.0 + .checked_sub(vcn.0.wrapping_neg() as u64) + .map(Into::into) + } + } + + pub fn position(&self, ntfs: &Ntfs) -> Result<u64> { + self.0 + .checked_mul(ntfs.cluster_size() as u64) + .ok_or(NtfsError::LcnTooBig { lcn: *self }) + } +} + +impl fmt::Display for Lcn { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl From<u64> for Lcn { + fn from(value: u64) -> Self { + Self(value) + } +} + +#[derive(BinRead, Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct Vcn(i64); + +impl Vcn { + pub fn offset(&self, ntfs: &Ntfs) -> Result<i64> { + self.0 + .checked_mul(ntfs.cluster_size() as i64) + .ok_or(NtfsError::VcnTooBig { vcn: *self }) + } +} + +impl fmt::Display for Vcn { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl From<i64> for Vcn { + fn from(value: i64) -> Self { + Self(value) + } +} |