use std::io::{Read, Seek};
use crate::attribute::FileAttributeFlags;
use crate::err::{Error, Result};
use log::trace;
use byteorder::{LittleEndian, ReadBytesExt};
use encoding::all::UTF_16LE;
use encoding::{DecoderTrap, Encoding};
use jiff::Timestamp;
use num_traits::FromPrimitive;
use serde::Serialize;
use winstructs::ntfs::mft_reference::MftReference;
#[derive(FromPrimitive, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[repr(u8)]
pub enum FileNamespace {
POSIX = 0,
Win32 = 1,
DOS = 2,
Win32AndDos = 3,
}
#[derive(Serialize, Clone, Debug, PartialEq)]
pub struct FileNameAttr {
pub parent: MftReference,
#[serde(serialize_with = "crate::utils::serialize_timestamp_chrono_compat")]
pub created: Timestamp,
#[serde(serialize_with = "crate::utils::serialize_timestamp_chrono_compat")]
pub modified: Timestamp,
#[serde(serialize_with = "crate::utils::serialize_timestamp_chrono_compat")]
pub mft_modified: Timestamp,
#[serde(serialize_with = "crate::utils::serialize_timestamp_chrono_compat")]
pub accessed: Timestamp,
pub logical_size: u64,
pub physical_size: u64,
pub flags: FileAttributeFlags,
pub reparse_value: u32,
pub name_length: u8,
pub namespace: FileNamespace,
pub name: String,
}
impl FileNameAttr {
pub fn from_stream<S: Read + Seek>(stream: &mut S) -> Result<FileNameAttr> {
trace!("Offset {}: FilenameAttr", stream.stream_position()?);
let parent =
MftReference::from_reader(stream).map_err(Error::failed_to_read_mft_reference)?;
let created =
crate::utils::windows_filetime_to_timestamp(stream.read_u64::<LittleEndian>()?)?;
let modified =
crate::utils::windows_filetime_to_timestamp(stream.read_u64::<LittleEndian>()?)?;
let mft_modified =
crate::utils::windows_filetime_to_timestamp(stream.read_u64::<LittleEndian>()?)?;
let accessed =
crate::utils::windows_filetime_to_timestamp(stream.read_u64::<LittleEndian>()?)?;
let logical_size = stream.read_u64::<LittleEndian>()?;
let physical_size = stream.read_u64::<LittleEndian>()?;
let flags = FileAttributeFlags::from_bits_truncate(stream.read_u32::<LittleEndian>()?);
let reparse_value = stream.read_u32::<LittleEndian>()?;
let name_length = stream.read_u8()?;
let namespace = stream.read_u8()?;
let namespace =
FileNamespace::from_u8(namespace).ok_or(Error::UnknownNamespace { namespace })?;
let mut name_buffer = vec![0; name_length as usize * 2];
stream.read_exact(&mut name_buffer)?;
let name = match UTF_16LE.decode(&name_buffer, DecoderTrap::Ignore) {
Ok(s) => s,
Err(_e) => return Err(Error::InvalidFilename {}),
};
Ok(FileNameAttr {
parent,
created,
modified,
mft_modified,
accessed,
logical_size,
physical_size,
flags,
reparse_value,
name_length,
namespace,
name,
})
}
}