diff options
author | Omer Ben-Amram <omerbenamram@gmail.com> | 2019-05-08 17:49:22 +0300 |
---|---|---|
committer | Omer Ben-Amram <omerbenamram@gmail.com> | 2019-05-08 17:49:22 +0300 |
commit | f7abc6eb9c8ad9037795a3598474893a7e93ba45 (patch) | |
tree | 85676cb7c091d96c5d0d04c7ef267a3481c7a694 /src | |
parent | adec67bb213032df46b00b9bddabfb0289505632 (diff) |
everything now takes stream by reference
Diffstat (limited to 'src')
-rw-r--r-- | src/attr_x10.rs | 24 | ||||
-rw-r--r-- | src/attr_x30.rs | 14 | ||||
-rw-r--r-- | src/attribute.rs | 163 | ||||
-rw-r--r-- | src/bin/mft_dump.rs | 12 | ||||
-rw-r--r-- | src/entry.rs | 80 | ||||
-rw-r--r-- | src/err.rs | 33 | ||||
-rw-r--r-- | src/errors.rs | 64 | ||||
-rw-r--r-- | src/lib.rs | 12 | ||||
-rw-r--r-- | src/mft.rs | 26 | ||||
-rw-r--r-- | src/utils.rs | 37 |
10 files changed, 240 insertions, 225 deletions
diff --git a/src/attr_x10.rs b/src/attr_x10.rs index 62f04f1..6348e16 100644 --- a/src/attr_x10.rs +++ b/src/attr_x10.rs @@ -1,16 +1,17 @@ -use crate::errors::MftError; +use crate::err::{self, Result}; -use serde::Serialize; use byteorder::{LittleEndian, ReadBytesExt}; +use chrono::{DateTime, Utc}; +use serde::Serialize; use std::io::Read; use winstructs::timestamp::WinTimestamp; #[derive(Serialize, Debug, Clone)] pub struct StandardInfoAttr { - pub created: WinTimestamp, - pub modified: WinTimestamp, - pub mft_modified: WinTimestamp, - pub accessed: WinTimestamp, + pub created: DateTime<Utc>, + pub modified: DateTime<Utc>, + pub mft_modified: DateTime<Utc>, + pub accessed: DateTime<Utc>, pub file_flags: u32, pub max_version: u32, pub version: u32, @@ -20,6 +21,7 @@ pub struct StandardInfoAttr { pub quota: u64, pub usn: u64, } + impl StandardInfoAttr { /// Parse a Standard Information attrbiute buffer. /// @@ -53,11 +55,11 @@ impl StandardInfoAttr { /// assert_eq!(attribute.usn, 8768215144); /// # } /// ``` - pub fn from_reader<R: Read>(reader: &mut R) -> Result<StandardInfoAttr, MftError> { - let created = WinTimestamp::from_reader(reader)?; - let modified = WinTimestamp::from_reader(reader)?; - let mft_modified = WinTimestamp::from_reader(reader)?; - let accessed = WinTimestamp::from_reader(reader)?; + pub fn from_reader<R: Read>(reader: &mut R) -> Result<StandardInfoAttr> { + let created = WinTimestamp::from_reader(reader)?.to_datetime(); + let modified = WinTimestamp::from_reader(reader)?.to_datetime(); + let mft_modified = WinTimestamp::from_reader(reader)?.to_datetime(); + let accessed = WinTimestamp::from_reader(reader)?.to_datetime(); let file_flags = reader.read_u32::<LittleEndian>()?; let max_version = reader.read_u32::<LittleEndian>()?; let version = reader.read_u32::<LittleEndian>()?; diff --git a/src/attr_x30.rs b/src/attr_x30.rs index 54c9d24..0ffb8f5 100644 --- a/src/attr_x30.rs +++ b/src/attr_x30.rs @@ -1,4 +1,4 @@ -use crate::errors::MftError; +use crate::err::{self, Result}; use byteorder::{LittleEndian, ReadBytesExt}; use encoding::all::UTF_16LE; @@ -8,6 +8,7 @@ use std::io::Read; use chrono::{DateTime, Utc}; use serde::Serialize; +use snafu::ResultExt; use winstructs::reference::MftReference; use winstructs::timestamp::WinTimestamp; @@ -67,7 +68,7 @@ impl FileNameAttr { /// assert_eq!(attribute.name, "$LogFile"); /// # } /// ``` - pub fn from_reader<R: Read>(mut reader: &mut R) -> Result<FileNameAttr, MftError> { + pub fn from_reader<R: Read>(reader: &mut R) -> Result<FileNameAttr> { let parent = MftReference(reader.read_u64::<LittleEndian>()?); let created = WinTimestamp::from_reader(reader)?.to_datetime(); let modified = WinTimestamp::from_reader(reader)?.to_datetime(); @@ -84,13 +85,8 @@ impl FileNameAttr { reader.read_exact(&mut name_buffer)?; let name = match UTF_16LE.decode(&name_buffer, DecoderTrap::Ignore) { - Ok(filename) => filename, - Err(error) => { - return Err(MftError::decode_error(format!( - "Error decoding name in filename attribute. [{}]", - error - ))) - } + Ok(s) => s, + Err(e) => return err::InvalidFilename {}.fail(), }; let fullname = None; diff --git a/src/attribute.rs b/src/attribute.rs index 666e4f1..5598107 100644 --- a/src/attribute.rs +++ b/src/attribute.rs @@ -1,21 +1,21 @@ +use crate::err::{self, Result}; + use crate::attr_x10::StandardInfoAttr; use crate::attr_x30::FileNameAttr; -use crate::errors::MftError; -use crate::utils; +use crate::utils::read_utf16_string; +use crate::{utils, ReadSeek}; use bitflags::bitflags; use byteorder::{LittleEndian, ReadBytesExt}; -use encoding::all::UTF_16LE; -use encoding::{DecoderTrap, Encoding}; use serde::{ser, Serialize}; -use std::{io::Read, io::Seek, io::SeekFrom, mem}; +use std::{io::Read, io::Seek}; #[derive(Clone, Debug)] pub struct RawAttribute(pub Vec<u8>); impl ser::Serialize for RawAttribute { - fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error> where S: ser::Serializer, { @@ -33,6 +33,7 @@ pub enum AttributeContent { } bitflags! { + #[derive(Default)] pub struct AttributeDataFlags: u16 { const IS_COMPRESSED = 0x0001; const COMPRESSION_MASK = 0x00FF; @@ -44,14 +45,14 @@ bitflags! { pub fn serialize_attr_data_flags<S>( item: &AttributeDataFlags, serializer: S, -) -> Result<S::Ok, S::Error> +) -> ::std::result::Result<S::Ok, S::Error> where S: ser::Serializer, { serializer.serialize_str(&format!("{:?}", item)) } -#[derive(Serialize, Clone, Debug)] +#[derive(Serialize, Clone, Debug, Default)] pub struct AttributeHeader { pub attribute_type: u32, #[serde(skip_serializing)] @@ -68,59 +69,53 @@ pub struct AttributeHeader { // 16 pub residential_header: ResidentialHeader, } -impl AttributeHeader { - pub fn new<R: Read + Seek>(mut reader: R) -> Result<AttributeHeader, MftError> { - let mut attribute_header: AttributeHeader = unsafe { mem::zeroed() }; - let current_offset = reader.seek(SeekFrom::Current(0))?; - // println!("at offset {}",current_offset); +impl AttributeHeader { + pub fn from_stream<S: ReadSeek>(stream: &mut S) -> Result<AttributeHeader> { + let current_offset = stream.tell()?; - attribute_header.attribute_type = reader.read_u32::<LittleEndian>()?; - if attribute_header.attribute_type == 0xFFFF_FFFF { - return Ok(attribute_header); - } - attribute_header.attribute_size = reader.read_u32::<LittleEndian>()?; - attribute_header.resident_flag = reader.read_u8()?; - attribute_header.name_size = reader.read_u8()?; - attribute_header.name_offset = reader.read_u16::<LittleEndian>()?; - attribute_header.data_flags = - AttributeDataFlags::from_bits_truncate(reader.read_u16::<LittleEndian>()?); - attribute_header.id = reader.read_u16::<LittleEndian>()?; - - if attribute_header.resident_flag == 0 { - attribute_header.residential_header = - ResidentialHeader::Resident(ResidentHeader::new(&mut reader)?); - } else if attribute_header.resident_flag == 1 { - attribute_header.residential_header = - ResidentialHeader::NonResident(NonResidentHeader::new(&mut reader)?); - } else { - panic!( - "Unhandled resident flag: {} (offet: {})", - attribute_header.resident_flag, current_offset - ); + let attribute_type = stream.read_u32::<LittleEndian>()?; + // TODO: explain why this should return default + if attribute_type == 0xFFFF_FFFF { + return Ok(AttributeHeader::default()); } - if attribute_header.name_size > 0 { - // Seek to offset - reader.seek(SeekFrom::Start( - current_offset + u64::from(attribute_header.name_offset), - ))?; - - let mut name_buffer = vec![0; (attribute_header.name_size * 2) as usize]; - reader.read_exact(&mut name_buffer)?; - - attribute_header.name = match UTF_16LE.decode(&name_buffer, DecoderTrap::Ignore) { - Ok(filename) => filename, - Err(error) => { - return Err(MftError::decode_error(format!( - "Error decoding filename in header. [{}]", - error - ))) + let attribute_size = stream.read_u32::<LittleEndian>()?; + let resident_flag = stream.read_u8()?; + let name_size = stream.read_u8()?; + let name_offset = stream.read_u16::<LittleEndian>()?; + let data_flags = AttributeDataFlags::from_bits_truncate(stream.read_u16::<LittleEndian>()?); + let id = stream.read_u16::<LittleEndian>()?; + + let residential_header = match resident_flag { + 0 => ResidentialHeader::Resident(ResidentHeader::new(stream)?), + 1 => ResidentialHeader::NonResident(NonResidentHeader::new(stream)?), + _ => { + return err::UnhandledResidentFlag { + flag: resident_flag, + offset: current_offset, } - }; - } + .fail(); + } + }; - Ok(attribute_header) + let name = if name_size > 0 { + read_utf16_string(stream, Some(name_size as usize))? + } else { + String::new() + }; + + Ok(AttributeHeader { + attribute_type, + attribute_size, + resident_flag, + name_size, + name_offset, + data_flags, + id, + name, + residential_header, + }) } } @@ -132,6 +127,12 @@ pub enum ResidentialHeader { NonResident(NonResidentHeader), } +impl Default for ResidentialHeader { + fn default() -> Self { + ResidentialHeader::None + } +} + #[derive(Serialize, Clone, Debug)] pub struct ResidentHeader { #[serde(skip_serializing)] @@ -142,16 +143,15 @@ pub struct ResidentHeader { #[serde(skip_serializing)] pub padding: u8, } -impl ResidentHeader { - pub fn new<R: Read>(mut reader: R) -> Result<ResidentHeader, MftError> { - let mut residential_header: ResidentHeader = unsafe { mem::zeroed() }; - residential_header.data_size = reader.read_u32::<LittleEndian>()?; - residential_header.data_offset = reader.read_u16::<LittleEndian>()?; - residential_header.index_flag = reader.read_u8()?; - residential_header.padding = reader.read_u8()?; - - Ok(residential_header) +impl ResidentHeader { + pub fn new<R: Read>(reader: &mut R) -> Result<ResidentHeader> { + Ok(ResidentHeader { + data_size: reader.read_u32::<LittleEndian>()?, + data_offset: reader.read_u16::<LittleEndian>()?, + index_flag: reader.read_u8()?, + padding: reader.read_u8()?, + }) } } @@ -170,23 +170,30 @@ pub struct NonResidentHeader { // pub size_total_allocated: Option<u64> } impl NonResidentHeader { - pub fn new<R: Read>(mut reader: R) -> Result<NonResidentHeader, MftError> { - let mut residential_header: NonResidentHeader = unsafe { mem::zeroed() }; - - residential_header.vnc_first = reader.read_u64::<LittleEndian>()?; - residential_header.vnc_last = reader.read_u64::<LittleEndian>()?; - residential_header.datarun_offset = reader.read_u16::<LittleEndian>()?; - residential_header.unit_compression_size = reader.read_u16::<LittleEndian>()?; - residential_header.padding = reader.read_u32::<LittleEndian>()?; - residential_header.size_allocated = reader.read_u64::<LittleEndian>()?; - residential_header.size_real = reader.read_u64::<LittleEndian>()?; - residential_header.size_compressed = reader.read_u64::<LittleEndian>()?; + pub fn new<R: Read>(reader: &mut R) -> Result<NonResidentHeader> { + let vnc_first = reader.read_u64::<LittleEndian>()?; + let vnc_last = reader.read_u64::<LittleEndian>()?; + let datarun_offset = reader.read_u16::<LittleEndian>()?; + let unit_compression_size = reader.read_u16::<LittleEndian>()?; + let padding = reader.read_u32::<LittleEndian>()?; + let size_allocated = reader.read_u64::<LittleEndian>()?; + let size_real = reader.read_u64::<LittleEndian>()?; + let size_compressed = reader.read_u64::<LittleEndian>()?; // if residential_header.unit_compression_size > 0 { // residential_header.size_total_allocated = Some(reader.read_u64::<LittleEndian>()?); // } - Ok(residential_header) + Ok(NonResidentHeader { + vnc_first, + vnc_last, + datarun_offset, + unit_compression_size, + padding, + size_allocated, + size_real, + size_compressed, + }) } } @@ -209,7 +216,7 @@ mod tests { ]; let attribute_buffer = Cursor::new(raw); - let attribute_header = match AttributeHeader::new(attribute_buffer) { + let attribute_header = match AttributeHeader::from_stream(attribute_buffer) { Ok(attribute_header) => attribute_header, Err(error) => panic!(error), }; @@ -234,7 +241,7 @@ mod tests { let attribute_buffer = Cursor::new(raw); - let attribute_header = match AttributeHeader::new(attribute_buffer) { + let attribute_header = match AttributeHeader::from_stream(attribute_buffer) { Ok(attribute_header) => attribute_header, Err(error) => panic!(error), }; diff --git a/src/bin/mft_dump.rs b/src/bin/mft_dump.rs index b3fafff..1bdf443 100644 --- a/src/bin/mft_dump.rs +++ b/src/bin/mft_dump.rs @@ -1,11 +1,11 @@ -use clap::{App, Arg, ArgMatches}; +use clap::{App, Arg}; use log::{error, warn}; -use rustymft::mft::MftHandler; +use mft::mft::MftHandler; use std::fs; -fn process_file(filename: &str, options: ArgMatches) -> bool { - let mut mft_handler = match MftHandler::new(filename) { +fn process_file(filename: &str) -> bool { + let mut mft_handler = match MftHandler::from_path(filename) { Ok(mft_handler) => mft_handler, Err(error) => { warn!("Could not parse file: {} [error: {}]", filename, error); @@ -29,10 +29,6 @@ fn process_file(filename: &str, options: ArgMatches) -> bool { true } -fn is_directory(source: &str) -> bool { - fs::metadata(source).unwrap().file_type().is_dir() -} - fn main() { let matches = App::new("MFT Parser") .version(env!("CARGO_PKG_VERSION")) diff --git a/src/entry.rs b/src/entry.rs index 53872ef..9745de5 100644 --- a/src/entry.rs +++ b/src/entry.rs @@ -1,8 +1,12 @@ +use crate::err::{self, Result}; + use crate::attr_x10::StandardInfoAttr; use crate::attr_x30::FileNameAttr; use crate::attribute; use crate::enumerator::PathMapping; use crate::mft::MftHandler; +use snafu::ensure; + use std::collections::BTreeMap; use winstructs::reference::MftReference; @@ -11,7 +15,7 @@ use byteorder::{LittleEndian, ReadBytesExt}; use bitflags::bitflags; use serde::{ser, Serialize}; -use std::error::Error; + use std::io::Cursor; use std::io::Read; use std::io::Seek; @@ -28,7 +32,10 @@ bitflags! { const UNKNOWN_2 = 0x08; } } -pub fn serialize_entry_flags<S>(item: &EntryFlags, serializer: S) -> Result<S::Ok, S::Error> +pub fn serialize_entry_flags<S>( + item: &EntryFlags, + serializer: S, +) -> ::std::result::Result<S::Ok, S::Error> where S: ser::Serializer, { @@ -62,12 +69,15 @@ pub struct EntryHeader { pub update_sequence_value: u32, pub entry_reference: MftReference, } + impl EntryHeader { - pub fn from_reader<R: Read>(reader: &mut R, entry: u64) -> Result<EntryHeader, Box<dyn Error>> { + pub fn from_reader<R: Read>(reader: &mut R, entry: u64) -> Result<EntryHeader> { let signature = reader.read_u32::<LittleEndian>()?; - if signature != 1_162_627_398 { - return Err(format!("Bad signature: {:04X}", signature)); - } + + ensure!( + signature != 1_162_627_398, + err::InvalidEntrySignature { bad_sig: signature } + ); let usa_offset = reader.read_u16::<LittleEndian>()?; let usa_size = reader.read_u16::<LittleEndian>()?; @@ -81,16 +91,13 @@ impl EntryHeader { let base_reference = MftReference(reader.read_u64::<LittleEndian>()?); let next_attribute_id = reader.read_u16::<LittleEndian>()?; - // TODO: what is this offset? - let record_number = if usa_offset == 48 { - let _padding = Some(reader.read_u16::<LittleEndian>()?); - reader.read_u32::<LittleEndian>()? as u64 - } else { - return Err(format!( - "Unhandled update sequence array offset: {}", - usa_offset - )); - }; + ensure!( + usa_offset == 48, + err::InvalidUsaOffset { offset: usa_offset } + ); + + let _padding = reader.read_u16::<LittleEndian>()?; + let record_number = reader.read_u32::<LittleEndian>()? as u64; let entry_reference = MftReference::get_from_entry_and_seq(record_number as u64, sequence); @@ -120,7 +127,7 @@ pub struct MftEntry { pub attributes: BTreeMap<String, Vec<attribute::MftAttribute>>, } impl MftEntry { - pub fn new(mut buffer: Vec<u8>, entry: u64) -> Result<MftEntry, Box<dyn Error>> { + pub fn new(buffer: Vec<u8>, entry: u64) -> Result<MftEntry> { let mut cursor = Cursor::new(&buffer); // Get Header let entry_header = EntryHeader::from_reader(&mut cursor, entry)?; @@ -173,14 +180,14 @@ impl MftEntry { // } // } - fn read_attributes<S: Read + Seek>(&mut self, buffer: &mut S) -> Result<u32, Box<dyn Error>> { + fn read_attributes<S: Read + Seek>(&mut self, buffer: &mut S) -> Result<u32> { let mut current_offset = buffer.seek(SeekFrom::Start(self.header.fst_attr_offset as u64))?; let attr_count: u32 = 0; loop { - let attribute_header = attribute::AttributeHeader::new(&mut buffer)?; + let attribute_header = attribute::AttributeHeader::from_stream(buffer)?; if attribute_header.attribute_type == 0xFFFF_FFFF { break; @@ -188,33 +195,24 @@ impl MftEntry { match attribute_header.residential_header { attribute::ResidentialHeader::Resident(ref header) => { - // Create buffer for raw attribute content - let mut content_buffer = vec![0; header.data_size as usize]; - - // read into content buffer - buffer.read_exact(&mut content_buffer).unwrap(); - // Create attribute content to parse buffer into - let mut attr_content: attribute::AttributeContent = unsafe { mem::zeroed() }; - // Get attribute contents - match attribute_header.attribute_type { - 0x10 => { - let attr = StandardInfoAttr::from_reader(content_buffer.as_slice())?; - - attr_content = attribute::AttributeContent::AttrX10(attr); - } + let attr_content = match attribute_header.attribute_type { + 0x10 => attribute::AttributeContent::AttrX10( + StandardInfoAttr::from_reader(buffer)?, + ), 0x30 => { - let attr = FileNameAttr::from_reader(content_buffer.as_slice())?; - - attr_content = attribute::AttributeContent::AttrX30(attr); + attribute::AttributeContent::AttrX30(FileNameAttr::from_reader(buffer)?) } _ => { - attr_content = attribute::AttributeContent::Raw( - attribute::RawAttribute(content_buffer), - ); + let mut content_buffer = vec![0; header.data_size as usize]; + buffer.read_exact(&mut content_buffer)?; + + attribute::AttributeContent::Raw(attribute::RawAttribute( + content_buffer, + )) } - } + }; self.set_attribute(attribute::MftAttribute { header: attribute_header.clone(), @@ -251,7 +249,7 @@ impl MftEntry { .push(attribute); } - pub fn set_fullnames(&mut self, mft_handler: &mut MftHandler) { + pub fn set_full_names(&mut self, mft_handler: &mut MftHandler) { if self.attributes.contains_key("0x0030") { if let Some(attr_list) = self.attributes.get_mut("0x0030") { for attribute in attr_list.iter_mut() { diff --git a/src/err.rs b/src/err.rs new file mode 100644 index 0000000..99c80a2 --- /dev/null +++ b/src/err.rs @@ -0,0 +1,33 @@ +use snafu::Snafu; +use std::path::PathBuf; +use std::{io, result}; + +pub type Result<T> = result::Result<T, Error>; + +#[derive(Debug, Snafu)] +#[snafu(visibility(pub(crate)))] +pub enum Error { + #[snafu(display("An I/O error has occurred: {}", "source"))] + IoError { source: std::io::Error }, + #[snafu(display("Failed to open file {}: {}", path.display(), source))] + FailedToOpenFile { + path: PathBuf, + source: std::io::Error, + }, + #[snafu(display("Error while decoding name in filename attribute"))] + InvalidFilename, + #[snafu(display("Bad signature: {:04X}", bad_sig))] + InvalidEntrySignature { bad_sig: u32 }, + #[snafu(display("Unhandled resident flag: {} (offset: {})", flag, offset))] + UnhandledResidentFlag { flag: u8, offset: u64 }, + #[snafu(display("Expected usa_offset `{}` to equal 48", offset))] + InvalidUsaOffset { offset: u16 }, + #[snafu(display("An unexpected error has occurred: {}", detail))] + Any { detail: String }, +} + +impl From<io::Error> for Error { + fn from(err: io::Error) -> Self { + Error::IoError { source: err } + } +} diff --git a/src/errors.rs b/src/errors.rs deleted file mode 100644 index 93ef88b..0000000 --- a/src/errors.rs +++ /dev/null @@ -1,64 +0,0 @@ -use std::fmt; -use std::fmt::Display; -use std::io; - -#[derive(Debug)] -pub enum ErrorKind { - IoError, - InvalidFileSignature, - InvalidEntrySignature, - Utf16Error, -} - -#[derive(Debug)] -pub struct MftError { - /// Formated error message - pub message: String, - /// The type of error - pub kind: ErrorKind, - /// Any additional information passed along, such as the argument name that caused the error - pub info: Option<Vec<String>>, -} - -impl MftError { - #[allow(dead_code)] - pub fn invalid_file_signature(err: String) -> Self { - MftError { - message: err.to_string(), - kind: ErrorKind::InvalidFileSignature, - info: Some(vec![]), - } - } - #[allow(dead_code)] - pub fn invalid_entry_signature(err: String) -> Self { - MftError { - message: err.to_string(), - kind: ErrorKind::InvalidFileSignature, - info: Some(vec![]), - } - } - #[allow(dead_code)] - pub fn decode_error(err: String) -> Self { - MftError { - message: err.to_string(), - kind: ErrorKind::Utf16Error, - info: Some(vec![]), - } - } -} - -impl From<io::Error> for MftError { - fn from(err: io::Error) -> Self { - MftError { - message: format!("{}", err), - kind: ErrorKind::IoError, - info: Some(vec![]), - } - } -} - -impl Display for MftError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - writeln!(f, "{}", self.message) - } -} @@ -1,9 +1,19 @@ +use std::io::{self, Read, Seek, SeekFrom}; + +pub mod err; pub mod attr_x10; pub mod attr_x30; pub mod attribute; pub mod entry; pub mod enumerator; -pub mod errors; pub mod mft; pub(crate) mod utils; + +pub trait ReadSeek: Read + Seek { + fn tell(&mut self) -> io::Result<u64> { + self.seek(SeekFrom::Current(0)) + } +} + +impl<T: Read + Seek> ReadSeek for T {} @@ -1,8 +1,10 @@ use crate::entry::MftEntry; use crate::enumerator::{PathEnumerator, PathMapping}; -use crate::errors::MftError; +use crate::err::{self, Result}; +use snafu::ResultExt; use std::fs::File; use std::io::{BufReader, Read, Seek, SeekFrom}; +use std::path::Path; use winstructs::reference::MftReference; pub struct MftHandler { @@ -14,14 +16,13 @@ pub struct MftHandler { } impl MftHandler { - pub fn new(filename: &str) -> Result<MftHandler, MftError> { - let mut mft_fh = match File::open(filename) { - Ok(usn_fh) => usn_fh, - // Handle error here - Err(error) => panic!("Error: {}", error), - }; + pub fn from_path(filename: impl AsRef<Path>) -> Result<MftHandler> { + let f = filename.as_ref(); + + let mut mft_fh = File::open(f).context(err::FailedToOpenFile { path: f.to_owned() })?; // get file size + // TODO: remove this, and find a better way let size = match mft_fh.seek(SeekFrom::End(0)) { Err(e) => panic!("Error: {}", e), Ok(size) => size, @@ -46,10 +47,9 @@ impl MftHandler { self._size / u64::from(self._entry_size) } - pub fn entry(&mut self, entry: u64) -> Result<MftEntry, MftError> { + pub fn entry(&mut self, entry: u64) -> Result<MftEntry> { self.file - .seek(SeekFrom::Start(entry * self._entry_size as u64)) - .unwrap(); + .seek(SeekFrom::Start(entry * self._entry_size as u64))?; let mut entry_buffer = vec![0; self._entry_size as usize]; self.file.read_exact(&mut entry_buffer)?; @@ -64,7 +64,7 @@ impl MftHandler { } } - mft_entry.set_fullnames(self); + mft_entry.set_full_names(self); Ok(mft_entry) } @@ -73,7 +73,7 @@ impl MftHandler { self.path_enumerator.print_mapping(); } - pub fn entry_from_buffer(&mut self, buffer: Vec<u8>, entry: u64) -> Result<MftEntry, MftError> { + pub fn entry_from_buffer(&mut self, buffer: Vec<u8>, entry: u64) -> Result<MftEntry> { let mft_entry = MftEntry::new(buffer, entry)?; Ok(mft_entry) @@ -119,7 +119,7 @@ impl MftHandler { } } - fn get_mapping_from_entry(&mut self, entry: u64) -> Result<Option<PathMapping>, MftError> { + fn get_mapping_from_entry(&mut self, entry: u64) -> Result<Option<PathMapping>> { self.file .seek(SeekFrom::Start(entry * self._entry_size as u64))?; diff --git a/src/utils.rs b/src/utils.rs index bea0651..fb9b812 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,4 +1,41 @@ +use crate::ReadSeek; +use byteorder::ReadBytesExt; +use std::char::decode_utf16; +use std::io; + pub fn to_hex_string(bytes: &[u8]) -> String { let strs: Vec<String> = bytes.iter().map(|b| format!("{:02X}", b)).collect(); strs.join("") } + +/// Reads a utf16 string from the given stream. +/// If `len` is given, exactly `len` u16 values are read from the stream. +/// If `len` is None, the string is assumed to be null terminated and the stream will be read to the first null (0). +pub fn read_utf16_string<T: ReadSeek>(stream: &mut T, len: Option<usize>) -> io::Result<String> { + let mut buffer = match len { + Some(len) => Vec::with_capacity(len), + None => Vec::new(), + }; + + match len { + Some(len) => { + for _ in 0..len { + let next_char = stream.read_u16::<byteorder::LittleEndian>()?; + buffer.push(next_char); + } + } + None => loop { + let next_char = stream.read_u16::<byteorder::LittleEndian>()?; + + if next_char == 0 { + break; + } + + buffer.push(next_char); + }, + } + + decode_utf16(buffer.into_iter()) + .map(|r| r.map_err(|_e| io::Error::from(io::ErrorKind::InvalidData))) + .collect() +} |