From 2095e9d58aa5039c38e76c0c0cbf3a2dd258bbe9 Mon Sep 17 00:00:00 2001 From: Alison Watson Date: Sat, 22 Jun 2019 06:27:50 -0400 Subject: [PATCH] maraiah: rewrite machdr.rs to use Read/Seek --- maraiah/file.rs | 37 ++++++++++++++++++ maraiah/machdr.rs | 97 ++++++++++++++++++++++++++++++++--------------- tests/misc.rs | 15 +++++--- 3 files changed, 113 insertions(+), 36 deletions(-) diff --git a/maraiah/file.rs b/maraiah/file.rs index 0f86c2b..b84042a 100644 --- a/maraiah/file.rs +++ b/maraiah/file.rs @@ -2,6 +2,7 @@ use crate::err::*; use std::fs; +use std::io::{SeekFrom, prelude::*}; /// Confirms that the path `p` is a folder. pub fn validate_folder_path(p: &str) -> ResultS<()> @@ -14,4 +15,40 @@ pub fn validate_folder_path(p: &str) -> ResultS<()> } } +impl Drop for SeekBackToStart + where T: Seek +{ + fn drop(&mut self) {if self.fl {let _ = self.seek(SeekFrom::Start(0));}} +} + +impl std::ops::Deref for SeekBackToStart + where T: Seek +{ + type Target = T; + + fn deref(&self) -> &Self::Target {&self.sc} +} + +impl std::ops::DerefMut for SeekBackToStart + where T: Seek +{ + fn deref_mut(&mut self) -> &mut Self::Target {&mut self.sc} +} + +impl SeekBackToStart + where T: Seek +{ + pub fn new(sc: T) -> Self {SeekBackToStart{sc, fl: true}} + + pub fn set_seek(&mut self, fl: bool) {self.fl = fl;} + pub fn get_seek(&self) -> bool {self.fl} +} + +/// Seeks back to the starting position of the inner object when losing scope, +/// unless a flag is set. +pub struct SeekBackToStart { + sc: T, + fl: bool, +} + // EOF diff --git a/maraiah/machdr.rs b/maraiah/machdr.rs index 91b11a3..69e8139 100644 --- a/maraiah/machdr.rs +++ b/maraiah/machdr.rs @@ -1,50 +1,77 @@ //! Macintosh archived format header utilities. -use crate::bin::*; +use crate::file::SeekBackToStart; +use std::io::{SeekFrom, prelude::*}; -/// Checks for an `AppleSingle` header. Returns offset to the resource fork. -pub fn check_apple_single(b: &[u8]) -> Option +/// Checks for an Apple Single header. Returns offset to the resource fork. +pub fn check_apple_single(fp: &mut R) -> bool + where R: Read + Seek { + let mut fp = SeekBackToStart::new(fp); + + let mut magic = [0; 8]; + let magic = if fp.read(&mut magic).is_ok() {magic} else {return false;}; + // check magic numbers - if b.len() < 26 || *b.get(0..8)? != [0, 5, 22, 0, 0, 2, 0, 0] { - return None; + if magic != [0, 5, 22, 0, 0, 2, 0, 0] { + return false; } - let num = usize::from(u16b(&b[24..])); + let mut num = [0; 2]; + let num = if fp.read(&mut num).is_ok() {num} else {return false;}; + let num = u64::from(u16::from_be_bytes(num)); - if b.len() < 26 + 12 * num { - return None; + if fp.seek(SeekFrom::Start(26 + 12 * num)).is_err() | + fp.seek(SeekFrom::Start(26)).is_err() { + return false; } // get the resource fork (entity 1) - for i in 0..num { - let p = 26 + 12 * i; - let ent = u32b(&b[p..]); - let ofs = usize_from_u32(u32b(&b[p + 4..])); - let len = usize_from_u32(u32b(&b[p + 8..])); + for _ in 0..num { + let mut ent = [0; 4]; + let mut ofs = [0; 4]; + let mut len = [0; 4]; + let ent = if fp.read(&mut ent).is_ok() {ent} else {return false;}; + let ofs = if fp.read(&mut ofs).is_ok() {ofs} else {return false;}; + let len = if fp.read(&mut len).is_ok() {len} else {return false;}; + let ent = u32::from_be_bytes(ent); + let ofs = u64::from(u32::from_be_bytes(ofs)); + let len = u64::from(u32::from_be_bytes(len)); if ent == 1 { - return if ofs + len > b.len() {None} else {Some(ofs)}; + if fp.seek(SeekFrom::Start(ofs + len)).is_ok() & + fp.seek(SeekFrom::Start(ofs)).is_ok() { + fp.set_seek(false); + return true; + } else { + return false; + } } } // no resource fork - None + false } -/// Checks for a `MacBinary II` header. Returns offset to the resource fork. -pub fn check_macbin(b: &[u8]) -> Option +/// Checks for a Mac Binary II header. Returns offset to the resource fork. +pub fn check_macbin(fp: &mut R) -> bool + where R: Read + Seek { - // check legacy version, length, zero fill, and macbin2 version - // I swear this isn't *completely* magic - if b.len() < 128 || b[0] != 0 || b[1] > 63 || b[74] != 0 || b[123] > 129 { - return None; + let mut fp = SeekBackToStart::new(fp); + + let mut head = [0; 128]; + let head = if fp.read(&mut head).is_ok() {head} else {return false;}; + + // check legacy version, length, zero fill, and macbin2 version. I swear, + // this isn't *completely* magic + if head[0] != 0 || head[1] > 63 || head[74] != 0 || head[123] > 129 { + return false; } let mut crc = 0; // check header crc - for &byte in b.iter().take(124) { + for &byte in head.iter().take(124) { for j in 8..16 { let d = u16::from(byte) << j; @@ -57,22 +84,32 @@ pub fn check_macbin(b: &[u8]) -> Option } // if ok, resource fork follows - if crc == u16b(&b[124..]) { - Some(128) + if crc == u16::from_be_bytes([head[124], head[125]]) { + fp.set_seek(false); + true } else { - None + false } } /// Reads a `MacBin` or `AppleSingle` header if there is one and returns the /// offset from the start of the header to the resource fork (if one is found.) -pub fn try_mac_header(b: &[u8]) -> usize +pub fn try_mac_header(fp: &mut R) -> bool + where R: Read + Seek { - if let Some(ofs) = check_macbin(b) { - ofs - } else { - check_apple_single(b).unwrap_or(0) + if check_macbin(fp) { + return true; } + + let _ = fp.seek(SeekFrom::Start(0)); + + if check_apple_single(fp) { + return true; + } + + let _ = fp.seek(SeekFrom::Start(0)); + + false } // EOF diff --git a/tests/misc.rs b/tests/misc.rs index fe9a2ce..8bbf01e 100644 --- a/tests/misc.rs +++ b/tests/misc.rs @@ -7,8 +7,10 @@ fn machdr_must_process() { const INPUT: &[u8] = include_bytes!("data/misc/macbin.in"); - assert_eq!(machdr::check_macbin(INPUT), Some(128)); - assert_eq!(machdr::try_mac_header(INPUT), 128); + let mut inp = std::io::Cursor::new(INPUT); + + assert_eq!(machdr::check_macbin(&mut inp), true); + assert_eq!(machdr::try_mac_header(&mut inp), true); // FIXME: missing test data for applesingle } @@ -16,10 +18,11 @@ fn machdr_must_process() #[test] fn machdr_must_not_process() { - for inp in &RANDOM { - assert_eq!(machdr::check_macbin(inp), None); - assert_eq!(machdr::check_apple_single(inp), None); - assert_eq!(machdr::try_mac_header(inp), 0); + for rinp in &RANDOM { + let mut inp = std::io::Cursor::new(rinp); + assert_eq!(machdr::check_macbin(&mut inp), false); + assert_eq!(machdr::check_apple_single(&mut inp), false); + assert_eq!(machdr::try_mac_header(&mut inp), false); } }