]> git.proxmox.com Git - proxmox-backup.git/blob - src/pxar/decoder.rs
pxar: pass feature_flags to encoder/decoder instead of individual flags
[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 CaDirectoryEntry {
17 start: u64,
18 end: u64,
19 pub filename: OsString,
20 pub entry: CaFormatEntry,
21 }
22
23 // This one needs Read+Seek
24 pub struct Decoder<'a, R: Read + Seek> {
25 inner: SequentialDecoder<'a, R>,
26 root_start: u64,
27 root_end: u64,
28 }
29
30 const HEADER_SIZE: u64 = std::mem::size_of::<CaFormatHeader>() as u64;
31
32 impl <'a, R: Read + Seek> Decoder<'a, R> {
33
34 pub fn new(reader: &'a mut R) -> Result<Self, Error> {
35
36 let root_end = reader.seek(SeekFrom::End(0))?;
37
38 Ok(Self {
39 inner: SequentialDecoder::new(reader, CA_FORMAT_DEFAULT),
40 root_start: 0,
41 root_end: root_end,
42 })
43 }
44
45 pub fn root(&self) -> CaDirectoryEntry {
46 CaDirectoryEntry {
47 start: self.root_start,
48 end: self.root_end,
49 filename: OsString::new(), // Empty
50 entry: CaFormatEntry {
51 mode: 0,
52 flags: 0,
53 uid: 0,
54 gid: 0,
55 mtime: 0,
56 }
57 }
58 }
59
60 fn seek(&mut self, pos: SeekFrom) -> Result<u64, Error> {
61 let pos = self.inner.get_reader_mut().seek(pos)?;
62 Ok(pos)
63 }
64
65 pub fn restore<F>(
66 &mut self,
67 dir: &CaDirectoryEntry,
68 path: &Path,
69 callback: F,
70 ) -> Result<(), Error>
71 where F: Fn(&Path) -> Result<(), Error>
72 {
73 let start = dir.start;
74
75 self.seek(SeekFrom::Start(start))?;
76
77 self.inner.restore(path, &callback)?;
78
79 Ok(())
80 }
81
82 fn read_directory_entry(&mut self, start: u64, end: u64) -> Result<CaDirectoryEntry, Error> {
83
84 self.seek(SeekFrom::Start(start))?;
85
86 let head: CaFormatHeader = self.inner.read_item()?;
87
88 if head.htype != CA_FORMAT_FILENAME {
89 bail!("wrong filename header type for object [{}..{}]", start, end);
90 }
91
92 let entry_start = start + head.size;
93
94 let filename = self.inner.read_filename(head.size)?;
95
96 let head: CaFormatHeader = self.inner.read_item()?;
97 check_ca_header::<CaFormatEntry>(&head, CA_FORMAT_ENTRY)?;
98 let entry: CaFormatEntry = self.inner.read_item()?;
99
100 Ok(CaDirectoryEntry {
101 start: entry_start,
102 end: end,
103 filename: filename,
104 entry,
105 })
106 }
107
108 pub fn list_dir(&mut self, dir: &CaDirectoryEntry) -> Result<Vec<CaDirectoryEntry>, Error> {
109
110 const GOODBYE_ITEM_SIZE: u64 = std::mem::size_of::<CaFormatGoodbyeItem>() as u64;
111
112 let start = dir.start;
113 let end = dir.end;
114
115 //println!("list_dir1: {} {}", start, end);
116
117 if (end - start) < (HEADER_SIZE + GOODBYE_ITEM_SIZE) {
118 bail!("detected short object [{}..{}]", start, end);
119 }
120
121 self.seek(SeekFrom::Start(end - GOODBYE_ITEM_SIZE))?;
122
123 let item: CaFormatGoodbyeItem = self.inner.read_item()?;
124
125 if item.hash != CA_FORMAT_GOODBYE_TAIL_MARKER {
126 bail!("missing goodbye tail marker for object [{}..{}]", start, end);
127 }
128
129 let goodbye_table_size = item.size;
130 if goodbye_table_size < (HEADER_SIZE + GOODBYE_ITEM_SIZE) {
131 bail!("short goodbye table size for object [{}..{}]", start, end);
132
133 }
134 let goodbye_inner_size = goodbye_table_size - HEADER_SIZE - GOODBYE_ITEM_SIZE;
135 if (goodbye_inner_size % GOODBYE_ITEM_SIZE) != 0 {
136 bail!("wrong goodbye inner table size for entry [{}..{}]", start, end);
137 }
138
139 let goodbye_start = end - goodbye_table_size;
140
141 if item.offset != (goodbye_start - start) {
142 println!("DEBUG: {} {}", u64::from_le(item.offset), goodbye_start - start);
143 bail!("wrong offset in goodbye tail marker for entry [{}..{}]", start, end);
144 }
145
146 self.seek(SeekFrom::Start(goodbye_start))?;
147 let head: CaFormatHeader = self.inner.read_item()?;
148
149 if head.htype != CA_FORMAT_GOODBYE {
150 bail!("wrong goodbye table header type for entry [{}..{}]", start, end);
151 }
152
153 if head.size != goodbye_table_size {
154 bail!("wrong goodbye table size for entry [{}..{}]", start, end);
155 }
156
157 let mut range_list = Vec::new();
158
159 for i in 0..goodbye_inner_size/GOODBYE_ITEM_SIZE {
160 let item: CaFormatGoodbyeItem = self.inner.read_item()?;
161
162 if item.offset > (goodbye_start - start) {
163 bail!("goodbye entry {} offset out of range [{}..{}] {} {} {}",
164 i, start, end, item.offset, goodbye_start, start);
165 }
166 let item_start = goodbye_start - item.offset;
167 let item_end = item_start + item.size;
168 if item_end > goodbye_start {
169 bail!("goodbye entry {} end out of range [{}..{}]",
170 i, start, end);
171 }
172
173 range_list.push((item_start, item_end));
174 }
175
176 let mut result = vec![];
177
178 for (item_start, item_end) in range_list {
179 let entry = self.read_directory_entry(item_start, item_end)?;
180 //println!("ENTRY: {} {} {:?}", item_start, item_end, entry.filename);
181 result.push(entry);
182 }
183
184 Ok(result)
185 }
186
187 pub fn print_filenames<W: std::io::Write>(
188 &mut self,
189 output: &mut W,
190 prefix: &mut PathBuf,
191 dir: &CaDirectoryEntry,
192 ) -> Result<(), Error> {
193
194 let mut list = self.list_dir(dir)?;
195
196 list.sort_unstable_by(|a, b| a.filename.cmp(&b.filename));
197
198 for item in &list {
199
200 prefix.push(item.filename.clone());
201
202 let mode = item.entry.mode as u32;
203
204 let ifmt = mode & libc::S_IFMT;
205
206 writeln!(output, "{:?}", prefix)?;
207
208 if ifmt == libc::S_IFDIR {
209 self.print_filenames(output, prefix, item)?;
210 } else if ifmt == libc::S_IFREG {
211 } else if ifmt == libc::S_IFLNK {
212 } else if ifmt == libc::S_IFBLK {
213 } else if ifmt == libc::S_IFCHR {
214 } else {
215 bail!("unknown item mode/type for {:?}", prefix);
216 }
217
218 prefix.pop();
219 }
220
221 Ok(())
222 }
223 }