use chrono::prelude::*;
use crate::attribute::{Attribute, AttributeRef, AttributeRefMut};
use crate::error::Error;
use crate::ffi_error::{LibfsntfsError, LibfsntfsErrorRef, LibfsntfsErrorRefMut};
use crate::volume::{Volume, VolumeRef};
use libfsntfs_sys::{
libfsntfs_attribute_t, libfsntfs_data_stream_t, off64_t, size64_t, SEEK_CUR, SEEK_END, SEEK_SET,
};
use libyal_rs_common::ffi::AsTypeRef;
use std::convert::TryFrom;
use std::ffi::c_void;
use std::fmt::{Debug, Formatter};
use std::fs::read;
use std::io::{BufRead, Read, Seek, SeekFrom};
use std::marker::PhantomData;
use std::option::Iter;
use std::os::raw::c_int;
use std::{fmt, io, mem, ptr};
#[repr(C)]
pub struct __FileEntry(isize);
pub type FileEntryRefMut = *mut __FileEntry;
pub type FileEntryRef = *const __FileEntry;
#[repr(C)]
pub struct FileEntry<'a>(FileEntryRefMut, &'a Volume);
impl<'a> AsTypeRef for FileEntry<'a> {
type Ref = FileEntryRef;
type RefMut = FileEntryRefMut;
#[inline]
fn as_type_ref(&self) -> Self::Ref {
self.0 as *const _
}
#[inline]
fn as_type_ref_mut(&mut self) -> Self::RefMut {
self.0
}
#[inline]
fn as_raw(&mut self) -> *mut Self::RefMut {
&mut self.0 as *mut _
}
}
impl<'a> FileEntry<'a> {
pub fn wrap_ptr(volume: &'a Volume, ptr: FileEntryRefMut) -> Self {
FileEntry(ptr, volume)
}
}
impl<'a> Drop for FileEntry<'a> {
fn drop(&mut self) {
use libyal_rs_common::ffi::AsTypeRef;
use log::trace;
let mut error = ptr::null_mut();
trace!("Calling `libfsntfs_file_entry_free`");
unsafe {
libfsntfs_file_entry_free(&mut self.as_type_ref_mut() as *mut _, &mut error);
}
debug_assert!(error.is_null(), "`libfsntfs_file_entry_free` failed!");
}
}
impl<'a> Debug for FileEntry<'a> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
f.debug_struct("FileEntry")
.field("Name", &self.get_name().unwrap_or("".to_string()))
.finish()
}
}
extern "C" {
pub fn libfsntfs_file_entry_free(
file_entry: *mut FileEntryRefMut,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_is_empty(
file_entry: FileEntryRef,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_is_allocated(
file_entry: FileEntryRef,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_file_reference(
file_entry: FileEntryRef,
file_reference: *mut u64,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_base_record_file_reference(
file_entry: FileEntryRef,
file_reference: *mut u64,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_parent_file_reference(
file_entry: FileEntryRef,
file_reference: *mut u64,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_parent_file_reference_by_attribute_index(
file_entry: FileEntryRef,
attribute_index: c_int,
file_reference: *mut u64,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_journal_sequence_number(
file_entry: FileEntryRef,
journal_sequence_number: *mut u64,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_creation_time(
file_entry: FileEntryRef,
filetime: *mut u64,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_modification_time(
file_entry: FileEntryRef,
filetime: *mut u64,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_access_time(
file_entry: FileEntryRef,
filetime: *mut u64,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_entry_modification_time(
file_entry: FileEntryRef,
filetime: *mut u64,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_file_attribute_flags(
file_entry: FileEntryRef,
file_attribute_flags: *mut u32,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_utf8_name_size(
file_entry: FileEntryRef,
utf8_name_size: *mut usize,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_utf8_name(
file_entry: FileEntryRef,
utf8_name: *mut u8,
utf8_name_size: usize,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_utf16_name_size(
file_entry: FileEntryRef,
utf16_name_size: *mut usize,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_utf16_name(
file_entry: FileEntryRef,
utf16_name: *mut u16,
utf16_name_size: usize,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_name_attribute_index(
file_entry: FileEntryRef,
attribute_index: *mut c_int,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_utf8_name_size_by_attribute_index(
file_entry: FileEntryRef,
attribute_index: c_int,
utf8_name_size: *mut usize,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_utf8_name_by_attribute_index(
file_entry: FileEntryRef,
attribute_index: c_int,
utf8_name: *mut u8,
utf8_name_size: usize,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_utf16_name_size_by_attribute_index(
file_entry: FileEntryRef,
attribute_index: c_int,
utf16_name_size: *mut usize,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_utf16_name_by_attribute_index(
file_entry: FileEntryRef,
attribute_index: c_int,
utf16_name: *mut u16,
utf16_name_size: usize,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_utf8_reparse_point_substitute_name_size(
file_entry: FileEntryRef,
utf8_name_size: *mut usize,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_utf8_reparse_point_substitute_name(
file_entry: FileEntryRef,
utf8_name: *mut u8,
utf8_name_size: usize,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_utf16_reparse_point_substitute_name_size(
file_entry: FileEntryRef,
utf16_name_size: *mut usize,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_utf16_reparse_point_substitute_name(
file_entry: FileEntryRef,
utf16_name: *mut u16,
utf16_name_size: usize,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_utf8_reparse_point_print_name_size(
file_entry: FileEntryRef,
utf8_name_size: *mut usize,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_utf8_reparse_point_print_name(
file_entry: FileEntryRef,
utf8_name: *mut u8,
utf8_name_size: usize,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_utf16_reparse_point_print_name_size(
file_entry: FileEntryRef,
utf16_name_size: *mut usize,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_utf16_reparse_point_print_name(
file_entry: FileEntryRef,
utf16_name: *mut u16,
utf16_name_size: usize,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_security_descriptor_size(
file_entry: FileEntryRef,
data_size: *mut usize,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_security_descriptor(
file_entry: FileEntryRef,
data: *mut u8,
data_size: usize,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_number_of_attributes(
file_entry: FileEntryRef,
number_of_attributes: *mut c_int,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_attribute_by_index(
file_entry: FileEntryRef,
attribute_index: c_int,
attribute: *mut AttributeRefMut,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_has_directory_entries_index(
file_entry: FileEntryRef,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_has_default_data_stream(
file_entry: FileEntryRef,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_number_of_alternate_data_streams(
file_entry: FileEntryRef,
number_of_alternate_data_streams: *mut c_int,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_alternate_data_stream_by_index(
file_entry: FileEntryRef,
alternate_data_stream_index: c_int,
alternate_data_stream: *mut *mut libfsntfs_data_stream_t,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_has_alternate_data_stream_by_utf8_name(
file_entry: FileEntryRef,
utf8_string: *const u8,
utf8_string_length: usize,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_has_alternate_data_stream_by_utf16_name(
file_entry: FileEntryRef,
utf16_string: *const u16,
utf16_string_length: usize,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_alternate_data_stream_by_utf8_name(
file_entry: FileEntryRef,
utf8_string: *const u8,
utf8_string_length: usize,
alternate_data_stream: *mut *mut libfsntfs_data_stream_t,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_alternate_data_stream_by_utf16_name(
file_entry: FileEntryRef,
utf16_string: *const u16,
utf16_string_length: usize,
alternate_data_stream: *mut *mut libfsntfs_data_stream_t,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_number_of_sub_file_entries(
file_entry: FileEntryRef,
number_of_sub_file_entries: *mut c_int,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_sub_file_entry_by_index(
file_entry: FileEntryRef,
sub_file_entry_index: c_int,
sub_file_entry: *mut FileEntryRefMut,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_sub_file_entry_by_utf8_name(
file_entry: FileEntryRef,
utf8_string: *const u8,
utf8_string_length: usize,
sub_file_entry: *mut FileEntryRefMut,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_sub_file_entry_by_utf16_name(
file_entry: FileEntryRef,
utf16_string: *const u16,
utf16_string_length: usize,
sub_file_entry: *mut FileEntryRefMut,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_read_buffer(
file_entry: FileEntryRef,
buffer: *mut ::std::os::raw::c_void,
buffer_size: usize,
error: *mut LibfsntfsErrorRefMut,
) -> isize;
pub fn libfsntfs_file_entry_read_buffer_at_offset(
file_entry: FileEntryRef,
buffer: *mut ::std::os::raw::c_void,
buffer_size: usize,
offset: off64_t,
error: *mut LibfsntfsErrorRefMut,
) -> isize;
pub fn libfsntfs_file_entry_seek_offset(
file_entry: FileEntryRef,
offset: off64_t,
whence: c_int,
error: *mut LibfsntfsErrorRefMut,
) -> off64_t;
pub fn libfsntfs_file_entry_get_offset(
file_entry: FileEntryRef,
offset: *mut off64_t,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_size(
file_entry: FileEntryRef,
size: *mut size64_t,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_number_of_extents(
file_entry: FileEntryRef,
number_of_extents: *mut c_int,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
pub fn libfsntfs_file_entry_get_extent_by_index(
file_entry: FileEntryRef,
extent_index: c_int,
extent_offset: *mut off64_t,
extent_size: *mut size64_t,
extent_flags: *mut u32,
error: *mut LibfsntfsErrorRefMut,
) -> c_int;
}
pub struct IterAttributes<'a> {
handle: &'a FileEntry<'a>,
num_attributes: u32,
idx: u32,
}
impl<'a> Iterator for IterAttributes<'a> {
type Item = Result<Attribute<'a>, Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.idx < self.num_attributes {
let attr = self.handle.get_attribute_by_index(self.idx as i32);
self.idx += 1;
return Some(attr);
}
None
}
}
pub struct IterSubEntries<'a: 'b, 'b> {
handle: &'b FileEntry<'a>,
num_sub_entries: u32,
idx: u32,
}
impl<'a: 'b, 'b> Iterator for IterSubEntries<'a, 'b> {
type Item = Result<FileEntry<'a>, Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.idx < self.num_sub_entries {
let sub_entry = self.handle.get_sub_file_entry(self.idx as i32);
self.idx += 1;
return Some(sub_entry);
}
None
}
}
impl<'a> Read for FileEntry<'a> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
let mut error = ptr::null_mut();
let read_count = unsafe {
libfsntfs_file_entry_read_buffer(
self.as_type_ref(),
buf.as_mut_ptr() as *mut c_void,
buf.len(),
&mut error,
)
};
if read_count <= -1 {
let ffi_err = Error::try_from(error);
let io_err = match ffi_err {
Ok(e) => io::Error::new(io::ErrorKind::Other, format!("{}", e)),
Err(e) => io::Error::new(
io::ErrorKind::Other,
format!("error while getting error information"),
),
};
Err(io_err)
} else {
Ok(read_count as usize)
}
}
}
impl<'a> Seek for FileEntry<'a> {
fn seek(&mut self, pos: SeekFrom) -> Result<u64, io::Error> {
let mut error = ptr::null_mut();
let seek_pos = match pos {
SeekFrom::Start(offset) => unsafe {
libfsntfs_file_entry_seek_offset(
self.as_type_ref(),
offset as i64,
SEEK_SET as i32,
&mut error,
)
},
SeekFrom::End(offset) => unsafe {
libfsntfs_file_entry_seek_offset(
self.as_type_ref(),
offset as i64,
SEEK_END as i32,
&mut error,
)
},
SeekFrom::Current(offset) => unsafe {
libfsntfs_file_entry_seek_offset(
self.as_type_ref(),
offset as i64,
SEEK_CUR as i32,
&mut error,
)
},
};
if seek_pos <= -1 {
let ffi_err = Error::try_from(error);
let io_err = match ffi_err {
Ok(e) => io::Error::new(io::ErrorKind::Other, format!("{}", e)),
Err(e) => io::Error::new(
io::ErrorKind::Other,
format!("error while getting error information"),
),
};
Err(io_err)
} else {
Ok(seek_pos as u64)
}
}
}
impl<'a> FileEntry<'a> {
pub fn get_access_time(&self) -> Option<DateTime<Utc>> {
unimplemented!();
}
pub fn get_size(&self) -> Result<u64, Error> {
let mut size = 0;
let mut error = ptr::null_mut();
if unsafe { libfsntfs_file_entry_get_size(self.as_type_ref(), &mut size, &mut error) } != 1
{
Err(Error::try_from(error)?)
} else {
Ok(size)
}
}
pub fn get_alternate_data_stream(&self, alternate_data_stream_index: isize) {
unimplemented!();
}
pub fn get_alternate_data_stream_by_name(&self, name: isize) {
unimplemented!();
}
pub fn iter_attributes(&self) -> Result<IterAttributes, Error> {
let number_of_attributes = self.get_number_of_attributes()? as u32;
Ok(IterAttributes {
handle: self,
num_attributes: number_of_attributes,
idx: 0,
})
}
pub fn iter_sub_entries<'c>(&'c self) -> Result<IterSubEntries<'a, 'c>, Error> {
let number_sub_entries = self.get_number_of_sub_file_entries()? as u32;
Ok(IterSubEntries {
handle: self,
num_sub_entries: number_sub_entries,
idx: 0,
})
}
pub fn get_number_of_attributes(&self) -> Result<c_int, Error> {
let mut num_attributes = 0_i32;
let mut error = ptr::null_mut();
if unsafe {
libfsntfs_file_entry_get_number_of_attributes(
self.as_type_ref(),
&mut num_attributes,
&mut error,
)
} != 1
{
Err(Error::try_from(error)?)
} else {
Ok(num_attributes)
}
}
pub fn get_attribute_by_index(&self, attribute_index: i32) -> Result<Attribute, Error> {
let mut attribute = ptr::null_mut();
let mut error = ptr::null_mut();
if unsafe {
libfsntfs_file_entry_get_attribute_by_index(
self.as_type_ref(),
attribute_index,
&mut attribute,
&mut error,
)
} != 1
{
Err(Error::try_from(error)?)
} else {
Ok(Attribute::wrap_ptr(self, attribute))
}
}
pub fn get_name(&self) -> Result<String, Error> {
get_sized_utf8_string!(
self,
libfsntfs_file_entry_get_utf8_name_size,
libfsntfs_file_entry_get_utf8_name
)
}
pub fn get_sub_file_entry(&self, sub_file_entry_index: i32) -> Result<FileEntry<'a>, Error> {
let mut sub_entry = ptr::null_mut();
let mut error = ptr::null_mut();
if unsafe {
libfsntfs_file_entry_get_sub_file_entry_by_index(
self.as_type_ref(),
sub_file_entry_index,
&mut sub_entry,
&mut error,
)
} != 1
{
Err(Error::try_from(error)?)
} else {
Ok(FileEntry::wrap_ptr(self.1, sub_entry))
}
}
pub fn get_number_of_sub_file_entries(&self) -> Result<c_int, Error> {
let mut number_of_file_entries = 0;
let mut error = ptr::null_mut();
if unsafe {
libfsntfs_file_entry_get_number_of_sub_file_entries(
self.as_type_ref(),
&mut number_of_file_entries,
&mut error,
)
} != 1
{
Err(Error::try_from(error)?)
} else {
Ok(number_of_file_entries)
}
}
pub fn get_parent_file_reference(&self) -> Result<u64, Error> {
let mut parent_file_idx = 0;
let mut error = ptr::null_mut();
if unsafe {
libfsntfs_file_entry_get_parent_file_reference(
self.as_type_ref(),
&mut parent_file_idx,
&mut error,
)
} != 1
{
Err(Error::try_from(error)?)
} else {
Ok(parent_file_idx)
}
}
pub fn get_file_reference(&self) -> Result<u64, Error> {
let mut file_idx = 0;
let mut error = ptr::null_mut();
if unsafe {
libfsntfs_file_entry_get_file_reference(
self.as_type_ref(),
&mut file_idx,
&mut error,
)
} != 1
{
Err(Error::try_from(error)?)
} else {
Ok(file_idx)
}
}
pub fn get_base_record_file_reference(&self) {
unimplemented!();
}
pub fn get_creation_time(&self) {
unimplemented!();
}
pub fn get_creation_time_as_integer(&self) {
unimplemented!();
}
pub fn get_entry_modification_time(&self) {
unimplemented!();
}
pub fn get_entry_modification_time_as_integer(&self) {
unimplemented!();
}
pub fn get_extent(&self, extent_index: isize) {
unimplemented!();
}
pub fn get_file_attribute_flags(&self) {
unimplemented!();
}
pub fn get_journal_sequence_number(&self) {
unimplemented!();
}
pub fn get_modification_time(&self) {
unimplemented!();
}
pub fn get_modification_time_as_integer(&self) {
unimplemented!();
}
pub fn get_name_attribute_index(&self) {
unimplemented!();
}
pub fn get_name_by_attribute_index(&self, attribute_index: isize) {
unimplemented!();
}
pub fn get_number_of_alternate_data_streams(&self) {
unimplemented!();
}
pub fn get_number_of_extents(&self) {
unimplemented!();
}
pub fn get_parent_file_reference_by_attribute_index(&self, attribute_index: isize) {
unimplemented!();
}
pub fn get_reparse_point_print_name(&self) {
unimplemented!();
}
pub fn get_reparse_point_substitute_name(&self) {
unimplemented!();
}
pub fn get_security_descriptor_data(&self) {
unimplemented!();
}
pub fn has_alternate_data_stream_by_name(&self, name: isize) {
unimplemented!();
}
pub fn has_default_data_stream(&self) {
unimplemented!();
}
pub fn has_directory_entries_index(&self) {
unimplemented!();
}
pub fn is_empty(&self) {
unimplemented!();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::fixtures::*;
use log::{info, trace};
use std::path::PathBuf;
#[test]
fn test_iter_attributes() {
let volume = sample_volume().unwrap();
let file_attribute = volume.get_file_entry_by_mft_idx(0).unwrap();
for attribute in file_attribute
.iter_attributes()
.unwrap()
.map(|a| a.unwrap())
{
println!("{:?}", attribute.get_name().unwrap());
println!("{:?}", attribute.get_type().unwrap());
}
}
#[test]
fn test_read() {
let volume = sample_volume().unwrap();
let mut entry = file_entry(&volume).unwrap();
let mut buf = vec![0; 10];
entry.read(&mut buf).unwrap();
assert_eq!(buf, [70, 73, 76, 69, 48, 0, 3, 0, 181, 104]);
}
#[test]
fn test_seek() {
let volume = sample_volume().unwrap();
let mut entry = file_entry(&volume).unwrap();
let mut buf = vec![0; 10];
entry.seek(SeekFrom::Start(10)).unwrap();
entry.read(&mut buf).unwrap();
assert_eq!(buf, [16, 0, 0, 0, 0, 0, 1, 0, 1, 0]);
}
#[test]
fn test_read_to_end() {
let volume = sample_volume().unwrap();
let mut entry = file_entry(&volume).unwrap();
let mut buf = Vec::new();
entry.read_to_end(&mut buf).unwrap();
assert_eq!(buf.len(), 75776);
}
}