Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/windirstat/mft.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorOmer Ben-Amram <omerbenamram@gmail.com>2019-05-08 17:49:22 +0300
committerOmer Ben-Amram <omerbenamram@gmail.com>2019-05-08 17:49:22 +0300
commitf7abc6eb9c8ad9037795a3598474893a7e93ba45 (patch)
tree85676cb7c091d96c5d0d04c7ef267a3481c7a694 /src
parentadec67bb213032df46b00b9bddabfb0289505632 (diff)
everything now takes stream by reference
Diffstat (limited to 'src')
-rw-r--r--src/attr_x10.rs24
-rw-r--r--src/attr_x30.rs14
-rw-r--r--src/attribute.rs163
-rw-r--r--src/bin/mft_dump.rs12
-rw-r--r--src/entry.rs80
-rw-r--r--src/err.rs33
-rw-r--r--src/errors.rs64
-rw-r--r--src/lib.rs12
-rw-r--r--src/mft.rs26
-rw-r--r--src/utils.rs37
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)
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
index 2cb5801..4d3d4fe 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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 {}
diff --git a/src/mft.rs b/src/mft.rs
index e61f6e2..59611ef 100644
--- a/src/mft.rs
+++ b/src/mft.rs
@@ -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()
+}