]>
git.proxmox.com Git - proxmox-backup.git/blob - src/pxar/decoder.rs
1 //! *pxar* format decoder for seekable files
3 //! This module contain the code to decode *pxar* archive files.
7 use super::format_definition
::*;
8 use super::sequential_decoder
::*;
10 use std
::io
::{Read, Seek, SeekFrom}
;
11 use std
::path
::{Path, PathBuf}
;
13 use std
::ffi
::OsString
;
16 pub struct DirectoryEntry
{
19 pub filename
: OsString
,
23 // This one needs Read+Seek
24 pub struct Decoder
<R
: Read
+ Seek
, F
: Fn(&Path
) -> Result
<(), Error
>> {
25 inner
: SequentialDecoder
<R
, F
>,
30 const HEADER_SIZE
: u64 = std
::mem
::size_of
::<PxarHeader
>() as u64;
31 const GOODBYE_ITEM_SIZE
: u64 = std
::mem
::size_of
::<PxarGoodbyeItem
>() as u64;
33 impl <R
: Read
+ Seek
, F
: Fn(&Path
) -> Result
<(), Error
>> Decoder
<R
, F
> {
35 pub fn new(mut reader
: R
, callback
: F
) -> Result
<Self, Error
> {
37 let root_end
= reader
.seek(SeekFrom
::End(0))?
;
40 inner
: SequentialDecoder
::new(reader
, super::flags
::DEFAULT
, callback
),
46 pub fn root(&mut self) -> Result
<DirectoryEntry
, Error
> {
47 self.seek(SeekFrom
::Start(0))?
;
48 let header
: PxarHeader
= self.inner
.read_item()?
;
49 check_ca_header
::<PxarEntry
>(&header
, PXAR_ENTRY
)?
;
50 let entry
: PxarEntry
= self.inner
.read_item()?
;
52 start
: self.root_start
,
54 filename
: OsString
::new(), // Empty
59 fn seek(&mut self, pos
: SeekFrom
) -> Result
<u64, Error
> {
60 let pos
= self.inner
.get_reader_mut().seek(pos
)?
;
68 ) -> Result
<(), Error
> {
69 let start
= dir
.start
;
71 self.seek(SeekFrom
::Start(start
))?
;
73 self.inner
.restore(path
, &Vec
::new())?
;
78 fn read_directory_entry(&mut self, start
: u64, end
: u64) -> Result
<DirectoryEntry
, Error
> {
80 self.seek(SeekFrom
::Start(start
))?
;
82 let head
: PxarHeader
= self.inner
.read_item()?
;
84 if head
.htype
!= PXAR_FILENAME
{
85 bail
!("wrong filename header type for object [{}..{}]", start
, end
);
88 let entry_start
= start
+ head
.size
;
90 let filename
= self.inner
.read_filename(head
.size
)?
;
92 let head
: PxarHeader
= self.inner
.read_item()?
;
93 check_ca_header
::<PxarEntry
>(&head
, PXAR_ENTRY
)?
;
94 let entry
: PxarEntry
= self.inner
.read_item()?
;
104 pub fn list_dir(&mut self, dir
: &DirectoryEntry
) -> Result
<Vec
<DirectoryEntry
>, Error
> {
106 let start
= dir
.start
;
109 //println!("list_dir1: {} {}", start, end);
111 if (end
- start
) < (HEADER_SIZE
+ GOODBYE_ITEM_SIZE
) {
112 bail
!("detected short object [{}..{}]", start
, end
);
115 self.seek(SeekFrom
::Start(end
- GOODBYE_ITEM_SIZE
))?
;
117 let item
: PxarGoodbyeItem
= self.inner
.read_item()?
;
119 if item
.hash
!= PXAR_GOODBYE_TAIL_MARKER
{
120 bail
!("missing goodbye tail marker for object [{}..{}]", start
, end
);
123 let goodbye_table_size
= item
.size
;
124 if goodbye_table_size
< (HEADER_SIZE
+ GOODBYE_ITEM_SIZE
) {
125 bail
!("short goodbye table size for object [{}..{}]", start
, end
);
128 let goodbye_inner_size
= goodbye_table_size
- HEADER_SIZE
- GOODBYE_ITEM_SIZE
;
129 if (goodbye_inner_size
% GOODBYE_ITEM_SIZE
) != 0 {
130 bail
!("wrong goodbye inner table size for entry [{}..{}]", start
, end
);
133 let goodbye_start
= end
- goodbye_table_size
;
135 if item
.offset
!= (goodbye_start
- start
) {
136 println
!("DEBUG: {} {}", u64::from_le(item
.offset
), goodbye_start
- start
);
137 bail
!("wrong offset in goodbye tail marker for entry [{}..{}]", start
, end
);
140 self.seek(SeekFrom
::Start(goodbye_start
))?
;
141 let head
: PxarHeader
= self.inner
.read_item()?
;
143 if head
.htype
!= PXAR_GOODBYE
{
144 bail
!("wrong goodbye table header type for entry [{}..{}]", start
, end
);
147 if head
.size
!= goodbye_table_size
{
148 bail
!("wrong goodbye table size for entry [{}..{}]", start
, end
);
151 let mut range_list
= Vec
::new();
153 for i
in 0..goodbye_inner_size
/GOODBYE_ITEM_SIZE
{
154 let item
: PxarGoodbyeItem
= self.inner
.read_item()?
;
156 if item
.offset
> (goodbye_start
- start
) {
157 bail
!("goodbye entry {} offset out of range [{}..{}] {} {} {}",
158 i
, start
, end
, item
.offset
, goodbye_start
, start
);
160 let item_start
= goodbye_start
- item
.offset
;
161 let item_end
= item_start
+ item
.size
;
162 if item_end
> goodbye_start
{
163 bail
!("goodbye entry {} end out of range [{}..{}]",
167 range_list
.push((item_start
, item_end
));
170 let mut result
= vec
![];
172 for (item_start
, item_end
) in range_list
{
173 let entry
= self.read_directory_entry(item_start
, item_end
)?
;
174 //println!("ENTRY: {} {} {:?}", item_start, item_end, entry.filename);
181 pub fn print_filenames
<W
: std
::io
::Write
>(
184 prefix
: &mut PathBuf
,
185 dir
: &DirectoryEntry
,
186 ) -> Result
<(), Error
> {
188 let mut list
= self.list_dir(dir
)?
;
190 list
.sort_unstable_by(|a
, b
| a
.filename
.cmp(&b
.filename
));
194 prefix
.push(item
.filename
.clone());
196 let mode
= item
.entry
.mode
as u32;
198 let ifmt
= mode
& libc
::S_IFMT
;
200 writeln
!(output
, "{:?}", prefix
)?
;
202 if ifmt
== libc
::S_IFDIR
{
203 self.print_filenames(output
, prefix
, item
)?
;
204 } else if ifmt
== libc
::S_IFREG
{
205 } else if ifmt
== libc
::S_IFLNK
{
206 } else if ifmt
== libc
::S_IFBLK
{
207 } else if ifmt
== libc
::S_IFCHR
{
209 bail
!("unknown item mode/type for {:?}", prefix
);