From b6ebfb8dab054bf7ff7292dc39989a03dbb5256b Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Mon, 7 Jan 2019 19:07:03 +0100 Subject: [PATCH] catar/decoder.rs: start implementing sequential decoder, cleanups And use [derive(Endian)] for endian conversion. --- src/catar/decoder.rs | 167 +++++++++++++++++++++++++-------- src/catar/format_definition.rs | 15 +++ 2 files changed, 142 insertions(+), 40 deletions(-) diff --git a/src/catar/decoder.rs b/src/catar/decoder.rs index 373a1fc5..57a2deff 100644 --- a/src/catar/decoder.rs +++ b/src/catar/decoder.rs @@ -3,12 +3,13 @@ //! This module contain the code to decode *catar* archive files. use failure::*; +use endian_trait::Endian; use super::format_definition::*; use crate::tools; use std::io::{Read, Seek, SeekFrom}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::os::unix::ffi::{OsStrExt, OsStringExt}; use std::ffi::{OsStr, OsString}; @@ -58,6 +59,127 @@ impl <'a, R: Read + Seek> CaTarDecoder<'a, R> { } } + fn read_item(&mut self) -> Result { + + let mut result: T = unsafe { std::mem::uninitialized() }; + + let buffer = unsafe { std::slice::from_raw_parts_mut( + &mut result as *mut T as *mut u8, + std::mem::size_of::() + )}; + + self.reader.read_exact(buffer)?; + + Ok(result.from_le()) + } + + fn read_symlink(&mut self, size: u64) -> Result { + if size < (HEADER_SIZE + 2) { + bail!("dectected short symlink target."); + } + let target_len = size - HEADER_SIZE; + + if target_len > (libc::PATH_MAX as u64) { + bail!("symlink target too long ({}).", target_len); + } + + let mut buffer = vec![0u8; target_len as usize]; + self.reader.read_exact(&mut buffer)?; + + let last_byte = buffer.pop().unwrap(); + if last_byte != 0u8 { + bail!("symlink target not nul terminated."); + } + + Ok(PathBuf::from(std::ffi::OsString::from_vec(buffer))) + } + + fn read_filename(&mut self, size: u64) -> Result { + if size < (HEADER_SIZE + 2) { + bail!("dectected short filename"); + } + let name_len = size - HEADER_SIZE; + + if name_len > ((libc::FILENAME_MAX as u64) + 1) { + bail!("filename too long ({}).", name_len); + } + + let mut buffer = vec![0u8; name_len as usize]; + self.reader.read_exact(&mut buffer)?; + + let last_byte = buffer.pop().unwrap(); + if last_byte != 0u8 { + bail!("filename entry not nul terminated."); + } + + Ok(std::ffi::OsString::from_vec(buffer)) + } + + pub fn restore Result<(), Error>>( + &mut self, + dir: &CaDirectoryEntry, + callback: F, + ) -> Result<(), Error> { + + let start = dir.start; + let end = dir.end; + + self.reader.seek(SeekFrom::Start(start))?; + + let mut path = PathBuf::from("."); + + self.restore_sequential(&mut path, &callback) + } + + pub fn restore_sequential Result<(), Error>>( + &mut self, + path: &mut PathBuf, + callback: &F, + ) -> Result<(), Error> { + + // read ENTRY first + let head: CaFormatHeader = self.read_item()?; + check_ca_header::(&head, CA_FORMAT_ENTRY)?; + let entry: CaFormatEntry = self.read_item()?; + + let is_dir = ((entry.mode as u32) & libc::S_IFMT) == libc::S_IFDIR; + + loop { + let head: CaFormatHeader = self.read_item()?; + match head.htype { + CA_FORMAT_SYMLINK => { + let target = self.read_symlink(head.size)?; + println!("TARGET: {:?}", target); + } + CA_FORMAT_FILENAME => { + if !is_dir { bail!("onyl directoriy entries may contain file names."); } + let name = self.read_filename(head.size)?; + path.push(name); + println!("NAME: {:?}", path); + self.restore_sequential(path, callback)?; + path.pop(); + } + CA_FORMAT_PAYLOAD => { + println!("Skip Payload"); + if head.size < HEADER_SIZE { bail!("detected short payload"); } + self.reader.seek(SeekFrom::Current((head.size - HEADER_SIZE) as i64))?; + return Ok(()); + } + CA_FORMAT_GOODBYE => { + println!("Skip Goodbye"); + if head.size < HEADER_SIZE { bail!("detected short goodbye table"); } + self.reader.seek(SeekFrom::Current((head.size - HEADER_SIZE) as i64))?; + return Ok(()); + } + _ => { + bail!("got unknown header type {:016x}", head.htype); + } + } + } + + Ok(()) + } + fn read_directory_entry(&mut self, start: u64, end: u64) -> Result { self.reader.seek(SeekFrom::Start(start))?; @@ -73,46 +195,11 @@ impl <'a, R: Read + Seek> CaTarDecoder<'a, R> { let entry_start = start + name_len; - if name_len < (HEADER_SIZE + 2) { - bail!("filename size too short for object [{}..{}]", start, end); - } - name_len -= HEADER_SIZE; + let filename = self.read_filename(name_len)?; - if name_len > ((libc::FILENAME_MAX as u64) + 1) { - bail!("filename too long for object [{}..{}]", start, end); - } - - let mut buffer = vec![0u8; name_len as usize]; - self.reader.read_exact(&mut buffer)?; - - // fixme: check nul termination - let last_byte = buffer.pop().unwrap(); - if last_byte != 0u8 { - bail!("filename entry not nul terminated, object [{}..{}]", start, end); - - } - - let filename = std::ffi::OsString::from_vec(buffer); - - let mut buffer = [0u8; HEADER_SIZE as usize]; - self.reader.read_exact(&mut buffer)?; - let head = tools::map_struct::(&buffer)?; - - if u64::from_le(head.htype) != CA_FORMAT_ENTRY { - bail!("wrong entry header type for object [{}..{}]", start, end); - } - - const ENTRY_SIZE: u64 = std::mem::size_of::() as u64; - - let mut entry_len = u64::from_le(head.size); - if entry_len != (HEADER_SIZE + ENTRY_SIZE) { - bail!("wrong entry header size for object [{}..{}]", start, end); - } - entry_len -= HEADER_SIZE; - - let mut buffer = [0u8; ENTRY_SIZE as usize]; - self.reader.read_exact(&mut buffer)?; - let entry = tools::map_struct::(&buffer)?; + let head: CaFormatHeader = self.read_item()?; + check_ca_header::(&head, CA_FORMAT_ENTRY)?; + let entry: CaFormatEntry = self.read_item()?; Ok(CaDirectoryEntry { start: entry_start, diff --git a/src/catar/format_definition.rs b/src/catar/format_definition.rs index 29a02176..8c122eae 100644 --- a/src/catar/format_definition.rs +++ b/src/catar/format_definition.rs @@ -6,6 +6,7 @@ //! `CaFormatHeader`, followed by the item data. use failure::*; +use endian_trait::Endian; use siphasher::sip::SipHasher24; @@ -20,6 +21,7 @@ pub const CA_FORMAT_GOODBYE_TAIL_MARKER: u64 = 0x57446fa533702943; pub const CA_FORMAT_FEATURE_FLAGS_MAX: u64 = 0xb000_0001_ffef_fe26; // fixme: ? +#[derive(Endian)] #[repr(C)] pub struct CaFormatHeader { /// The size of the item, including the size of `CaFormatHeader`. @@ -28,6 +30,7 @@ pub struct CaFormatHeader { pub htype: u64, } +#[derive(Endian)] #[repr(C)] pub struct CaFormatEntry { pub feature_flags: u64, @@ -38,6 +41,7 @@ pub struct CaFormatEntry { pub mtime: u64, } +#[derive(Endian)] #[repr(C)] pub struct CaFormatGoodbyeItem { /// The offset from the start of the GOODBYE object to the start @@ -78,3 +82,14 @@ pub fn compute_goodbye_hash(name: &[u8]) -> u64 { hasher.write(name); hasher.finish() } + +pub fn check_ca_header(head: &CaFormatHeader, htype: u64) -> Result<(), Error> { + if head.htype != htype { + bail!("got wrong header type ({:016x} != {:016x}", head.htype, htype); + } + if head.size != (std::mem::size_of::() + std::mem::size_of::()) as u64 { + bail!("got wrong header size for type {:016x}", htype); + } + + Ok(()) +}