use core::fmt;
use bitflags::bitflags;
use zerocopy::{FromBytes, Immutable, KnownLayout, LittleEndian, U16, 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::{
NtfsStructuredValue, NtfsStructuredValueFromResidentAttributeValue,
};
use crate::types::NtfsPosition;
const VOLUME_INFORMATION_SIZE: usize = 12;
#[derive(Clone, Debug, FromBytes, Immutable, KnownLayout, Unaligned)]
#[repr(C, packed)]
struct VolumeInformationData {
_reserved: U64<LittleEndian>,
major_version: u8,
minor_version: u8,
flags: U16<LittleEndian>,
}
bitflags! {
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct NtfsVolumeFlags: u16 {
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;
}
}
impl fmt::Display for NtfsVolumeFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
#[derive(Clone, Debug)]
pub struct NtfsVolumeInformation {
info: VolumeInformationData,
}
impl NtfsVolumeInformation {
fn new<T>(r: &mut T, position: NtfsPosition, value_length: u64) -> Result<Self>
where
T: Read,
{
if value_length < VOLUME_INFORMATION_SIZE as u64 {
return Err(NtfsError::InvalidStructuredValueSize {
position,
ty: NtfsAttributeType::VolumeInformation,
expected: VOLUME_INFORMATION_SIZE as u64,
actual: value_length,
});
}
let info = read_pod::<T, VolumeInformationData, VOLUME_INFORMATION_SIZE>(r)?;
Ok(Self { info })
}
pub fn flags(&self) -> NtfsVolumeFlags {
NtfsVolumeFlags::from_bits_truncate(self.info.flags.get())
}
pub fn major_version(&self) -> u8 {
self.info.major_version
}
pub fn minor_version(&self) -> u8 {
self.info.minor_version
}
}
impl<'n, 'f> NtfsStructuredValue<'n, 'f> for NtfsVolumeInformation {
const TY: NtfsAttributeType = NtfsAttributeType::VolumeInformation;
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 NtfsVolumeInformation {
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)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn short_value_reports_volume_information_type() {
let mut cursor = ReadOnlyCursor::new(&[]);
let error = NtfsVolumeInformation::new(&mut cursor, NtfsPosition::none(), 0).unwrap_err();
assert!(matches!(
error,
NtfsError::InvalidStructuredValueSize {
ty: NtfsAttributeType::VolumeInformation,
expected,
actual: 0,
..
} if expected == VOLUME_INFORMATION_SIZE as u64
));
}
}