use zerocopy::{FromBytes, Immutable, KnownLayout, LittleEndian, U32, U64, Unaligned};
use crate::attribute::NtfsAttributeType;
use crate::attribute_value::{NtfsAttributeValue, NtfsResidentAttributeValue};
use crate::error::{NtfsError, Result};
use crate::helpers::{ReadOnlyCursor, read_pod};
use crate::io::{Read, Seek};
use crate::structured_values::{
NtfsFileAttributeFlags, NtfsStructuredValue, NtfsStructuredValueFromResidentAttributeValue,
};
use crate::time::NtfsTime;
use crate::types::NtfsPosition;
const NTFS1_FIELDS_SIZE: usize = 48;
const ADDITIONAL_NTFS3_FIELDS_SIZE: usize = 24;
#[derive(Clone, Debug, FromBytes, Immutable, KnownLayout, Unaligned)]
#[repr(C, packed)]
struct Ntfs1Fields {
creation_time: NtfsTime,
modification_time: NtfsTime,
mft_record_modification_time: NtfsTime,
access_time: NtfsTime,
file_attributes: U32<LittleEndian>,
maximum_versions: U32<LittleEndian>,
version: U32<LittleEndian>,
class_id: U32<LittleEndian>,
}
#[derive(Clone, Debug, FromBytes, Immutable, KnownLayout, Unaligned)]
#[repr(C, packed)]
struct Ntfs3Fields {
owner_id: U32<LittleEndian>,
security_id: U32<LittleEndian>,
quota_charged: U64<LittleEndian>,
usn: U64<LittleEndian>,
}
#[derive(Clone, Debug)]
pub struct NtfsStandardInformation {
ntfs1_data: Ntfs1Fields,
ntfs3_data: Option<Ntfs3Fields>,
}
impl NtfsStandardInformation {
pub(crate) fn from_slice(slice: &[u8], position: NtfsPosition) -> Result<Self> {
let mut cursor = ReadOnlyCursor::new(slice);
Self::new(&mut cursor, position, slice.len() as u64)
}
fn new<T>(r: &mut T, position: NtfsPosition, value_length: u64) -> Result<Self>
where
T: Read,
{
if value_length < NTFS1_FIELDS_SIZE as u64 {
return Err(NtfsError::InvalidStructuredValueSize {
position,
ty: NtfsAttributeType::StandardInformation,
expected: NTFS1_FIELDS_SIZE as u64,
actual: value_length,
});
}
let ntfs1_data = read_pod::<T, Ntfs1Fields, NTFS1_FIELDS_SIZE>(r)?;
let mut ntfs3_data = None;
if value_length >= (NTFS1_FIELDS_SIZE + ADDITIONAL_NTFS3_FIELDS_SIZE) as u64 {
ntfs3_data = Some(read_pod::<T, Ntfs3Fields, ADDITIONAL_NTFS3_FIELDS_SIZE>(r)?);
}
Ok(Self {
ntfs1_data,
ntfs3_data,
})
}
pub fn access_time(&self) -> NtfsTime {
self.ntfs1_data.access_time
}
pub fn class_id(&self) -> u32 {
self.ntfs1_data.class_id.get()
}
pub fn creation_time(&self) -> NtfsTime {
self.ntfs1_data.creation_time
}
pub fn file_attributes(&self) -> NtfsFileAttributeFlags {
NtfsFileAttributeFlags::from_bits_truncate(self.ntfs1_data.file_attributes.get())
}
pub fn maximum_versions(&self) -> u32 {
self.ntfs1_data.maximum_versions.get()
}
pub fn mft_record_modification_time(&self) -> NtfsTime {
self.ntfs1_data.mft_record_modification_time
}
pub fn modification_time(&self) -> NtfsTime {
self.ntfs1_data.modification_time
}
pub fn owner_id(&self) -> Option<u32> {
self.ntfs3_data.as_ref().map(|x| x.owner_id.get())
}
pub fn quota_charged(&self) -> Option<u64> {
self.ntfs3_data.as_ref().map(|x| x.quota_charged.get())
}
pub fn security_id(&self) -> Option<u32> {
self.ntfs3_data.as_ref().map(|x| x.security_id.get())
}
pub fn usn(&self) -> Option<u64> {
self.ntfs3_data.as_ref().map(|x| x.usn.get())
}
pub fn version(&self) -> u32 {
self.ntfs1_data.version.get()
}
}
impl<'n, 'f> NtfsStructuredValue<'n, 'f> for NtfsStandardInformation {
const TY: NtfsAttributeType = NtfsAttributeType::StandardInformation;
fn from_attribute_value<T>(fs: &mut T, value: NtfsAttributeValue<'n, 'f>) -> Result<Self>
where
T: Read + Seek,
{
let position = value.data_position();
let value_length = value.len();
let mut value_attached = value.attach(fs);
Self::new(&mut value_attached, position, value_length)
}
}
impl<'n, 'f> NtfsStructuredValueFromResidentAttributeValue<'n, 'f> for NtfsStandardInformation {
fn from_resident_attribute_value(value: NtfsResidentAttributeValue<'f>) -> Result<Self> {
let position = value.data_position();
let value_length = value.len();
let mut cursor = ReadOnlyCursor::new(value.data());
Self::new(&mut cursor, position, value_length)
}
}