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