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
path: root/src
diff options
context:
space:
mode:
authorColin Finck <colin@reactos.org>2021-06-05 17:01:57 +0300
committerColin Finck <colin@reactos.org>2021-06-05 17:01:57 +0300
commit6d174c7910237a592cf811ab5d406599130401d6 (patch)
tree3e6126a3d03b4a010aa43013e9f12351091a608e /src
parent4d4357d22497943e535fe16ca299d2ff6644f431 (diff)
Add `Lcn`, `Vcn` to simplify cluster calculations and make them typesafe
Diffstat (limited to 'src')
-rw-r--r--src/attribute.rs5
-rw-r--r--src/attribute_value.rs59
-rw-r--r--src/boot_sector.rs7
-rw-r--r--src/error.rs17
-rw-r--r--src/index_entry.rs7
-rw-r--r--src/index_record.rs7
-rw-r--r--src/lib.rs2
-rw-r--r--src/ntfs.rs9
-rw-r--r--src/types.rs63
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
+ }
}
diff --git a/src/lib.rs b/src/lib.rs
index 0cd1dbe..f086aca 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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)
+ }
+}