]> git.proxmox.com Git - proxmox-backup.git/blob - src/pxar/decoder.rs
src/pxar/decoder.rs: get correct stats for root directory
[proxmox-backup.git] / src / pxar / decoder.rs
1 //! *pxar* format decoder for seekable files
2 //!
3 //! This module contain the code to decode *pxar* archive files.
4
5 use failure::*;
6
7 use super::format_definition::*;
8 use super::sequential_decoder::*;
9
10 use std::io::{Read, Seek, SeekFrom};
11 use std::path::{Path, PathBuf};
12
13 use std::ffi::OsString;
14
15
16 pub struct DirectoryEntry {
17 start: u64,
18 end: u64,
19 pub filename: OsString,
20 pub entry: PxarEntry,
21 }
22
23 // This one needs Read+Seek
24 pub struct Decoder<R: Read + Seek, F: Fn(&Path) -> Result<(), Error>> {
25 inner: SequentialDecoder<R, F>,
26 root_start: u64,
27 root_end: u64,
28 }
29
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;
32
33 impl <R: Read + Seek, F: Fn(&Path) -> Result<(), Error>> Decoder<R, F> {
34
35 pub fn new(mut reader: R, callback: F) -> Result<Self, Error> {
36
37 let root_end = reader.seek(SeekFrom::End(0))?;
38
39 Ok(Self {
40 inner: SequentialDecoder::new(reader, super::flags::DEFAULT, callback),
41 root_start: 0,
42 root_end: root_end,
43 })
44 }
45
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()?;
51 Ok(DirectoryEntry {
52 start: self.root_start,
53 end: self.root_end,
54 filename: OsString::new(), // Empty
55 entry: entry,
56 })
57 }
58
59 fn seek(&mut self, pos: SeekFrom) -> Result<u64, Error> {
60 let pos = self.inner.get_reader_mut().seek(pos)?;
61 Ok(pos)
62 }
63
64 pub fn restore(
65 &mut self,
66 dir: &DirectoryEntry,
67 path: &Path,
68 ) -> Result<(), Error> {
69 let start = dir.start;
70
71 self.seek(SeekFrom::Start(start))?;
72
73 self.inner.restore(path, &Vec::new())?;
74
75 Ok(())
76 }
77
78 fn read_directory_entry(&mut self, start: u64, end: u64) -> Result<DirectoryEntry, Error> {
79
80 self.seek(SeekFrom::Start(start))?;
81
82 let head: PxarHeader = self.inner.read_item()?;
83
84 if head.htype != PXAR_FILENAME {
85 bail!("wrong filename header type for object [{}..{}]", start, end);
86 }
87
88 let entry_start = start + head.size;
89
90 let filename = self.inner.read_filename(head.size)?;
91
92 let head: PxarHeader = self.inner.read_item()?;
93 check_ca_header::<PxarEntry>(&head, PXAR_ENTRY)?;
94 let entry: PxarEntry = self.inner.read_item()?;
95
96 Ok(DirectoryEntry {
97 start: entry_start,
98 end: end,
99 filename: filename,
100 entry,
101 })
102 }
103
104 pub fn list_dir(&mut self, dir: &DirectoryEntry) -> Result<Vec<DirectoryEntry>, Error> {
105
106 let start = dir.start;
107 let end = dir.end;
108
109 //println!("list_dir1: {} {}", start, end);
110
111 if (end - start) < (HEADER_SIZE + GOODBYE_ITEM_SIZE) {
112 bail!("detected short object [{}..{}]", start, end);
113 }
114
115 self.seek(SeekFrom::Start(end - GOODBYE_ITEM_SIZE))?;
116
117 let item: PxarGoodbyeItem = self.inner.read_item()?;
118
119 if item.hash != PXAR_GOODBYE_TAIL_MARKER {
120 bail!("missing goodbye tail marker for object [{}..{}]", start, end);
121 }
122
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);
126
127 }
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);
131 }
132
133 let goodbye_start = end - goodbye_table_size;
134
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);
138 }
139
140 self.seek(SeekFrom::Start(goodbye_start))?;
141 let head: PxarHeader = self.inner.read_item()?;
142
143 if head.htype != PXAR_GOODBYE {
144 bail!("wrong goodbye table header type for entry [{}..{}]", start, end);
145 }
146
147 if head.size != goodbye_table_size {
148 bail!("wrong goodbye table size for entry [{}..{}]", start, end);
149 }
150
151 let mut range_list = Vec::new();
152
153 for i in 0..goodbye_inner_size/GOODBYE_ITEM_SIZE {
154 let item: PxarGoodbyeItem = self.inner.read_item()?;
155
156 if item.offset > (goodbye_start - start) {
157 bail!("goodbye entry {} offset out of range [{}..{}] {} {} {}",
158 i, start, end, item.offset, goodbye_start, start);
159 }
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 [{}..{}]",
164 i, start, end);
165 }
166
167 range_list.push((item_start, item_end));
168 }
169
170 let mut result = vec![];
171
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);
175 result.push(entry);
176 }
177
178 Ok(result)
179 }
180
181 pub fn print_filenames<W: std::io::Write>(
182 &mut self,
183 output: &mut W,
184 prefix: &mut PathBuf,
185 dir: &DirectoryEntry,
186 ) -> Result<(), Error> {
187
188 let mut list = self.list_dir(dir)?;
189
190 list.sort_unstable_by(|a, b| a.filename.cmp(&b.filename));
191
192 for item in &list {
193
194 prefix.push(item.filename.clone());
195
196 let mode = item.entry.mode as u32;
197
198 let ifmt = mode & libc::S_IFMT;
199
200 writeln!(output, "{:?}", prefix)?;
201
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 {
208 } else {
209 bail!("unknown item mode/type for {:?}", prefix);
210 }
211
212 prefix.pop();
213 }
214
215 Ok(())
216 }
217 }