use binrw::io::{Read, Seek, SeekFrom};
use super::{DataRunsState, NtfsDataRuns, StreamState};
use crate::attribute::{NtfsAttribute, NtfsAttributeType};
use crate::error::{NtfsError, Result};
use crate::file::NtfsFile;
use crate::ntfs::Ntfs;
use crate::structured_values::{NtfsAttributeListEntries, NtfsAttributeListEntry};
use crate::traits::NtfsReadSeek;
use crate::types::NtfsPosition;
#[derive(Clone, Debug)]
pub struct NtfsAttributeListNonResidentAttributeValue<'n, 'f> {
ntfs: &'n Ntfs,
initial_attribute_list_entries: NtfsAttributeListEntries<'n, 'f>,
connected_entries: AttributeListConnectedEntries<'n, 'f>,
data_size: u64,
attribute_state: Option<AttributeState<'n>>,
stream_state: StreamState,
}
impl<'n, 'f> NtfsAttributeListNonResidentAttributeValue<'n, 'f> {
pub(crate) fn new<T>(
ntfs: &'n Ntfs,
fs: &mut T,
attribute_list_entries: NtfsAttributeListEntries<'n, 'f>,
instance: u16,
ty: NtfsAttributeType,
data_size: u64,
) -> Result<Self>
where
T: Read + Seek,
{
let connected_entries =
AttributeListConnectedEntries::new(attribute_list_entries.clone(), instance, ty);
let stream_state = StreamState::new(data_size);
let mut value = Self {
ntfs,
initial_attribute_list_entries: attribute_list_entries,
connected_entries,
data_size,
attribute_state: None,
stream_state,
};
value.next_attribute(fs)?;
Ok(value)
}
pub fn data_position(&self) -> NtfsPosition {
self.stream_state.data_position()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn len(&self) -> u64 {
self.data_size
}
fn next_data_run(&mut self) -> Result<bool> {
let attribute_state = match &mut self.attribute_state {
Some(attribute_state) => attribute_state,
None => return Ok(false),
};
let data_runs_state = match attribute_state.data_runs_state.take() {
Some(data_runs_state) => data_runs_state,
None => return Ok(false),
};
let attribute = NtfsAttribute::new(
&attribute_state.file,
attribute_state.attribute_offset,
None,
)?;
let (data, position) = attribute.non_resident_value_data_and_position()?;
let mut stream_data_runs =
NtfsDataRuns::from_state(self.ntfs, data, position, data_runs_state);
let stream_data_run = match stream_data_runs.next() {
Some(stream_data_run) => stream_data_run,
None => return Ok(false),
};
let stream_data_run = stream_data_run?;
self.stream_state.set_stream_data_run(Some(stream_data_run));
attribute_state.data_runs_state = Some(stream_data_runs.into_state());
Ok(true)
}
fn next_attribute<T>(&mut self, fs: &mut T) -> Result<bool>
where
T: Read + Seek,
{
let entry = match self.connected_entries.next(fs) {
Some(entry) => entry,
None => return Ok(false),
};
let entry = entry?;
let file = entry.to_file(self.ntfs, fs)?;
let attribute = entry.to_attribute(&file)?;
let attribute_offset = attribute.offset();
if attribute.is_resident() {
return Err(NtfsError::UnexpectedResidentAttribute {
position: attribute.position(),
});
}
let (data, position) = attribute.non_resident_value_data_and_position()?;
let mut stream_data_runs = NtfsDataRuns::new(self.ntfs, data, position);
let stream_data_run = match stream_data_runs.next() {
Some(stream_data_run) => stream_data_run,
None => return Ok(false),
};
let stream_data_run = stream_data_run?;
self.stream_state.set_stream_data_run(Some(stream_data_run));
let data_runs_state = Some(stream_data_runs.into_state());
self.attribute_state = Some(AttributeState {
file,
attribute_offset,
data_runs_state,
});
Ok(true)
}
pub fn ntfs(&self) -> &'n Ntfs {
self.ntfs
}
fn rewind<T>(&mut self, fs: &mut T) -> Result<()>
where
T: Read + Seek,
{
self.connected_entries.attribute_list_entries =
Some(self.initial_attribute_list_entries.clone());
self.stream_state = StreamState::new(self.len());
self.next_attribute(fs)?;
Ok(())
}
}
impl<'n, 'f> NtfsReadSeek for NtfsAttributeListNonResidentAttributeValue<'n, 'f> {
fn read<T>(&mut self, fs: &mut T, buf: &mut [u8]) -> Result<usize>
where
T: Read + Seek,
{
let mut bytes_read = 0usize;
while bytes_read < buf.len() {
if self.stream_state.read_data_run(fs, buf, &mut bytes_read)? {
continue;
}
if self.next_data_run()? {
continue;
}
if self.next_attribute(fs)? {
continue;
} else {
break;
}
}
Ok(bytes_read)
}
fn seek<T>(&mut self, fs: &mut T, pos: SeekFrom) -> Result<u64>
where
T: Read + Seek,
{
let pos = self.stream_state.optimize_seek(pos, self.len())?;
let mut bytes_left_to_seek = match pos {
SeekFrom::Start(n) => {
self.rewind(fs)?;
n
}
SeekFrom::Current(n) if n >= 0 => n as u64,
_ => unreachable!(),
};
while bytes_left_to_seek > 0 {
if self
.stream_state
.seek_data_run(fs, pos, &mut bytes_left_to_seek)?
{
break;
}
if self.next_data_run()? {
continue;
}
if self.next_attribute(fs)? {
continue;
} else {
self.stream_state.set_stream_data_run(None);
break;
}
}
match pos {
SeekFrom::Start(n) => self.stream_state.set_stream_position(n),
SeekFrom::Current(n) => self
.stream_state
.set_stream_position(self.stream_position() + n as u64),
_ => unreachable!(),
}
Ok(self.stream_position())
}
fn stream_position(&self) -> u64 {
self.stream_state.stream_position()
}
}
#[derive(Clone, Debug)]
struct AttributeListConnectedEntries<'n, 'f> {
attribute_list_entries: Option<NtfsAttributeListEntries<'n, 'f>>,
instance: u16,
ty: NtfsAttributeType,
}
impl<'n, 'f> AttributeListConnectedEntries<'n, 'f> {
fn new(
attribute_list_entries: NtfsAttributeListEntries<'n, 'f>,
instance: u16,
ty: NtfsAttributeType,
) -> Self {
Self {
attribute_list_entries: Some(attribute_list_entries),
instance,
ty,
}
}
fn next<T>(&mut self, fs: &mut T) -> Option<Result<NtfsAttributeListEntry>>
where
T: Read + Seek,
{
let attribute_list_entries = self.attribute_list_entries.as_mut()?;
let entry = iter_try!(attribute_list_entries.next(fs)?);
if entry.instance() == self.instance && iter_try!(entry.ty()) == self.ty {
Some(Ok(entry))
} else {
self.attribute_list_entries = None;
None
}
}
}
#[derive(Clone, Debug)]
struct AttributeState<'n> {
file: NtfsFile<'n>,
attribute_offset: usize,
data_runs_state: Option<DataRunsState>,
}