diff options
author | Colin Finck <colin@reactos.org> | 2021-04-21 22:41:28 +0300 |
---|---|---|
committer | Colin Finck <colin@reactos.org> | 2021-04-21 22:41:28 +0300 |
commit | a724f9775f268ef313982c73c712b389d3faf186 (patch) | |
tree | a0c16358af9470319077e8264b58556fd46137e6 | |
parent | 11be5cf34e0b455a4677c3eb8cf72032ac97fbe6 (diff) |
Implement `NtfsVolumeInformation` and `NtfsVolumeName` structured values
-rw-r--r-- | src/attribute.rs | 13 | ||||
-rw-r--r-- | src/structured_values/mod.rs | 2 | ||||
-rw-r--r-- | src/structured_values/volume_information.rs | 73 | ||||
-rw-r--r-- | src/structured_values/volume_name.rs | 81 |
4 files changed, 167 insertions, 2 deletions
diff --git a/src/attribute.rs b/src/attribute.rs index 9be8ebe..37d5582 100644 --- a/src/attribute.rs +++ b/src/attribute.rs @@ -7,6 +7,7 @@ use crate::ntfs_file::NtfsFile; use crate::string::NtfsString; use crate::structured_values::{ NtfsFileName, NtfsObjectId, NtfsStandardInformation, NtfsStructuredValue, + NtfsVolumeInformation, NtfsVolumeName, }; use binread::io::{Read, Seek, SeekFrom}; use binread::{BinRead, BinReaderExt}; @@ -253,8 +254,16 @@ impl NtfsAttribute { Ok(NtfsStructuredValue::ObjectId(inner)) } NtfsAttributeType::SecurityDescriptor => panic!("TODO"), - NtfsAttributeType::VolumeName => panic!("TODO"), - NtfsAttributeType::VolumeInformation => panic!("TODO"), + NtfsAttributeType::VolumeName => { + let inner = + NtfsVolumeName::new(self.position, attached_value, self.value_length())?; + Ok(NtfsStructuredValue::VolumeName(inner)) + } + NtfsAttributeType::VolumeInformation => { + let inner = + NtfsVolumeInformation::new(self.position, attached_value, self.value_length())?; + Ok(NtfsStructuredValue::VolumeInformation(inner)) + } ty => Err(NtfsError::UnsupportedStructuredValue { position: self.position, ty, diff --git a/src/structured_values/mod.rs b/src/structured_values/mod.rs index 4a18d91..99b3cee 100644 --- a/src/structured_values/mod.rs +++ b/src/structured_values/mod.rs @@ -42,4 +42,6 @@ pub enum NtfsStructuredValue { StandardInformation(NtfsStandardInformation), FileName(NtfsFileName), ObjectId(NtfsObjectId), + VolumeInformation(NtfsVolumeInformation), + VolumeName(NtfsVolumeName), } diff --git a/src/structured_values/volume_information.rs b/src/structured_values/volume_information.rs index 53769a7..57243ce 100644 --- a/src/structured_values/volume_information.rs +++ b/src/structured_values/volume_information.rs @@ -1,2 +1,75 @@ // Copyright 2021 Colin Finck <colin@reactos.org> // SPDX-License-Identifier: GPL-2.0-or-later + +use crate::attribute::NtfsAttributeType; +use crate::attribute_value::NtfsAttributeValueAttached; +use crate::error::{NtfsError, Result}; +use binread::io::{Read, Seek}; +use binread::{BinRead, BinReaderExt}; +use bitflags::bitflags; + +/// Size of all [`VolumeInformationData`] fields. +const VOLUME_INFORMATION_SIZE: u64 = 12; + +#[derive(BinRead, Clone, Debug)] +struct VolumeInformationData { + reserved: u64, + major_version: u8, + minor_version: u8, + flags: u16, +} + +bitflags! { + pub struct NtfsVolumeFlags: u16 { + /// The volume needs to be checked by `chkdsk`. + const IS_DIRTY = 0x0001; + const RESIZE_LOG_FILE = 0x0002; + const UPGRADE_ON_MOUNT = 0x0004; + const MOUNTED_ON_NT4 = 0x0008; + const DELETE_USN_UNDERWAY = 0x0010; + const REPAIR_OBJECT_ID = 0x0020; + const CHKDSK_UNDERWAY = 0x4000; + const MODIFIED_BY_CHKDSK = 0x8000; + } +} + +#[derive(Clone, Debug)] +pub struct NtfsVolumeInformation { + data: VolumeInformationData, +} + +impl NtfsVolumeInformation { + pub(crate) fn new<T>( + attribute_position: u64, + mut value_attached: NtfsAttributeValueAttached<'_, T>, + value_length: u64, + ) -> Result<Self> + where + T: Read + Seek, + { + if value_length < VOLUME_INFORMATION_SIZE { + return Err(NtfsError::InvalidAttributeSize { + position: attribute_position, + ty: NtfsAttributeType::StandardInformation, + expected: VOLUME_INFORMATION_SIZE, + actual: value_length, + }); + } + + let data = value_attached.read_le::<VolumeInformationData>()?; + + Ok(Self { data }) + } + + pub fn flags(&self) -> NtfsVolumeFlags { + NtfsVolumeFlags::from_bits_truncate(self.data.flags) + } + + pub fn major_version(&self) -> u8 { + self.data.major_version + } + + pub fn minor_version(&self) -> u8 { + self.data.minor_version + } +} diff --git a/src/structured_values/volume_name.rs b/src/structured_values/volume_name.rs index 53769a7..72bd3bc 100644 --- a/src/structured_values/volume_name.rs +++ b/src/structured_values/volume_name.rs @@ -1,2 +1,83 @@ // Copyright 2021 Colin Finck <colin@reactos.org> // SPDX-License-Identifier: GPL-2.0-or-later + +use crate::attribute::NtfsAttributeType; +use crate::attribute_value::NtfsAttributeValueAttached; +use crate::error::{NtfsError, Result}; +use crate::string::NtfsString; +use binread::io::{Read, Seek, SeekFrom}; +use core::mem; + +/// The smallest VolumeName attribute has a name containing just a single character. +const VOLUME_NAME_MIN_SIZE: u64 = mem::size_of::<u16>() as u64; + +/// The largest VolumeName attribute has a name containing 128 UTF-16 code points (256 bytes). +const VOLUME_NAME_MAX_SIZE: u64 = 128 * mem::size_of::<u16>() as u64; + +#[derive(Clone, Debug)] +pub struct NtfsVolumeName { + name_position: u64, + name_length: u16, +} + +impl NtfsVolumeName { + pub(crate) fn new<T>( + attribute_position: u64, + value_attached: NtfsAttributeValueAttached<'_, T>, + value_length: u64, + ) -> Result<Self> + where + T: Read + Seek, + { + if value_length < VOLUME_NAME_MIN_SIZE { + return Err(NtfsError::InvalidAttributeSize { + position: attribute_position, + ty: NtfsAttributeType::VolumeName, + expected: VOLUME_NAME_MIN_SIZE, + actual: value_length, + }); + } else if value_length > VOLUME_NAME_MAX_SIZE { + return Err(NtfsError::InvalidAttributeSize { + position: attribute_position, + ty: NtfsAttributeType::VolumeName, + expected: VOLUME_NAME_MAX_SIZE, + actual: value_length, + }); + } + + let name_position = value_attached.position(); + let name_length = value_length as u16; + + Ok(Self { + name_position, + name_length, + }) + } + + /// Returns the volume name length, in bytes. + /// + /// A volume name has a maximum length of 128 UTF-16 code points (256 bytes). + pub fn name_length(&self) -> usize { + self.name_length as usize + } + + /// Reads the volume name into the given buffer, and returns an + /// [`NtfsString`] wrapping that buffer. + pub fn read_name<'a, T>(&self, fs: &mut T, buf: &'a mut [u8]) -> Result<NtfsString<'a>> + where + T: Read + Seek, + { + let name_length = self.name_length(); + if buf.len() < name_length { + return Err(NtfsError::BufferTooSmall { + expected: name_length, + actual: buf.len(), + }); + } + + fs.seek(SeekFrom::Start(self.name_position))?; + fs.read_exact(&mut buf[..name_length])?; + + Ok(NtfsString(&buf[..name_length])) + } +} |