diff --git a/src/bin/pxar.rs b/src/bin/pxar.rs index e3a8588a..23b4b5d0 100644 --- a/src/bin/pxar.rs +++ b/src/bin/pxar.rs @@ -9,70 +9,30 @@ use proxmox_backup::api_schema::router::*; use serde_json::{Value}; -use std::io::{Read, Write}; +use std::io::Write; use std::path::PathBuf; -use proxmox_backup::pxar::format_definition::*; use proxmox_backup::pxar::encoder::*; use proxmox_backup::pxar::decoder::*; -use proxmox_backup::tools::*; - -fn print_goodby_entries(buffer: &[u8]) -> Result<(), Error> { - println!("GOODBY START: {}", buffer.len()); - - let entry_size = std::mem::size_of::(); - if (buffer.len() % entry_size) != 0 { - bail!("unexpected goodby item size ({})", entry_size); - } - - let mut pos = 0; - - while pos < buffer.len() { - - let item = map_struct::(&buffer[pos..pos+entry_size])?; - - if item.hash == CA_FORMAT_GOODBYE_TAIL_MARKER { - println!(" Entry Offset: {}", item.offset); - if item.size != (buffer.len() + 16) as u64 { - bail!("gut unexpected goodby entry size (tail marker)"); - } - } else { - println!(" Offset: {}", item.offset); - println!(" Size: {}", item.size); - println!(" Hash: {:016x}", item.hash); - } - - pos += entry_size; - } - - Ok(()) -} - fn print_filenames( - _param: Value, + param: Value, _info: &ApiMethod, _rpcenv: &mut RpcEnvironment, ) -> Result { - /* FIXME - let archive = tools::required_string_param(¶m, "archive")?; let file = std::fs::File::open(archive)?; let mut reader = std::io::BufReader::new(file); - let mut decoder = PxarDecoder::new(&mut reader)?; - - let root = decoder.root(); + let mut decoder = PxarDecoder::new(&mut reader); let stdout = std::io::stdout(); let mut out = stdout.lock(); - decoder.print_filenames(&mut out, &mut PathBuf::from("."), &root)?; - */ - - panic!("not implemented"); + let mut path = PathBuf::from("."); + decoder.dump_entry(&mut path, false, &mut out)?; Ok(Value::Null) } @@ -95,7 +55,8 @@ fn dump_archive( println!("PXAR dump: {}", archive); - decoder.dump_archive(&mut out); + let mut path = PathBuf::new(); + decoder.dump_entry(&mut path, true, &mut out)?; Ok(Value::Null) } diff --git a/src/pxar/decoder.rs b/src/pxar/decoder.rs index 4536c9e2..c4644b75 100644 --- a/src/pxar/decoder.rs +++ b/src/pxar/decoder.rs @@ -422,80 +422,151 @@ impl <'a, R: Read> PxarDecoder<'a, R> { Ok(()) } - /// Dump archive format details. This is ment for debugging. - pub fn dump_archive( + /// List/Dump archive content. + /// + /// Simply print the list of contained files. This dumps archive + /// format details when the verbose flag is set (useful for debug). + pub fn dump_entry( &mut self, + path: &mut PathBuf, + verbose: bool, output: &mut W, ) -> Result<(), Error> { - let mut nesting = 0; - - let mut dirpath = PathBuf::new(); - - let head: CaFormatHeader = self.read_item()?; - check_ca_header::(&head, CA_FORMAT_ENTRY)?; - let entry: CaFormatEntry = self.read_item()?; - println!("Root: {:08x} {:08x}", entry.mode, (entry.mode as u32) & libc::S_IFDIR); - - loop { - let head: CaFormatHeader = self.read_item()?; - + let print_head = |head: &CaFormatHeader| { println!("Type: {:016x}", head.htype); println!("Size: {}", head.size); + }; + + let head: CaFormatHeader = self.read_item()?; + if verbose { + println!("Path: {:?}", path); + print_head(&head); + } else { + println!("{:?}", path); + } + + check_ca_header::(&head, CA_FORMAT_ENTRY)?; + let entry: CaFormatEntry = self.read_item()?; + + if verbose { + println!("Mode: {:08x} {:08x}", entry.mode, (entry.mode as u32) & libc::S_IFDIR); + } + // fixme: dump attributes (ACLs, ...) + + let ifmt = (entry.mode as u32) & libc::S_IFMT; + + if ifmt == libc::S_IFDIR { + + let mut entry_count = 0; + + loop { + let head: CaFormatHeader = self.read_item()?; + if verbose { + print_head(&head); + } + match head.htype { + + CA_FORMAT_FILENAME => { + let name = self.read_filename(head.size)?; + if verbose { println!("Name: {:?}", name); } + entry_count += 1; + path.push(&name); + self.dump_entry(path, verbose, output)?; + path.pop(); + } + CA_FORMAT_GOODBYE => { + let table_size = (head.size - HEADER_SIZE) as usize; + if verbose { + println!("Goodbye: {:?}", path); + self.dump_goodby_entries(entry_count, table_size)?; + } else { + self.skip_bytes(table_size); + } + break; + } + _ => { + panic!("got unexpected header type inside directory"); + } + } + } + } else { + + let head: CaFormatHeader = self.read_item()?; + if verbose { + print_head(&head); + } match head.htype { - CA_FORMAT_FILENAME => { - let name = self.read_filename(head.size)?; - //let hash = compute_goodbye_hash(&rest[..rest.len()-1]); - println!("Name: {:?}", name); - - let head: CaFormatHeader = self.read_item()?; - check_ca_header::(&head, CA_FORMAT_ENTRY)?; - let entry: CaFormatEntry = self.read_item()?; - println!("Mode: {:08x} {:08x}", entry.mode, (entry.mode as u32) & libc::S_IFDIR); - - if ((entry.mode as u32) & libc::S_IFMT) == libc::S_IFDIR { - nesting += 1; - dirpath.push(&name); - println!("Path: {:?}", dirpath); - } else { - dirpath.push(&name); - println!("Path: {:?}", dirpath); - dirpath.pop(); - } - } - CA_FORMAT_GOODBYE => { - self.skip_bytes((head.size - HEADER_SIZE) as usize)?; - nesting -= 1; - println!("Goodbye: {:?}", dirpath); - dirpath.pop(); - if nesting == 0 { - // fixme: check eof?? - break; - } - } CA_FORMAT_SYMLINK => { let target = self.read_symlink(head.size)?; - println!("Symlink: {:?}", target); + if verbose { + println!("Symlink: {:?}", target); + } } CA_FORMAT_DEVICE => { let device: CaFormatDevice = self.read_item()?; - println!("Device: {}, {}", device.major, device.minor); + if verbose { + println!("Device: {}, {}", device.major, device.minor); + } } CA_FORMAT_PAYLOAD => { let payload_size = (head.size - HEADER_SIZE) as usize; - println!("Payload: {}", payload_size); + if verbose { + println!("Payload: {}", payload_size); + } self.skip_bytes(payload_size)?; } _ => { - panic!("unknown header type"); + panic!("got unexpected header type inside non-directory"); } } } Ok(()) } + + fn dump_goodby_entries( + &mut self, + entry_count: usize, + table_size: usize, + ) -> Result<(), Error> { + + let item_size = std::mem::size_of::(); + if table_size < item_size { + bail!("Goodbye table to small ({} < {})", table_size, item_size); + } + if (table_size % item_size) != 0 { + bail!("Goodbye table with strange size ({})", table_size); + } + + let entries = (table_size / item_size); + + if entry_count != (entries - 1) { + bail!("Goodbye table with wrong entry count ({} != {})", entry_count, entries - 1); + } + + let mut count = 0; + + loop { + let item: CaFormatGoodbyeItem = self.read_item()?; + count += 1; + if item.hash == CA_FORMAT_GOODBYE_TAIL_MARKER { + if count != entries { + bail!("unexpected goodbye tail marker"); + } + println!("Goodby tail mark."); + break; + } + println!("Goodby item: offset {}, size {}, hash {:016x}", item.offset, item.size, item.hash); + if count >= (table_size / item_size) { + bail!("too many goodbye items (no tail marker)"); + } + } + + Ok(()) + } } fn file_openat(parent: RawFd, filename: &OsStr, flags: OFlag, mode: Mode) -> Result {