diff options
author | Omer Ben-Amram <omerbenamram@gmail.com> | 2020-01-06 18:18:01 +0300 |
---|---|---|
committer | Omer Ben-Amram <omerbenamram@gmail.com> | 2020-01-06 18:18:01 +0300 |
commit | 84daee189019564510255d4fd861712d5e127ad3 (patch) | |
tree | 091d4c9dc486f1023d1d41afec1613a52b157823 | |
parent | 1450e1050d7f97e0e3c7529680553b6a8299beea (diff) |
snafu -> thiserror/anyhow
-rw-r--r-- | Cargo.toml | 49 | ||||
-rw-r--r-- | src/attribute/header.rs | 12 | ||||
-rw-r--r-- | src/attribute/raw.rs | 3 | ||||
-rw-r--r-- | src/attribute/x10.rs | 11 | ||||
-rw-r--r-- | src/attribute/x20.rs | 7 | ||||
-rw-r--r-- | src/attribute/x30.rs | 19 | ||||
-rw-r--r-- | src/attribute/x40.rs | 11 | ||||
-rw-r--r-- | src/attribute/x80.rs | 3 | ||||
-rw-r--r-- | src/bin/mft_dump.rs | 70 | ||||
-rw-r--r-- | src/entry.rs | 51 | ||||
-rw-r--r-- | src/err.rs | 62 | ||||
-rw-r--r-- | src/mft.rs | 5 |
12 files changed, 142 insertions, 161 deletions
@@ -11,45 +11,46 @@ authors = ["Omer Ben-Amram <omerbenamram@gmail.com>"] edition = "2018" [dependencies] -log = {version = "^0.4", features=["release_max_level_debug"]} +log = { version = "0.4.8", features = ["release_max_level_debug"] } clap = "2.33.0" encoding = "0.2.33" -byteorder = "1.3.1" -bitflags = "1.0.4" -serde = {version = "1.0.91", features = ["derive"]} -serde_json = "1.0.39" -csv = "1.0.7" -snafu = {version="0.5.0", features = ["backtraces", "rust_1_30"]} -num-traits = "0.2" -num-derive = "0.3" -winstructs = "0.2.0" +byteorder = "1.3.2" +bitflags = "1.2.1" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +csv = "1.1.1" +thiserror = "1.0" +anyhow = "1.0" +num-traits = "0.2.10" +num-derive = "0.3.0" +winstructs = "0.2.3" lru = "0.4.3" -itertools = "0.8.0" -rand = "0.7.0" +itertools = "0.8.2" +rand = "0.7.2" # `mft_dump` dependencies -simplelog = "0.7.1" -dialoguer = "0.4.0" -indoc = "0.3" +simplelog = "0.7.4" +dialoguer = "0.5.0" +indoc = "0.3.4" [dependencies.chrono] -version = "0.4.6" +version = "0.4.10" features = ["serde"] [dev-dependencies] -criterion = "0.3" -skeptic = "0.13" -assert_cmd = "0.12" -predicates = "1" -env_logger = "0.7.0" -tempfile = "3" +criterion = "0.3.0" +skeptic = "0.13.4" +assert_cmd = "0.12.0" +predicates = "1.0.2" +env_logger = "0.7.1" +tempfile = "3.1.0" # rexpect relies on unix process semantics, but it's only used for process interaction tests. [target.'cfg(not(target_os = "windows"))'.dev-dependencies] -rexpect = "0.3" +rexpect = "0.3.0" [build-dependencies] -skeptic = "0.13" +skeptic = "0.13.4" [[bin]] name = "mft_dump" diff --git a/src/attribute/header.rs b/src/attribute/header.rs index df19697..ac7cf44 100644 --- a/src/attribute/header.rs +++ b/src/attribute/header.rs @@ -1,5 +1,5 @@ use crate::attribute::{AttributeDataFlags, MftAttributeType}; -use crate::err::{self, Result}; +use crate::err::{Error, Result}; use crate::utils::read_utf16_string; use crate::ReadSeek; @@ -54,10 +54,9 @@ impl MftAttributeHeader { let type_code = match MftAttributeType::from_u32(type_code_value) { Some(attribute_type) => attribute_type, None => { - return err::UnknownAttributeType { + return Err(Error::UnknownAttributeType { attribute_type: type_code_value, - } - .fail() + }) } }; @@ -81,11 +80,10 @@ impl MftAttributeHeader { 0 => ResidentialHeader::Resident(ResidentHeader::from_stream(stream)?), 1 => ResidentialHeader::NonResident(NonResidentHeader::from_stream(stream)?), _ => { - return err::UnhandledResidentFlag { + return Err(Error::UnhandledResidentFlag { flag: resident_flag, offset: stream.tell()?, - } - .fail(); + }) } }; diff --git a/src/attribute/raw.rs b/src/attribute/raw.rs index 8285ae5..c00fb1d 100644 --- a/src/attribute/raw.rs +++ b/src/attribute/raw.rs @@ -2,7 +2,6 @@ use crate::attribute::MftAttributeType; use crate::err::{self, Result}; use crate::{utils, ReadSeek}; use serde::{ser, Serialize}; -use snafu::ResultExt; /// Placeholder attribute for currently unparsed attributes. #[derive(Serialize, Clone, Debug)] @@ -20,7 +19,7 @@ impl RawAttribute { ) -> Result<Self> { let mut data = vec![0_u8; data_size]; - stream.read_exact(&mut data).context(err::IoError)?; + stream.read_exact(&mut data)?; Ok(RawAttribute { attribute_type, diff --git a/src/attribute/x10.rs b/src/attribute/x10.rs index ce7a610..cb7bd2c 100644 --- a/src/attribute/x10.rs +++ b/src/attribute/x10.rs @@ -1,12 +1,11 @@ use crate::attribute::FileAttributeFlags; -use crate::err::{self, Result}; +use crate::err::{Error, Result}; use crate::ReadSeek; use byteorder::{LittleEndian, ReadBytesExt}; use chrono::{DateTime, Utc}; use log::trace; use serde::Serialize; -use snafu::ResultExt; use winstructs::timestamp::WinTimestamp; #[derive(Serialize, Debug, Clone)] @@ -62,16 +61,16 @@ impl StandardInfoAttr { pub fn from_reader<S: ReadSeek>(reader: &mut S) -> Result<StandardInfoAttr> { trace!("Offset {}: StandardInfoAttr", reader.tell()?); let created = WinTimestamp::from_reader(reader) - .context(err::FailedToReadWindowsTime)? + .map_err(Error::failed_to_read_windows_time)? .to_datetime(); let modified = WinTimestamp::from_reader(reader) - .context(err::FailedToReadWindowsTime)? + .map_err(Error::failed_to_read_windows_time)? .to_datetime(); let mft_modified = WinTimestamp::from_reader(reader) - .context(err::FailedToReadWindowsTime)? + .map_err(Error::failed_to_read_windows_time)? .to_datetime(); let accessed = WinTimestamp::from_reader(reader) - .context(err::FailedToReadWindowsTime)? + .map_err(Error::failed_to_read_windows_time)? .to_datetime(); Ok(StandardInfoAttr { diff --git a/src/attribute/x20.rs b/src/attribute/x20.rs index 13425ba..8437823 100644 --- a/src/attribute/x20.rs +++ b/src/attribute/x20.rs @@ -1,4 +1,4 @@ -use crate::err::{self, Result}; +use crate::err::{Error, Result}; use crate::ReadSeek; use log::trace; @@ -8,7 +8,6 @@ use encoding::{DecoderTrap, Encoding}; use serde::Serialize; -use snafu::ResultExt; use std::io::SeekFrom; use winstructs::ntfs::mft_reference::MftReference; @@ -34,7 +33,7 @@ impl AttributeListAttr { let name_offset = stream.read_u8()?; let first_vcn = stream.read_u64::<LittleEndian>()?; let base_reference = - MftReference::from_reader(stream).context(err::FailedToReadMftReference)?; + MftReference::from_reader(stream).map_err(Error::failed_to_read_mft_reference)?; let attribute_id = stream.read_u16::<LittleEndian>()?; let name = if name_length > 0 { @@ -45,7 +44,7 @@ impl AttributeListAttr { match UTF_16LE.decode(&name_buffer, DecoderTrap::Ignore) { Ok(s) => s, - Err(_e) => return err::InvalidFilename {}.fail(), + Err(_e) => return Err(Error::InvalidFilename {}), } } else { String::new() diff --git a/src/attribute/x30.rs b/src/attribute/x30.rs index 8a0d0f4..1228039 100644 --- a/src/attribute/x30.rs +++ b/src/attribute/x30.rs @@ -1,8 +1,7 @@ use crate::attribute::FileAttributeFlags; -use crate::err::{self, Result}; +use crate::err::{self, Error, Result}; use crate::ReadSeek; use log::trace; -use snafu::OptionExt; use byteorder::{LittleEndian, ReadBytesExt}; use encoding::all::UTF_16LE; @@ -12,7 +11,6 @@ use chrono::{DateTime, Utc}; use num_traits::FromPrimitive; use serde::Serialize; -use snafu::ResultExt; use winstructs::ntfs::mft_reference::MftReference; use winstructs::timestamp::WinTimestamp; @@ -77,18 +75,19 @@ impl FileNameAttr { /// ``` pub fn from_stream<S: ReadSeek>(stream: &mut S) -> Result<FileNameAttr> { trace!("Offset {}: FilenameAttr", stream.tell()?); - let parent = MftReference::from_reader(stream).context(err::FailedToReadMftReference)?; + let parent = + MftReference::from_reader(stream).map_err(Error::failed_to_read_mft_reference)?; let created = WinTimestamp::from_reader(stream) - .context(err::FailedToReadWindowsTime)? + .map_err(Error::failed_to_read_windows_time)? .to_datetime(); let modified = WinTimestamp::from_reader(stream) - .context(err::FailedToReadWindowsTime)? + .map_err(Error::failed_to_read_windows_time)? .to_datetime(); let mft_modified = WinTimestamp::from_reader(stream) - .context(err::FailedToReadWindowsTime)? + .map_err(Error::failed_to_read_windows_time)? .to_datetime(); let accessed = WinTimestamp::from_reader(stream) - .context(err::FailedToReadWindowsTime)? + .map_err(Error::failed_to_read_windows_time)? .to_datetime(); let logical_size = stream.read_u64::<LittleEndian>()?; @@ -98,14 +97,14 @@ impl FileNameAttr { let name_length = stream.read_u8()?; let namespace = stream.read_u8()?; let namespace = - FileNamespace::from_u8(namespace).context(err::UnknownNamespace { namespace })?; + FileNamespace::from_u8(namespace).ok_or(Error::UnknownNamespace { namespace })?; let mut name_buffer = vec![0; (name_length as usize * 2) as usize]; stream.read_exact(&mut name_buffer)?; let name = match UTF_16LE.decode(&name_buffer, DecoderTrap::Ignore) { Ok(s) => s, - Err(_e) => return err::InvalidFilename {}.fail(), + Err(_e) => return Err(Error::InvalidFilename {}), }; Ok(FileNameAttr { diff --git a/src/attribute/x40.rs b/src/attribute/x40.rs index beea829..2526c82 100644 --- a/src/attribute/x40.rs +++ b/src/attribute/x40.rs @@ -1,7 +1,6 @@ -use crate::err::{self, Result}; +use crate::err::{Error, Result}; use crate::ReadSeek; use serde::Serialize; -use snafu::ResultExt; use winstructs::guid::Guid; /// $Data Attribute @@ -20,11 +19,11 @@ pub struct ObjectIdAttr { impl ObjectIdAttr { /// Data size should be either 16 or 64 pub fn from_stream<S: ReadSeek>(stream: &mut S, data_size: usize) -> Result<ObjectIdAttr> { - let object_id = Guid::from_stream(stream).context(err::FailedToReadGuid)?; + let object_id = Guid::from_reader(stream).map_err(Error::failed_to_read_guid)?; let (birth_volume_id, birth_object_id, domain_id) = if data_size == 64 { - let g1 = Guid::from_stream(stream).context(err::FailedToReadGuid)?; - let g2 = Guid::from_stream(stream).context(err::FailedToReadGuid)?; - let g3 = Guid::from_stream(stream).context(err::FailedToReadGuid)?; + let g1 = Guid::from_reader(stream).map_err(Error::failed_to_read_guid)?; + let g2 = Guid::from_reader(stream).map_err(Error::failed_to_read_guid)?; + let g3 = Guid::from_reader(stream).map_err(Error::failed_to_read_guid)?; (Some(g1), Some(g2), Some(g3)) } else { (None, None, None) diff --git a/src/attribute/x80.rs b/src/attribute/x80.rs index 3f1efa6..b9d8ec0 100644 --- a/src/attribute/x80.rs +++ b/src/attribute/x80.rs @@ -1,7 +1,6 @@ use crate::err::{self, Result}; use crate::{utils, ReadSeek}; use serde::ser; -use snafu::ResultExt; /// $Data Attribute #[derive(Clone, Debug)] @@ -11,7 +10,7 @@ impl DataAttr { pub fn from_stream<S: ReadSeek>(stream: &mut S, data_size: usize) -> Result<DataAttr> { let mut data = vec![0_u8; data_size]; - stream.read_exact(&mut data).context(err::IoError)?; + stream.read_exact(&mut data)?; Ok(DataAttr(data)) } diff --git a/src/bin/mft_dump.rs b/src/bin/mft_dump.rs index 75f4a8e..793581a 100644 --- a/src/bin/mft_dump.rs +++ b/src/bin/mft_dump.rs @@ -9,8 +9,7 @@ use mft::{MftEntry, ReadSeek}; use dialoguer::Confirmation; use mft::csv::FlatMftEntryWithName; -use snafu::ErrorCompat; -use std::error::Error; +use anyhow::{anyhow, Context, Error, Result}; use std::fs::File; use std::io::Write; use std::path::{Path, PathBuf}; @@ -22,13 +21,6 @@ use std::ops::RangeInclusive; use std::str::FromStr; use std::{fs, io, path}; -/// Simple error macro for use inside of internal errors in `MftDump` -macro_rules! err { - ($($tt:tt)*) => { Err(Box::<dyn std::error::Error>::from(format!($($tt)*))) } -} - -type StdErr = Box<dyn std::error::Error>; - #[derive(Debug, PartialOrd, PartialEq)] enum OutputFormat { JSON, @@ -56,19 +48,19 @@ impl Ranges { } impl FromStr for Ranges { - type Err = StdErr; + type Err = Error; - fn from_str(s: &str) -> Result<Self, Self::Err> { + fn from_str(s: &str) -> Result<Self> { let mut ranges = vec![]; for x in s.split(',') { // range if x.contains('-') { let range: Vec<&str> = x.split('-').collect(); if range.len() != 2 { - return err!( + return Err(anyhow!( "Failed to parse ranges: Range should contain exactly one `-`, found {}", x - ); + )); } ranges.push(range[0].parse()?..=range[1].parse()?); @@ -148,7 +140,7 @@ struct MftDump { } impl MftDump { - pub fn from_cli_matches(matches: &ArgMatches) -> Result<Self, StdErr> { + pub fn from_cli_matches(matches: &ArgMatches) -> Result<Self> { let output_format = OutputFormat::from_str(matches.value_of("output-format").unwrap_or_default()) .expect("Validated with clap default values"); @@ -159,11 +151,11 @@ impl MftDump { match Self::create_output_file(path, !matches.is_present("no-confirm-overwrite")) { Ok(f) => Some(Box::new(f)), Err(e) => { - return err!( + return Err(anyhow!( "An error occurred while creating output file at `{}` - `{}`", path, e - ); + )); } } } else { @@ -205,12 +197,15 @@ impl MftDump { }) } - fn create_output_dir(path: impl AsRef<Path>) -> Result<(), StdErr> { + fn create_output_dir(path: impl AsRef<Path>) -> Result<()> { let p = path.as_ref(); if p.exists() { if !p.is_dir() { - return err!("There is a file at {}, refusing to overwrite", p.display()); + return Err(anyhow!( + "There is a file at {}, refusing to overwrite", + p.display() + )); } // p exists and is a directory, it's ok to add files. } else { @@ -221,17 +216,14 @@ impl MftDump { } /// If `prompt` is passed, will display a confirmation prompt before overwriting files. - fn create_output_file( - path: impl AsRef<Path>, - prompt: bool, - ) -> Result<File, Box<dyn std::error::Error>> { + fn create_output_file(path: impl AsRef<Path>, prompt: bool) -> Result<File> { let p = path.as_ref(); if p.is_dir() { - return err!( + return Err(anyhow!( "There is a directory at {}, refusing to overwrite", p.display() - ); + )); } if p.exists() { @@ -245,11 +237,11 @@ impl MftDump { .interact() { Ok(true) => Ok(File::create(p)?), - Ok(false) => err!("Cancelled"), - Err(e) => err!( + Ok(false) => Err(anyhow!("Cancelled")), + Err(e) => Err(anyhow!( "Failed to write confirmation prompt to term caused by\n{}", e - ), + )), } } else { Ok(File::create(p)?) @@ -267,23 +259,23 @@ impl MftDump { Ok(File::create(p)?) } } - None => err!("Output file cannot be root."), + None => Err(anyhow!("Output file cannot be root.")), } } } /// Main entry point for `EvtxDump` - pub fn run(&mut self) -> Result<(), StdErr> { + pub fn run(&mut self) -> Result<()> { self.try_to_initialize_logging(); let mut parser = match MftParser::from_path(&self.filepath) { Ok(parser) => parser, Err(e) => { - return err!( + return Err(anyhow!( "Failed to open file {}.\n\tcaused by: {}", self.filepath.display(), &e - ) + )) } }; @@ -320,12 +312,6 @@ impl MftDump { }, Err(error) => { eprintln!("{}", error); - - if self.backtraces { - if let Some(bt) = error.backtrace() { - eprintln!("{}", bt); - } - } continue; } }; @@ -371,11 +357,11 @@ impl MftDump { ); if PathBuf::from(&data_stream_path).exists() { - return err!( + return Err(anyhow!( "Tried to override an existing stream {} already exists!\ This is a bug, please report to github!", data_stream_path - ); + )); } let mut f = File::create(&data_stream_path)?; @@ -407,12 +393,12 @@ impl MftDump { io::stderr(), ) { Ok(_) => {} - Err(e) => eprintln!("Failed to initialize logging: {}", e.description()), + Err(e) => eprintln!("Failed to initialize logging: {}", e), }; } } - pub fn print_json_entry(&mut self, entry: &MftEntry) -> Result<(), Box<dyn std::error::Error>> { + pub fn print_json_entry(&mut self, entry: &MftEntry) -> Result<()> { let out = self .output .as_mut() @@ -435,7 +421,7 @@ impl MftDump { entry: &MftEntry, parser: &mut MftParser<impl ReadSeek>, writer: &mut csv::Writer<W>, - ) -> Result<(), Box<dyn std::error::Error>> { + ) -> Result<()> { let flat_entry = FlatMftEntryWithName::from_entry(&entry, parser); writer.serialize(flat_entry)?; diff --git a/src/entry.rs b/src/entry.rs index 9e43855..2f89d04 100644 --- a/src/entry.rs +++ b/src/entry.rs @@ -1,8 +1,7 @@ -use crate::err::{self, Result}; +use crate::err::{self, Error, Result}; use crate::impl_serialize_for_bitflags; use log::trace; -use snafu::{ensure, ResultExt}; use winstructs::ntfs::mft_reference::MftReference; @@ -98,12 +97,11 @@ impl EntryHeader { let header_is_valid = [FILE_HEADER, BAAD_HEADER, ZERO_HEADER].contains(&&signature); - ensure!( - header_is_valid, - err::InvalidEntrySignature { - bad_sig: signature.to_vec() - } - ); + if !header_is_valid { + return Err(Error::InvalidEntrySignature { + bad_sig: signature.to_vec(), + }); + } if signature == *ZERO_HEADER { return Ok(Self::zero()); @@ -120,7 +118,7 @@ impl EntryHeader { let entry_size_allocated = reader.read_u32::<LittleEndian>()?; let base_reference = - MftReference::from_reader(reader).context(err::FailedToReadMftReference)?; + MftReference::from_reader(reader).map_err(Error::failed_to_read_mft_reference)?; let first_attribute_id = reader.read_u16::<LittleEndian>()?; @@ -187,8 +185,8 @@ impl MftEntry { }) } - /// Initializes an MFT Entry from a buffer but skips checking and fixing the - /// fixup array. This will throw InvalidEntrySignature error if the entry header + /// Initializes an MFT Entry from a buffer but skips checking and fixing the + /// fixup array. This will throw InvalidEntrySignature error if the entry header /// is not valid. pub fn from_buffer_skip_fixup(buffer: Vec<u8>, entry_number: u64) -> Result<MftEntry> { let mut cursor = Cursor::new(&buffer); @@ -196,12 +194,11 @@ impl MftEntry { let entry_header = EntryHeader::from_reader(&mut cursor, entry_number)?; trace!("Number of sectors: {:#?}", entry_header); - ensure!( - entry_header.is_valid(), - err::InvalidEntrySignature { - bad_sig: entry_header.signature.to_vec() - } - ); + if !entry_header.is_valid() { + return Err(Error::InvalidEntrySignature { + bad_sig: entry_header.signature.to_vec(), + }); + } Ok(MftEntry { header: entry_header, @@ -264,14 +261,13 @@ impl MftEntry { let end_of_sector_bytes = &mut buffer[end_of_sector_bytes_start_offset..end_of_sector_bytes_end_offset]; - ensure!( - end_of_sector_bytes == update_sequence, - err::FailedToApplyFixup { + if end_of_sector_bytes != update_sequence { + return Err(Error::FailedToApplyFixup { stride_number, end_of_sector_bytes: end_of_sector_bytes.to_vec(), - fixup_bytes: fixup_bytes.to_vec() - } - ); + fixup_bytes: fixup_bytes.to_vec(), + }); + } end_of_sector_bytes.copy_from_slice(&fixup_bytes); } @@ -308,12 +304,9 @@ impl MftEntry { return None; } - match cursor.seek(SeekFrom::Start(offset)).context(err::IoError) { - Ok(_) => {} - Err(e) => { - exhausted = true; - return Some(Err(e)); - } + if let Err(e) = cursor.seek(SeekFrom::Start(offset)) { + exhausted = true; + return Some(Err(e.into())); }; let header = MftAttributeHeader::from_stream(&mut cursor); @@ -1,61 +1,71 @@ -use snafu::{Backtrace, Snafu}; -use std::path::PathBuf; -use std::{io, result}; +use std::path::{Path, PathBuf}; +use thiserror::Error; -pub type Result<T> = result::Result<T, Error>; +pub type Result<T> = ::std::result::Result<T, Error>; -#[derive(Debug, Snafu)] -#[snafu(visibility(pub(crate)))] +#[derive(Debug, Error)] pub enum Error { - #[snafu(display("An I/O error has occurred: {}", source))] + #[error("An I/O error has occurred: {}", source)] IoError { + #[from] source: std::io::Error, - backtrace: Backtrace, }, - #[snafu(display("Failed to open file {}: {}", path.display(), source))] + #[error("Failed to open file {}: {}", path.display(), source)] FailedToOpenFile { path: PathBuf, source: std::io::Error, }, - #[snafu(display("Error while decoding name in filename attribute"))] + #[error("Error while decoding name in filename attribute")] InvalidFilename, - #[snafu(display( + #[error( "Bad signature: {:x?}, expected one of [b\"FILE\", b\"BAAD\", b\"0000\"]", bad_sig - ))] + )] InvalidEntrySignature { bad_sig: Vec<u8> }, - #[snafu(display("Unknown `AttributeType`: {:04X}", attribute_type))] + #[error("Unknown `AttributeType`: {:04X}", attribute_type)] UnknownAttributeType { attribute_type: u32 }, - #[snafu(display("Unknown filename namespace {}", namespace))] + #[error("Unknown filename namespace {}", namespace)] UnknownNamespace { namespace: u8 }, - #[snafu(display("Unhandled resident flag: {} (offset: {})", flag, offset))] + #[error("Unhandled resident flag: {} (offset: {})", flag, offset)] UnhandledResidentFlag { flag: u8, offset: u64 }, - #[snafu(display( + #[error( "Fixup bytes do not match bytes at end of stride {} {:x?}: {:x?}", stride_number, end_of_sector_bytes, fixup_bytes - ))] + )] FailedToApplyFixup { stride_number: usize, end_of_sector_bytes: Vec<u8>, fixup_bytes: Vec<u8>, }, - #[snafu(display("Failed to read MftReference: `{}`", source))] + #[error("Failed to read MftReference: `{}`", source)] FailedToReadMftReference { source: winstructs::err::Error }, - #[snafu(display("Failed to read WindowsTime: `{}`", source))] + #[error("Failed to read WindowsTime: `{}`", source)] FailedToReadWindowsTime { source: winstructs::err::Error }, - #[snafu(display("Failed to read GUID: `{}`", source))] + #[error("Failed to read GUID: `{}`", source)] FailedToReadGuid { source: winstructs::err::Error }, - #[snafu(display("An unexpected error has occurred: {}", detail))] + #[error("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, - backtrace: Backtrace::new(), +impl Error { + pub fn failed_to_read_windows_time(source: winstructs::err::Error) -> Error { + Error::FailedToReadWindowsTime { source } + } + + pub fn failed_to_read_mft_reference(source: winstructs::err::Error) -> Error { + Error::FailedToReadMftReference { source } + } + + pub fn failed_to_read_guid(source: winstructs::err::Error) -> Error { + Error::FailedToReadGuid { source } + } + + pub fn failed_to_open_file(path: impl AsRef<Path>, source: std::io::Error) -> Error { + Error::FailedToOpenFile { + path: path.as_ref().to_path_buf(), + source, } } } @@ -1,9 +1,8 @@ use crate::entry::MftEntry; -use crate::err::{self, Result}; +use crate::err::{Error, Result}; use crate::{EntryHeader, ReadSeek}; use log::{debug, trace}; -use snafu::ResultExt; use lru::LruCache; use std::fs::{self, File}; @@ -25,7 +24,7 @@ impl MftParser<BufReader<File>> { pub fn from_path(filename: impl AsRef<Path>) -> Result<Self> { let f = filename.as_ref(); - let mft_fh = File::open(f).context(err::FailedToOpenFile { path: f.to_owned() })?; + let mft_fh = File::open(f).map_err(|e| Error::failed_to_open_file(f, e))?; let size = fs::metadata(f)?.len(); Self::from_read_seek(BufReader::with_capacity(4096, mft_fh), Some(size)) |