From 1450e1050d7f97e0e3c7529680553b6a8299beea Mon Sep 17 00:00:00 2001 From: Omer Ben-Amram Date: Mon, 6 Jan 2020 13:08:42 +0200 Subject: clippy lints --- src/attribute/x80.rs | 2 +- src/csv.rs | 18 +++++++++--------- src/entry.rs | 9 ++++----- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/attribute/x80.rs b/src/attribute/x80.rs index f194a0e..3f1efa6 100644 --- a/src/attribute/x80.rs +++ b/src/attribute/x80.rs @@ -26,6 +26,6 @@ impl ser::Serialize for DataAttr { where S: ser::Serializer, { - serializer.serialize_str(&utils::to_hex_string(&self.0).to_string()) + serializer.serialize_str(&utils::to_hex_string(&self.0)) } } diff --git a/src/csv.rs b/src/csv.rs index fbe1b65..1a3aca8 100644 --- a/src/csv.rs +++ b/src/csv.rs @@ -93,7 +93,7 @@ impl FlatMftEntryWithName { let has_ads = entry_attributes .iter() .any(|a| { - a.header.type_code == MftAttributeType::DATA && a.header.name.len() > 0 + a.header.type_code == MftAttributeType::DATA && !a.header.name.is_empty() }); @@ -111,14 +111,14 @@ impl FlatMftEntryWithName { is_a_directory: entry.is_dir(), is_deleted: !entry.header.flags.contains(EntryFlags::ALLOCATED), has_alternate_data_streams: has_ads, - standard_info_flags: standard_info.as_ref().and_then(|i| Some(i.file_flags)), - standard_info_last_modified: standard_info.as_ref().and_then(|i| Some(i.modified)), - standard_info_last_access: standard_info.as_ref().and_then(|i| Some(i.accessed)), - standard_info_created: standard_info.as_ref().and_then(|i| Some(i.created)), - file_name_flags: file_name.as_ref().and_then(|i| Some(i.flags)), - file_name_last_modified: file_name.as_ref().and_then(|i| Some(i.modified)), - file_name_last_access: file_name.as_ref().and_then(|i| Some(i.accessed)), - file_name_created: file_name.as_ref().and_then(|i| Some(i.created)), + standard_info_flags: standard_info.as_ref().map(|i| i.file_flags), + standard_info_last_modified: standard_info.as_ref().map(|i| i.modified), + standard_info_last_access: standard_info.as_ref().map(|i| i.accessed), + standard_info_created: standard_info.as_ref().map(|i| i.created), + file_name_flags: file_name.as_ref().map(|i| i.flags), + file_name_last_modified: file_name.as_ref().map(|i| i.modified), + file_name_last_access: file_name.as_ref().map(|i| i.accessed), + file_name_created: file_name.as_ref().map(|i| i.created), file_size, full_path: parser .get_full_path_for_entry(entry) diff --git a/src/entry.rs b/src/entry.rs index f1b2a63..9e43855 100644 --- a/src/entry.rs +++ b/src/entry.rs @@ -22,9 +22,9 @@ use std::io::{Cursor, Seek}; const SEQUENCE_NUMBER_STRIDE: usize = 512; -pub const ZERO_HEADER: &'static [u8; 4] = b"\x00\x00\x00\x00"; -pub const BAAD_HEADER: &'static [u8; 4] = b"BAAD"; -pub const FILE_HEADER: &'static [u8; 4] = b"FILE"; +pub const ZERO_HEADER: &[u8; 4] = b"\x00\x00\x00\x00"; +pub const BAAD_HEADER: &[u8; 4] = b"BAAD"; +pub const FILE_HEADER: &[u8; 4] = b"FILE"; #[derive(Debug, Clone)] pub struct MftEntry { @@ -239,7 +239,6 @@ impl MftEntry { /// https://docs.microsoft.com/en-us/windows/desktop/devnotes/multi-sector-header /// **Note**: The fixup will be written at the end of each 512-byte stride, /// even if the device has more (or less) than 512 bytes per sector. - #[must_use] fn apply_fixups(header: &EntryHeader, buffer: &mut [u8]) -> Result<()> { let number_of_fixups = u32::from(header.usa_size - 1); trace!("Number of fixups: {}", number_of_fixups); @@ -313,7 +312,7 @@ impl MftEntry { Ok(_) => {} Err(e) => { exhausted = true; - return Some(Err(e.into())); + return Some(Err(e)); } }; -- cgit v1.2.3 From 84daee189019564510255d4fd861712d5e127ad3 Mon Sep 17 00:00:00 2001 From: Omer Ben-Amram Date: Mon, 6 Jan 2020 17:18:01 +0200 Subject: snafu -> thiserror/anyhow --- Cargo.toml | 49 +++++++++++++++++----------------- src/attribute/header.rs | 12 ++++----- src/attribute/raw.rs | 3 +-- src/attribute/x10.rs | 11 ++++---- src/attribute/x20.rs | 7 +++-- src/attribute/x30.rs | 19 +++++++------- src/attribute/x40.rs | 11 ++++---- src/attribute/x80.rs | 3 +-- src/bin/mft_dump.rs | 70 ++++++++++++++++++++----------------------------- src/entry.rs | 51 ++++++++++++++++------------------- src/err.rs | 62 +++++++++++++++++++++++++------------------ src/mft.rs | 5 ++-- 12 files changed, 142 insertions(+), 161 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7c0070a..d889597 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,45 +11,46 @@ authors = ["Omer Ben-Amram "] 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 { 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(reader: &mut S) -> Result { 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::()?; 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::()?; 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(stream: &mut S) -> Result { 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::()?; @@ -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(stream: &mut S, data_size: usize) -> Result { - 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(stream: &mut S, data_size: usize) -> Result { 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::::from(format!($($tt)*))) } -} - -type StdErr = Box; - #[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 { + fn from_str(s: &str) -> Result { 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 { + pub fn from_cli_matches(matches: &ArgMatches) -> Result { 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) -> Result<(), StdErr> { + fn create_output_dir(path: impl AsRef) -> 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, - prompt: bool, - ) -> Result> { + fn create_output_file(path: impl AsRef, prompt: bool) -> Result { 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> { + 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, writer: &mut csv::Writer, - ) -> Result<(), Box> { + ) -> 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::()?; 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::()?; @@ -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, entry_number: u64) -> Result { 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); diff --git a/src/err.rs b/src/err.rs index 78076fb..838c55a 100644 --- a/src/err.rs +++ b/src/err.rs @@ -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 = result::Result; +pub type Result = ::std::result::Result; -#[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 }, - #[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, fixup_bytes: Vec, }, - #[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 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, source: std::io::Error) -> Error { + Error::FailedToOpenFile { + path: path.as_ref().to_path_buf(), + source, } } } diff --git a/src/mft.rs b/src/mft.rs index 9d78543..7cf3b0b 100644 --- a/src/mft.rs +++ b/src/mft.rs @@ -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> { pub fn from_path(filename: impl AsRef) -> Result { 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)) -- cgit v1.2.3 From aea96c09d0581f7b289d1f1da06cd0191e87ace3 Mon Sep 17 00:00:00 2001 From: Omer Ben-Amram Date: Mon, 6 Jan 2020 21:11:48 +0200 Subject: migrated to anyhow, and fixed more lints --- src/attribute/raw.rs | 2 +- src/attribute/x30.rs | 2 +- src/attribute/x80.rs | 2 +- src/bin/mft_dump.rs | 37 ++++++++----------------------------- src/entry.rs | 2 +- src/err.rs | 10 +++++----- src/mft.rs | 2 ++ tests/fixtures.rs | 4 ++-- tests/test_cli_interactive.rs | 10 +++++----- 9 files changed, 26 insertions(+), 45 deletions(-) diff --git a/src/attribute/raw.rs b/src/attribute/raw.rs index c00fb1d..d416340 100644 --- a/src/attribute/raw.rs +++ b/src/attribute/raw.rs @@ -1,5 +1,5 @@ use crate::attribute::MftAttributeType; -use crate::err::{self, Result}; +use crate::err::{Result}; use crate::{utils, ReadSeek}; use serde::{ser, Serialize}; diff --git a/src/attribute/x30.rs b/src/attribute/x30.rs index 1228039..22aa8ec 100644 --- a/src/attribute/x30.rs +++ b/src/attribute/x30.rs @@ -1,5 +1,5 @@ use crate::attribute::FileAttributeFlags; -use crate::err::{self, Error, Result}; +use crate::err::{Error, Result}; use crate::ReadSeek; use log::trace; diff --git a/src/attribute/x80.rs b/src/attribute/x80.rs index b9d8ec0..d295e16 100644 --- a/src/attribute/x80.rs +++ b/src/attribute/x80.rs @@ -1,4 +1,4 @@ -use crate::err::{self, Result}; +use crate::err::{Result}; use crate::{utils, ReadSeek}; use serde::ser; diff --git a/src/bin/mft_dump.rs b/src/bin/mft_dump.rs index 793581a..6655524 100644 --- a/src/bin/mft_dump.rs +++ b/src/bin/mft_dump.rs @@ -13,7 +13,6 @@ use anyhow::{anyhow, Context, Error, Result}; use std::fs::File; use std::io::Write; use std::path::{Path, PathBuf}; -use std::process::exit; use mft::entry::ZERO_HEADER; use std::fmt::Write as FmtWrite; @@ -136,7 +135,6 @@ struct MftDump { verbosity_level: Option, output_format: OutputFormat, ranges: Option, - backtraces: bool, } impl MftDump { @@ -145,7 +143,9 @@ impl MftDump { OutputFormat::from_str(matches.value_of("output-format").unwrap_or_default()) .expect("Validated with clap default values"); - let backtraces = matches.is_present("backtraces"); + if matches.is_present("backtraces") { + std::env::set_var("RUST_LIB_BACKTRACE", "1"); + } let output: Option> = if let Some(path) = matches.value_of("output-target") { match Self::create_output_file(path, !matches.is_present("no-confirm-overwrite")) { @@ -193,7 +193,6 @@ impl MftDump { verbosity_level, output_format, ranges, - backtraces, }) } @@ -268,16 +267,7 @@ impl MftDump { 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(anyhow!( - "Failed to open file {}.\n\tcaused by: {}", - self.filepath.display(), - &e - )) - } - }; + let mut parser = MftParser::from_path(&self.filepath)?; // Since the JSON parser can do away with a &mut Write, but the csv parser needs ownership // of `Write`, we eagerly create the csv writer here, moving the Box out from @@ -456,7 +446,7 @@ pub fn sanitized(component: &str) -> String { buf } -fn main() { +fn main() -> Result<()> { let matches = App::new("MFT Parser") .version(env!("CARGO_PKG_VERSION")) .author("Omer B. ") @@ -520,19 +510,8 @@ fn main() { .help("If set, a backtrace will be printed with some errors if available")) .get_matches(); - let mut app = match MftDump::from_cli_matches(&matches) { - Ok(app) => app, - Err(e) => { - eprintln!("An error occurred while setting up the app: {}", &e); - exit(1); - } - }; + let mut app = MftDump::from_cli_matches(&matches).context("Failed setting up the app")?; + app.run().context("A runtime error has occurred")?; - match app.run() { - Ok(()) => {} - Err(e) => { - eprintln!("A runtime error has occurred: {}", &e); - exit(1); - } - }; + Ok(()) } diff --git a/src/entry.rs b/src/entry.rs index 2f89d04..1ce8f35 100644 --- a/src/entry.rs +++ b/src/entry.rs @@ -1,4 +1,4 @@ -use crate::err::{self, Error, Result}; +use crate::err::{Error, Result}; use crate::impl_serialize_for_bitflags; use log::trace; diff --git a/src/err.rs b/src/err.rs index 838c55a..6073830 100644 --- a/src/err.rs +++ b/src/err.rs @@ -5,12 +5,12 @@ pub type Result = ::std::result::Result; #[derive(Debug, Error)] pub enum Error { - #[error("An I/O error has occurred: {}", source)] + #[error("An I/O error has occurred")] IoError { #[from] source: std::io::Error, }, - #[error("Failed to open file {}: {}", path.display(), source)] + #[error("Failed to open file {}", path.display())] FailedToOpenFile { path: PathBuf, source: std::io::Error, @@ -39,11 +39,11 @@ pub enum Error { end_of_sector_bytes: Vec, fixup_bytes: Vec, }, - #[error("Failed to read MftReference: `{}`", source)] + #[error("Failed to read MftReference")] FailedToReadMftReference { source: winstructs::err::Error }, - #[error("Failed to read WindowsTime: `{}`", source)] + #[error("Failed to read WindowsTime")] FailedToReadWindowsTime { source: winstructs::err::Error }, - #[error("Failed to read GUID: `{}`", source)] + #[error("Failed to read GUID")] FailedToReadGuid { source: winstructs::err::Error }, #[error("An unexpected error has occurred: {}", detail)] Any { detail: String }, diff --git a/src/mft.rs b/src/mft.rs index 7cf3b0b..4cb2c91 100644 --- a/src/mft.rs +++ b/src/mft.rs @@ -180,6 +180,8 @@ mod tests { count += 1; } } + + assert!(count > 0) } #[test] diff --git a/tests/fixtures.rs b/tests/fixtures.rs index 17dfed6..580c4e8 100644 --- a/tests/fixtures.rs +++ b/tests/fixtures.rs @@ -1,9 +1,9 @@ #![allow(dead_code)] use std::path::PathBuf; -use std::sync::{Once, ONCE_INIT}; +use std::sync::{Once}; -static LOGGER_INIT: Once = ONCE_INIT; +static LOGGER_INIT: Once = Once::new(); // Rust runs the tests concurrently, so unless we synchronize logging access // it will crash when attempting to run `cargo test` with some logging facilities. diff --git a/tests/test_cli_interactive.rs b/tests/test_cli_interactive.rs index 6667ac0..987f54a 100644 --- a/tests/test_cli_interactive.rs +++ b/tests/test_cli_interactive.rs @@ -4,13 +4,13 @@ mod fixtures; -use fixtures::*; -use std::fs::File; -use std::io::{Read, Write}; -use tempfile::tempdir; -use assert_cmd::cargo::cargo_bin; + + + + + #[cfg(not(target_os = "windows"))] use rexpect::spawn; -- cgit v1.2.3 From 7aedab4838fd74d4eb64f7ab5900cd35a5c45a6b Mon Sep 17 00:00:00 2001 From: Omer Ben-Amram Date: Mon, 6 Jan 2020 21:18:52 +0200 Subject: fmt --- Cargo.toml | 12 ++++++++---- src/attribute/raw.rs | 2 +- src/attribute/x80.rs | 2 +- src/csv.rs | 5 +---- src/utils.rs | 3 ++- tests/fixtures.rs | 2 +- tests/test_cli_interactive.rs | 7 ------- 7 files changed, 14 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d889597..26ecdab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,6 @@ 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" @@ -29,9 +28,14 @@ itertools = "0.8.2" rand = "0.7.2" # `mft_dump` dependencies -simplelog = "0.7.4" -dialoguer = "0.5.0" -indoc = "0.3.4" +anyhow = {version = "1.0", optional = true} +simplelog = {version = "0.7.4", optional = true} +dialoguer = {version = "0.5.0", optional = true} +indoc = {version = "0.3.4", optional = true} + +[features] +default = ["mft-dump"] +mft-dump = ["anyhow", "simplelog", "dialoguer", "indoc"] [dependencies.chrono] version = "0.4.10" diff --git a/src/attribute/raw.rs b/src/attribute/raw.rs index d416340..2482455 100644 --- a/src/attribute/raw.rs +++ b/src/attribute/raw.rs @@ -1,5 +1,5 @@ use crate::attribute::MftAttributeType; -use crate::err::{Result}; +use crate::err::Result; use crate::{utils, ReadSeek}; use serde::{ser, Serialize}; diff --git a/src/attribute/x80.rs b/src/attribute/x80.rs index d295e16..81918c7 100644 --- a/src/attribute/x80.rs +++ b/src/attribute/x80.rs @@ -1,4 +1,4 @@ -use crate::err::{Result}; +use crate::err::Result; use crate::{utils, ReadSeek}; use serde::ser; diff --git a/src/csv.rs b/src/csv.rs index 1a3aca8..3420b6e 100644 --- a/src/csv.rs +++ b/src/csv.rs @@ -92,10 +92,7 @@ impl FlatMftEntryWithName { let has_ads = entry_attributes .iter() - .any(|a| { - a.header.type_code == MftAttributeType::DATA && !a.header.name.is_empty() - }); - + .any(|a| a.header.type_code == MftAttributeType::DATA && !a.header.name.is_empty()); FlatMftEntryWithName { entry_id: entry.header.record_number, diff --git a/src/utils.rs b/src/utils.rs index 25470e7..559c70c 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -43,7 +43,8 @@ pub fn read_utf16_string(stream: &mut T, len: Option) -> io: }, } - decode_utf16(buffer.into_iter()) + // We need to stop if we see a NUL byte, even if asked for more bytes. + decode_utf16(buffer.into_iter().take_while(|&byte| byte != 0x00)) .map(|r| r.map_err(|_e| io::Error::from(io::ErrorKind::InvalidData))) .collect() } diff --git a/tests/fixtures.rs b/tests/fixtures.rs index 580c4e8..bc831b3 100644 --- a/tests/fixtures.rs +++ b/tests/fixtures.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] use std::path::PathBuf; -use std::sync::{Once}; +use std::sync::Once; static LOGGER_INIT: Once = Once::new(); diff --git a/tests/test_cli_interactive.rs b/tests/test_cli_interactive.rs index 987f54a..2366454 100644 --- a/tests/test_cli_interactive.rs +++ b/tests/test_cli_interactive.rs @@ -4,13 +4,6 @@ mod fixtures; - - - - - - - #[cfg(not(target_os = "windows"))] use rexpect::spawn; -- cgit v1.2.3 From 33fb95823d750ac8b227ac3712b0ce95fcf87b01 Mon Sep 17 00:00:00 2001 From: Omer Ben-Amram Date: Mon, 6 Jan 2020 21:24:15 +0200 Subject: binary target is optional --- Cargo.toml | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 26ecdab..69bfdd5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,31 +11,31 @@ authors = ["Omer Ben-Amram "] edition = "2018" [dependencies] -log = { version = "0.4.8", features = ["release_max_level_debug"] } -clap = "2.33.0" -encoding = "0.2.33" -byteorder = "1.3.2" -bitflags = "1.2.1" +log = { version = "0.4", features = ["release_max_level_debug"] } +encoding = "0.2" +byteorder = "1.3" +bitflags = "1.2" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -csv = "1.1.1" +csv = "1.1" thiserror = "1.0" -num-traits = "0.2.10" -num-derive = "0.3.0" +num-traits = "0.2" +num-derive = "0.3" winstructs = "0.2.3" lru = "0.4.3" -itertools = "0.8.2" -rand = "0.7.2" +itertools = "0.8" +rand = "0.7" # `mft_dump` dependencies +clap = {version = "2.33.0", optional = true} anyhow = {version = "1.0", optional = true} simplelog = {version = "0.7.4", optional = true} dialoguer = {version = "0.5.0", optional = true} indoc = {version = "0.3.4", optional = true} [features] -default = ["mft-dump"] -mft-dump = ["anyhow", "simplelog", "dialoguer", "indoc"] +default = ["mft_dump"] +mft_dump = ["anyhow", "simplelog", "dialoguer", "indoc", "clap"] [dependencies.chrono] version = "0.4.10" @@ -58,6 +58,7 @@ skeptic = "0.13.4" [[bin]] name = "mft_dump" +required-features = ["mft_dump"] [[bench]] name = "benchmark" -- cgit v1.2.3 From 517061677ab30b2417e72c6eb988cd46dbe133b2 Mon Sep 17 00:00:00 2001 From: Omer Ben-Amram Date: Mon, 6 Jan 2020 21:26:37 +0200 Subject: changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42b4e09..1fb0869 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.0] - 2020-01-06 + +### Changed +- Bumped dependencies. +- `mft_dump` is now an optional features so consumers of the library can enjoy faster compilation time. +- Changed error handling to `anyhow` + `thiserror` from `snafu`. + ## [0.4.4] - 2019-06-19 ### Fixed -- cgit v1.2.3 From 2df0d10f21a0f283772dd73711b4be712c1da2f9 Mon Sep 17 00:00:00 2001 From: Omer Ben-Amram Date: Mon, 6 Jan 2020 21:36:30 +0200 Subject: entire non-windows test is now a module --- tests/test_cli_interactive.rs | 141 ++++++++++++++++++++++-------------------- 1 file changed, 74 insertions(+), 67 deletions(-) diff --git a/tests/test_cli_interactive.rs b/tests/test_cli_interactive.rs index 2366454..f2655f2 100644 --- a/tests/test_cli_interactive.rs +++ b/tests/test_cli_interactive.rs @@ -5,71 +5,78 @@ mod fixtures; #[cfg(not(target_os = "windows"))] -use rexpect::spawn; - -// It should behave the same on windows, but interactive testing relies on unix pty internals. -#[test] -#[cfg(not(target_os = "windows"))] -fn test_it_confirms_before_overwriting_a_file() { - let d = tempdir().unwrap(); - let f = d.as_ref().join("test.out"); - - let mut file = File::create(&f).unwrap(); - file.write_all(b"I'm a file!").unwrap(); - - let sample = mft_sample(); - - let cmd_string = format!( - "{bin} -f {output_file} {sample}", - bin = cargo_bin("mft_dump").display(), - output_file = f.to_string_lossy(), - sample = sample.to_str().unwrap() - ); - - let mut p = spawn(&cmd_string, Some(20000)).unwrap(); - p.exp_regex(r#"Are you sure you want to override.*"#) - .unwrap(); - p.send_line("y").unwrap(); - p.exp_eof().unwrap(); - - let mut expected = vec![]; - - File::open(&f).unwrap().read_to_end(&mut expected).unwrap(); - assert!( - !expected.len() > 100, - "Expected output to be printed to file" - ) -} - -#[test] -#[cfg(not(target_os = "windows"))] -fn test_it_confirms_before_overwriting_a_file_and_quits() { - let d = tempdir().unwrap(); - let f = d.as_ref().join("test.out"); - - let mut file = File::create(&f).unwrap(); - file.write_all(b"I'm a file!").unwrap(); - - let sample = mft_sample(); - - let cmd_string = format!( - "{bin} -f {output_file} {sample}", - bin = cargo_bin("mft_dump").display(), - output_file = f.to_string_lossy(), - sample = sample.to_str().unwrap() - ); - - let mut p = spawn(&cmd_string, Some(20000)).unwrap(); - p.exp_regex(r#"Are you sure you want to override.*"#) - .unwrap(); - p.send_line("n").unwrap(); - p.exp_eof().unwrap(); - - let mut expected = vec![]; - - File::open(&f).unwrap().read_to_end(&mut expected).unwrap(); - assert!( - !expected.len() > 100, - "Expected output to be printed to file" - ) +mod tests { + use fixtures::*; + + use std::fs::File; + use std::io::{Read, Write}; + use tempfile::tempdir; + + use assert_cmd::cargo::cargo_bin; + use rexpect::spawn; + + // It should behave the same on windows, but interactive testing relies on unix pty internals. + #[test] + fn test_it_confirms_before_overwriting_a_file() { + let d = tempdir().unwrap(); + let f = d.as_ref().join("test.out"); + + let mut file = File::create(&f).unwrap(); + file.write_all(b"I'm a file!").unwrap(); + + let sample = mft_sample(); + + let cmd_string = format!( + "{bin} -f {output_file} {sample}", + bin = cargo_bin("mft_dump").display(), + output_file = f.to_string_lossy(), + sample = sample.to_str().unwrap() + ); + + let mut p = spawn(&cmd_string, Some(20000)).unwrap(); + p.exp_regex(r#"Are you sure you want to override.*"#) + .unwrap(); + p.send_line("y").unwrap(); + p.exp_eof().unwrap(); + + let mut expected = vec![]; + + File::open(&f).unwrap().read_to_end(&mut expected).unwrap(); + assert!( + !expected.len() > 100, + "Expected output to be printed to file" + ) + } + + #[test] + fn test_it_confirms_before_overwriting_a_file_and_quits() { + let d = tempdir().unwrap(); + let f = d.as_ref().join("test.out"); + + let mut file = File::create(&f).unwrap(); + file.write_all(b"I'm a file!").unwrap(); + + let sample = mft_sample(); + + let cmd_string = format!( + "{bin} -f {output_file} {sample}", + bin = cargo_bin("mft_dump").display(), + output_file = f.to_string_lossy(), + sample = sample.to_str().unwrap() + ); + + let mut p = spawn(&cmd_string, Some(20000)).unwrap(); + p.exp_regex(r#"Are you sure you want to override.*"#) + .unwrap(); + p.send_line("n").unwrap(); + p.exp_eof().unwrap(); + + let mut expected = vec![]; + + File::open(&f).unwrap().read_to_end(&mut expected).unwrap(); + assert!( + !expected.len() > 100, + "Expected output to be printed to file" + ) + } } -- cgit v1.2.3 From 2108f23738463d5a259b0e0d4551cdfea5b74774 Mon Sep 17 00:00:00 2001 From: Omer Ben-Amram Date: Mon, 6 Jan 2020 21:58:57 +0200 Subject: fixed! --- tests/test_cli_interactive.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_cli_interactive.rs b/tests/test_cli_interactive.rs index f2655f2..41c179a 100644 --- a/tests/test_cli_interactive.rs +++ b/tests/test_cli_interactive.rs @@ -2,11 +2,12 @@ // since they use `rexpect`, which internally uses quirky fork semantics to open a pty. // They will fail if tried to be executed concurrently any other CLI test. +#[cfg(not(target_os = "windows"))] mod fixtures; #[cfg(not(target_os = "windows"))] -mod tests { - use fixtures::*; +mod cli_tests { + use super::fixtures::*; use std::fs::File; use std::io::{Read, Write}; -- cgit v1.2.3 From 678d4df9663d4bfdd119e0cbf35f556b4414dec3 Mon Sep 17 00:00:00 2001 From: Omer Ben-Amram Date: Mon, 6 Jan 2020 22:12:19 +0200 Subject: update winstructs --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 69bfdd5..79463e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ csv = "1.1" thiserror = "1.0" num-traits = "0.2" num-derive = "0.3" -winstructs = "0.2.3" +winstructs = "0.3.0" lru = "0.4.3" itertools = "0.8" rand = "0.7" -- cgit v1.2.3