]> git.proxmox.com Git - proxmox-backup.git/blob - src/pxar/sequential_decoder.rs
src/pxar/sequential_decoder.rs: Call callback only on restored paths.
[proxmox-backup.git] / src / pxar / sequential_decoder.rs
1 //! *pxar* format decoder.
2 //!
3 //! This module contain the code to decode *pxar* archive files.
4
5 use failure::*;
6 use endian_trait::Endian;
7
8 use super::format_definition::*;
9 use super::exclude_pattern::*;
10
11 use std::io::{Read, Write};
12 use std::path::{Path, PathBuf};
13
14 use std::os::unix::io::AsRawFd;
15 use std::os::unix::io::RawFd;
16 use std::os::unix::io::FromRawFd;
17 use std::os::unix::ffi::{OsStrExt, OsStringExt};
18 use std::ffi::CString;
19 use std::ffi::{OsStr, OsString};
20
21 use nix::fcntl::OFlag;
22 use nix::sys::stat::Mode;
23 use nix::errno::Errno;
24 use nix::NixPath;
25
26 use proxmox::tools::io::ReadExt;
27 use proxmox::tools::vec;
28
29 use crate::tools::fs;
30 use crate::tools::acl;
31 use crate::tools::xattr;
32
33 // This one need Read, but works without Seek
34 pub struct SequentialDecoder<'a, R: Read, F: Fn(&Path) -> Result<(), Error>> {
35 reader: &'a mut R,
36 feature_flags: u64,
37 skip_buffer: Vec<u8>,
38 callback: F,
39 }
40
41 const HEADER_SIZE: u64 = std::mem::size_of::<CaFormatHeader>() as u64;
42
43 impl <'a, R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<'a, R, F> {
44
45 pub fn new(reader: &'a mut R, feature_flags: u64, callback: F) -> Self {
46 let skip_buffer = vec::undefined(64*1024);
47
48 Self {
49 reader,
50 feature_flags,
51 skip_buffer,
52 callback,
53 }
54 }
55
56 pub (crate) fn get_reader_mut(&mut self) -> & mut R {
57 self.reader
58 }
59
60 pub (crate) fn read_item<T: Endian>(&mut self) -> Result<T, Error> {
61
62 let mut result: T = unsafe { std::mem::uninitialized() };
63
64 let buffer = unsafe { std::slice::from_raw_parts_mut(
65 &mut result as *mut T as *mut u8,
66 std::mem::size_of::<T>()
67 )};
68
69 self.reader.read_exact(buffer)?;
70
71 Ok(result.from_le())
72 }
73
74 fn read_link(&mut self, size: u64) -> Result<PathBuf, Error> {
75 if size < (HEADER_SIZE + 2) {
76 bail!("dectected short link target.");
77 }
78 let target_len = size - HEADER_SIZE;
79
80 if target_len > (libc::PATH_MAX as u64) {
81 bail!("link target too long ({}).", target_len);
82 }
83
84 let mut buffer = self.reader.read_exact_allocated(target_len as usize)?;
85
86 let last_byte = buffer.pop().unwrap();
87 if last_byte != 0u8 {
88 bail!("link target not nul terminated.");
89 }
90
91 Ok(PathBuf::from(std::ffi::OsString::from_vec(buffer)))
92 }
93
94 fn read_hardlink(&mut self, size: u64) -> Result<(PathBuf, u64), Error> {
95 if size < (HEADER_SIZE + 8 + 2) {
96 bail!("dectected short hardlink header.");
97 }
98 let offset: u64 = self.read_item()?;
99 let target = self.read_link(size - 8)?;
100
101 for c in target.components() {
102 match c {
103 std::path::Component::Normal(_) => { /* OK */ },
104 _ => {
105 bail!("hardlink target contains invalid component {:?}", c);
106 }
107 }
108 }
109
110 Ok((target, offset))
111 }
112
113 pub (crate) fn read_filename(&mut self, size: u64) -> Result<OsString, Error> {
114 if size < (HEADER_SIZE + 2) {
115 bail!("dectected short filename");
116 }
117 let name_len = size - HEADER_SIZE;
118
119 if name_len > ((libc::FILENAME_MAX as u64) + 1) {
120 bail!("filename too long ({}).", name_len);
121 }
122
123 let mut buffer = self.reader.read_exact_allocated(name_len as usize)?;
124
125 let last_byte = buffer.pop().unwrap();
126 if last_byte != 0u8 {
127 bail!("filename entry not nul terminated.");
128 }
129
130 if buffer == b"." || buffer == b".." {
131 bail!("found invalid filename '.' or '..'.");
132 }
133
134 if buffer.iter().find(|b| (**b == b'/' || **b == b'\0')).is_some() {
135 bail!("found invalid filename with slashes or nul bytes.");
136 }
137
138 let name = std::ffi::OsString::from_vec(buffer);
139 if name.is_empty() {
140 bail!("found empty filename.");
141 }
142
143 Ok(name)
144 }
145
146 fn has_features(&self, feature_flags: u64) -> bool {
147 (self.feature_flags & feature_flags) == feature_flags
148 }
149
150 fn read_xattr(&mut self, size: usize) -> Result<CaFormatXAttr, Error> {
151 let buffer = self.reader.read_exact_allocated(size)?;
152
153 let separator = buffer.iter().position(|c| *c == b'\0')
154 .ok_or_else(|| format_err!("no value found in xattr"))?;
155
156 let (name, value) = buffer.split_at(separator);
157 if !xattr::is_valid_xattr_name(name) ||
158 xattr::is_security_capability(name)
159 {
160 bail!("incorrect xattr name - {}.", String::from_utf8_lossy(name));
161 }
162
163 Ok(CaFormatXAttr {
164 name: name.to_vec(),
165 value: value[1..].to_vec(),
166 })
167 }
168
169 fn read_fcaps(&mut self, size: usize) -> Result<CaFormatFCaps, Error> {
170 let buffer = self.reader.read_exact_allocated(size)?;
171
172 Ok(CaFormatFCaps { data: buffer })
173 }
174
175 fn restore_attributes(
176 &mut self,
177 entry: &CaFormatEntry,
178 fd: Option<RawFd>
179 ) -> Result<CaFormatHeader, Error> {
180 let mut xattrs = Vec::new();
181 let mut fcaps = None;
182 let mut quota_projid = None;
183
184 let mut acl_user = Vec::new();
185 let mut acl_group = Vec::new();
186 let mut acl_group_obj = None;
187
188 let mut acl_default = None;
189 let mut acl_default_user = Vec::new();
190 let mut acl_default_group = Vec::new();
191
192 let mut head: CaFormatHeader = self.read_item()?;
193 let mut size = (head.size - HEADER_SIZE) as usize;
194 loop {
195 match head.htype {
196 CA_FORMAT_XATTR => {
197 if self.has_features(CA_FORMAT_WITH_XATTRS) {
198 xattrs.push(self.read_xattr(size)?);
199 } else {
200 self.skip_bytes(size)?;
201 }
202 },
203 CA_FORMAT_FCAPS => {
204 if self.has_features(CA_FORMAT_WITH_FCAPS) {
205 fcaps = Some(self.read_fcaps(size)?);
206 } else {
207 self.skip_bytes(size)?;
208 }
209 },
210 CA_FORMAT_ACL_USER => {
211 if self.has_features(CA_FORMAT_WITH_ACL) {
212 acl_user.push(self.read_item::<CaFormatACLUser>()?);
213 } else {
214 self.skip_bytes(size)?;
215 }
216 },
217 CA_FORMAT_ACL_GROUP => {
218 if self.has_features(CA_FORMAT_WITH_ACL) {
219 acl_group.push(self.read_item::<CaFormatACLGroup>()?);
220 } else {
221 self.skip_bytes(size)?;
222 }
223 },
224 CA_FORMAT_ACL_GROUP_OBJ => {
225 if self.has_features(CA_FORMAT_WITH_ACL) {
226 acl_group_obj = Some(self.read_item::<CaFormatACLGroupObj>()?);
227 } else {
228 self.skip_bytes(size)?;
229 }
230 },
231 CA_FORMAT_ACL_DEFAULT => {
232 if self.has_features(CA_FORMAT_WITH_ACL) {
233 acl_default = Some(self.read_item::<CaFormatACLDefault>()?);
234 } else {
235 self.skip_bytes(size)?;
236 }
237 },
238 CA_FORMAT_ACL_DEFAULT_USER => {
239 if self.has_features(CA_FORMAT_WITH_ACL) {
240 acl_default_user.push(self.read_item::<CaFormatACLUser>()?);
241 } else {
242 self.skip_bytes(size)?;
243 }
244 },
245 CA_FORMAT_ACL_DEFAULT_GROUP => {
246 if self.has_features(CA_FORMAT_WITH_ACL) {
247 acl_default_group.push(self.read_item::<CaFormatACLGroup>()?);
248 } else {
249 self.skip_bytes(size)?;
250 }
251 },
252 CA_FORMAT_QUOTA_PROJID => {
253 if self.has_features(CA_FORMAT_WITH_QUOTA_PROJID) {
254 quota_projid = Some(self.read_item::<CaFormatQuotaProjID>()?);
255 } else {
256 self.skip_bytes(size)?;
257 }
258 },
259 _ => break,
260 }
261 head = self.read_item()?;
262 size = (head.size - HEADER_SIZE) as usize;
263 }
264 // If fd is None, this indicates that we just want to skip over these entries (no restore).
265 // If on the other hand there is Some(fd), restore the attributes on it.
266 if let Some(fd) = fd {
267 self.restore_xattrs_fcaps_fd(fd, xattrs, fcaps)?;
268
269 let mut acl = acl::ACL::init(5)?;
270 acl.add_entry_full(acl::ACL_USER_OBJ, None, mode_user_to_acl_permissions(entry.mode))?;
271 acl.add_entry_full(acl::ACL_OTHER, None, mode_other_to_acl_permissions(entry.mode))?;
272 match acl_group_obj {
273 Some(group_obj) => {
274 acl.add_entry_full(acl::ACL_MASK, None, mode_group_to_acl_permissions(entry.mode))?;
275 acl.add_entry_full(acl::ACL_GROUP_OBJ, None, group_obj.permissions)?;
276 },
277 None => {
278 acl.add_entry_full(acl::ACL_GROUP_OBJ, None, mode_group_to_acl_permissions(entry.mode))?;
279 },
280 }
281 for user in acl_user {
282 acl.add_entry_full(acl::ACL_USER, Some(user.uid), user.permissions)?;
283 }
284 for group in acl_group {
285 acl.add_entry_full(acl::ACL_GROUP, Some(group.gid), group.permissions)?;
286 }
287 let proc_path = Path::new("/proc/self/fd/").join(fd.to_string());
288 if !acl.is_valid() {
289 bail!("Error while restoring ACL - ACL invalid");
290 }
291 acl.set_file(&proc_path, acl::ACL_TYPE_ACCESS)?;
292
293 if let Some(default) = acl_default {
294 let mut acl = acl::ACL::init(5)?;
295 acl.add_entry_full(acl::ACL_USER_OBJ, None, default.user_obj_permissions)?;
296 acl.add_entry_full(acl::ACL_GROUP_OBJ, None, default.group_obj_permissions)?;
297 acl.add_entry_full(acl::ACL_OTHER, None, default.other_permissions)?;
298 if default.mask_permissions != std::u64::MAX {
299 acl.add_entry_full(acl::ACL_MASK, None, default.mask_permissions)?;
300 }
301 for user in acl_default_user {
302 acl.add_entry_full(acl::ACL_USER, Some(user.uid), user.permissions)?;
303 }
304 for group in acl_default_group {
305 acl.add_entry_full(acl::ACL_GROUP, Some(group.gid), group.permissions)?;
306 }
307 if !acl.is_valid() {
308 bail!("Error while restoring ACL - ACL invalid");
309 }
310 acl.set_file(&proc_path, acl::ACL_TYPE_DEFAULT)?;
311 }
312 self.restore_quota_projid(fd, quota_projid)?;
313 }
314
315 Ok(head)
316 }
317
318 // Restore xattrs and fcaps to the given RawFd.
319 fn restore_xattrs_fcaps_fd(
320 &mut self,
321 fd: RawFd,
322 xattrs: Vec<CaFormatXAttr>,
323 fcaps: Option<CaFormatFCaps>
324 ) -> Result<(), Error> {
325 for xattr in xattrs {
326 if let Err(err) = xattr::fsetxattr(fd, xattr) {
327 bail!("fsetxattr failed with error: {}", err);
328 }
329 }
330 if let Some(fcaps) = fcaps {
331 if let Err(err) = xattr::fsetxattr_fcaps(fd, fcaps) {
332 bail!("fsetxattr_fcaps failed with error: {}", err);
333 }
334 }
335
336 Ok(())
337 }
338
339 fn restore_quota_projid(
340 &mut self,
341 fd: RawFd,
342 projid: Option<CaFormatQuotaProjID>
343 ) -> Result<(), Error> {
344 if let Some(projid) = projid {
345 let mut fsxattr = fs::FSXAttr::default();
346 unsafe {
347 fs::fs_ioc_fsgetxattr(fd, &mut fsxattr)
348 .map_err(|err| format_err!("error while getting fsxattr to restore quota project id - {}", err))?;
349 }
350 fsxattr.fsx_projid = projid.projid as u32;
351 unsafe {
352 fs::fs_ioc_fssetxattr(fd, &fsxattr)
353 .map_err(|err| format_err!("error while setting fsxattr to restore quota project id - {}", err))?;
354 }
355 }
356
357 Ok(())
358 }
359
360 fn restore_mode(&mut self, entry: &CaFormatEntry, fd: RawFd) -> Result<(), Error> {
361
362 let mode = Mode::from_bits_truncate((entry.mode as u32) & 0o7777);
363
364 nix::sys::stat::fchmod(fd, mode)?;
365
366 Ok(())
367 }
368
369 fn restore_mode_at(&mut self, entry: &CaFormatEntry, dirfd: RawFd, filename: &OsStr) -> Result<(), Error> {
370
371 let mode = Mode::from_bits_truncate((entry.mode as u32) & 0o7777);
372
373 // NOTE: we want :FchmodatFlags::NoFollowSymlink, but fchmodat does not support that
374 // on linux (see man fchmodat). Fortunately, we can simply avoid calling this on symlinks.
375 nix::sys::stat::fchmodat(Some(dirfd), filename, mode, nix::sys::stat::FchmodatFlags::FollowSymlink)?;
376
377 Ok(())
378 }
379
380 fn restore_ugid(&mut self, entry: &CaFormatEntry, fd: RawFd) -> Result<(), Error> {
381
382 let uid = entry.uid as u32;
383 let gid = entry.gid as u32;
384
385 let res = unsafe { libc::fchown(fd, uid, gid) };
386 Errno::result(res)?;
387
388 Ok(())
389 }
390
391 fn restore_ugid_at(&mut self, entry: &CaFormatEntry, dirfd: RawFd, filename: &OsStr) -> Result<(), Error> {
392
393 let uid = entry.uid as u32;
394 let gid = entry.gid as u32;
395
396 let res = filename.with_nix_path(|cstr| unsafe {
397 libc::fchownat(dirfd, cstr.as_ptr(), uid, gid, libc::AT_SYMLINK_NOFOLLOW)
398 })?;
399 Errno::result(res)?;
400
401 Ok(())
402 }
403
404 fn restore_mtime(&mut self, entry: &CaFormatEntry, fd: RawFd) -> Result<(), Error> {
405
406 let times = nsec_to_update_timespec(entry.mtime);
407
408 let res = unsafe { libc::futimens(fd, &times[0]) };
409 Errno::result(res)?;
410
411 Ok(())
412 }
413
414 fn restore_mtime_at(&mut self, entry: &CaFormatEntry, dirfd: RawFd, filename: &OsStr) -> Result<(), Error> {
415
416 let times = nsec_to_update_timespec(entry.mtime);
417
418 let res = filename.with_nix_path(|cstr| unsafe {
419 libc::utimensat(dirfd, cstr.as_ptr(), &times[0], libc::AT_SYMLINK_NOFOLLOW)
420 })?;
421 Errno::result(res)?;
422
423 Ok(())
424 }
425
426 fn restore_device_at(&mut self, entry: &CaFormatEntry, dirfd: RawFd, filename: &OsStr, device: &CaFormatDevice) -> Result<(), Error> {
427
428 let rdev = nix::sys::stat::makedev(device.major, device.minor);
429 let mode = ((entry.mode as u32) & libc::S_IFMT) | 0o0600;
430 let res = filename.with_nix_path(|cstr| unsafe {
431 libc::mknodat(dirfd, cstr.as_ptr(), mode, rdev)
432 })?;
433 Errno::result(res)?;
434
435 Ok(())
436 }
437
438 fn restore_socket_at(&mut self, dirfd: RawFd, filename: &OsStr) -> Result<(), Error> {
439
440 let mode = libc::S_IFSOCK | 0o0600;
441 let res = filename.with_nix_path(|cstr| unsafe {
442 libc::mknodat(dirfd, cstr.as_ptr(), mode, 0)
443 })?;
444 Errno::result(res)?;
445
446 Ok(())
447 }
448
449 fn restore_fifo_at(&mut self, dirfd: RawFd, filename: &OsStr) -> Result<(), Error> {
450
451 let mode = libc::S_IFIFO | 0o0600;
452 let res = filename.with_nix_path(|cstr| unsafe {
453 libc::mkfifoat(dirfd, cstr.as_ptr(), mode)
454 })?;
455 Errno::result(res)?;
456
457 Ok(())
458 }
459
460 fn skip_bytes(&mut self, count: usize) -> Result<(), Error> {
461 let mut done = 0;
462 while done < count {
463 let todo = count - done;
464 let n = if todo > self.skip_buffer.len() { self.skip_buffer.len() } else { todo };
465 let data = &mut self.skip_buffer[..n];
466 self.reader.read_exact(data)?;
467 done += n;
468 }
469 Ok(())
470 }
471
472 fn restore_symlink(
473 &mut self,
474 parent_fd: Option<RawFd>,
475 full_path: &PathBuf,
476 entry: &CaFormatEntry,
477 filename: &OsStr
478 ) -> Result<(), Error> {
479 //fixme: create symlink
480 //fixme: restore permission, acls, xattr, ...
481
482 let head: CaFormatHeader = self.read_item()?;
483 match head.htype {
484 CA_FORMAT_SYMLINK => {
485 let target = self.read_link(head.size)?;
486 //println!("TARGET: {:?}", target);
487 if let Some(fd) = parent_fd {
488 if let Err(err) = symlinkat(&target, fd, filename) {
489 bail!("create symlink {:?} failed - {}", full_path, err);
490 }
491 }
492 }
493 _ => {
494 bail!("got unknown header type inside symlink entry {:016x}", head.htype);
495 }
496 }
497
498 if let Some(fd) = parent_fd {
499 // self.restore_mode_at(&entry, fd, filename)?; //not supported on symlinks
500 self.restore_ugid_at(&entry, fd, filename)?;
501 self.restore_mtime_at(&entry, fd, filename)?;
502 }
503
504 Ok(())
505 }
506
507 fn restore_socket(
508 &mut self,
509 parent_fd: Option<RawFd>,
510 entry: &CaFormatEntry,
511 filename: &OsStr
512 ) -> Result<(), Error> {
513 if let Some(fd) = parent_fd {
514 self.restore_socket_at(fd, filename)?;
515 self.restore_mode_at(&entry, fd, filename)?;
516 self.restore_ugid_at(&entry, fd, filename)?;
517 self.restore_mtime_at(&entry, fd, filename)?;
518 }
519
520 Ok(())
521 }
522
523 fn restore_fifo(
524 &mut self,
525 parent_fd: Option<RawFd>,
526 entry: &CaFormatEntry,
527 filename: &OsStr
528 ) -> Result<(), Error> {
529 if let Some(fd) = parent_fd {
530 self.restore_fifo_at(fd, filename)?;
531 self.restore_mode_at(&entry, fd, filename)?;
532 self.restore_ugid_at(&entry, fd, filename)?;
533 self.restore_mtime_at(&entry, fd, filename)?;
534 }
535
536 Ok(())
537 }
538
539 fn restore_device(
540 &mut self,
541 parent_fd: Option<RawFd>,
542 entry: &CaFormatEntry,
543 filename: &OsStr
544 ) -> Result<(), Error> {
545 let head: CaFormatHeader = self.read_item()?;
546 if head.htype != CA_FORMAT_DEVICE {
547 bail!("got unknown header type inside device entry {:016x}", head.htype);
548 }
549 let device: CaFormatDevice = self.read_item()?;
550 if let Some(fd) = parent_fd {
551 self.restore_device_at(&entry, fd, filename, &device)?;
552 self.restore_mode_at(&entry, fd, filename)?;
553 self.restore_ugid_at(&entry, fd, filename)?;
554 self.restore_mtime_at(&entry, fd, filename)?;
555 }
556
557 Ok(())
558 }
559
560 /// Restores a regular file with its content and associated attributes to the
561 /// folder provided by the raw filedescriptor.
562 /// If None is passed instead of a filedescriptor, the file is not restored but
563 /// the archive reader is skipping over it instead.
564 fn restore_regular_file(
565 &mut self,
566 parent_fd: Option<RawFd>,
567 full_path: &PathBuf,
568 entry: &CaFormatEntry,
569 filename: &OsStr
570 ) -> Result<(), Error> {
571 let mut read_buffer: [u8; 64*1024] = unsafe { std::mem::uninitialized() };
572
573 if let Some(fd) = parent_fd {
574 let flags = OFlag::O_CREAT|OFlag::O_WRONLY|OFlag::O_EXCL;
575 let open_mode = Mode::from_bits_truncate(0o0600 | entry.mode as u32); //fixme: upper 32bits of entry.mode?
576 let mut file = file_openat(fd, filename, flags, open_mode)
577 .map_err(|err| format_err!("open file {:?} failed - {}", full_path, err))?;
578
579 self.restore_ugid(&entry, file.as_raw_fd())?;
580 // fcaps have to be restored after restore_ugid as chown clears security.capability xattr, see CVE-2015-1350
581 let head = self.restore_attributes(&entry, Some(file.as_raw_fd()))
582 .map_err(|err| format_err!("Restoring of file attributes failed - {}", err))?;
583
584 if head.htype != CA_FORMAT_PAYLOAD {
585 bail!("got unknown header type for file entry {:016x}", head.htype);
586 }
587
588 if head.size < HEADER_SIZE {
589 bail!("detected short payload");
590 }
591 let need = (head.size - HEADER_SIZE) as usize;
592
593 let mut done = 0;
594 while done < need {
595 let todo = need - done;
596 let n = if todo > read_buffer.len() { read_buffer.len() } else { todo };
597 let data = &mut read_buffer[..n];
598 self.reader.read_exact(data)?;
599 file.write_all(data)?;
600 done += n;
601 }
602
603 self.restore_mode(&entry, file.as_raw_fd())?;
604 self.restore_mtime(&entry, file.as_raw_fd())?;
605 } else {
606 let head = self.restore_attributes(&entry, None)
607 .map_err(|err| format_err!("Restoring of file attributes failed - {}", err))?;
608 if head.htype != CA_FORMAT_PAYLOAD {
609 bail!("got unknown header type for file entry {:016x}", head.htype);
610 }
611 if head.size < HEADER_SIZE {
612 bail!("detected short payload");
613 }
614 self.skip_bytes((head.size - HEADER_SIZE) as usize)?;
615 }
616
617 Ok(())
618 }
619
620 fn restore_dir_sequential(
621 &mut self,
622 base_path: &Path,
623 relative_path: &mut PathBuf,
624 full_path: &PathBuf,
625 parent_fd: Option<RawFd>,
626 entry: &CaFormatEntry,
627 filename: &OsStr,
628 match_pattern: &Vec<PxarExcludePattern>,
629 ) -> Result<(), Error> {
630 // By passing back Some(dir) we assure the fd stays open and in scope
631 let (fd, _dir) = if let Some(pfd) = parent_fd {
632 let dir = if filename.is_empty() {
633 nix::dir::Dir::openat(pfd, ".", OFlag::O_DIRECTORY, Mode::empty())?
634 } else {
635 dir_mkdirat(pfd, filename, true)
636 .map_err(|err| format_err!("unable to open directory {:?} - {}", full_path, err))?
637 };
638 (Some(dir.as_raw_fd()), Some(dir))
639 } else {
640 (None, None)
641 };
642
643 if let Some(fd) = fd {
644 self.restore_ugid(&entry, fd)?;
645 }
646 // fcaps have to be restored after restore_ugid as chown clears security.capability xattr, see CVE-2015-1350
647 let mut head = self.restore_attributes(&entry, fd)
648 .map_err(|err| format_err!("Restoring of directory attributes failed - {}", err))?;
649
650 while head.htype == CA_FORMAT_FILENAME {
651 let name = self.read_filename(head.size)?;
652 relative_path.push(&name);
653 self.restore_sequential(base_path, relative_path, &name, fd, match_pattern)?;
654 relative_path.pop();
655 head = self.read_item()?;
656 }
657
658 if head.htype != CA_FORMAT_GOODBYE {
659 bail!("got unknown header type inside directory entry {:016x}", head.htype);
660 }
661
662 //println!("Skip Goodbye");
663 if head.size < HEADER_SIZE { bail!("detected short goodbye table"); }
664 self.skip_bytes((head.size - HEADER_SIZE) as usize)?;
665
666 // Only restore if we want to restore this part of the archive
667 if let Some(fd) = fd {
668 self.restore_mode(&entry, fd)?;
669 self.restore_mtime(&entry, fd)?;
670 }
671
672 Ok(())
673 }
674
675 /// Restore an archive into the specified directory.
676 ///
677 /// The directory is created if it does not exist.
678 pub fn restore(&mut self, path: &Path, match_pattern: &Vec<PxarExcludePattern>) -> Result<(), Error> {
679
680 let _ = std::fs::create_dir(path);
681
682 let dir = nix::dir::Dir::open(path, nix::fcntl::OFlag::O_DIRECTORY, nix::sys::stat::Mode::empty())
683 .map_err(|err| format_err!("unable to open target directory {:?} - {}", path, err))?;
684 let fd = Some(dir.as_raw_fd());
685
686 let mut relative_path = PathBuf::new();
687 self.restore_sequential(path, &mut relative_path, &OsString::new(), fd, match_pattern)
688 }
689
690 fn restore_sequential(
691 &mut self,
692 base_path: &Path,
693 relative_path: &mut PathBuf,
694 filename: &OsStr, // repeats path last relative_path component
695 parent_fd: Option<RawFd>,
696 match_pattern: &Vec<PxarExcludePattern>,
697 ) -> Result<(), Error> {
698 let full_path = base_path.join(&relative_path);
699
700 let head: CaFormatHeader = self.read_item()?;
701 if head.htype == PXAR_FORMAT_HARDLINK {
702 let (target, _offset) = self.read_hardlink(head.size)?;
703 let target_path = base_path.join(&target);
704 if let Some(_) = parent_fd {
705 (self.callback)(&full_path)?;
706 hardlink(&target_path, &full_path)?;
707 }
708 return Ok(());
709 }
710
711 check_ca_header::<CaFormatEntry>(&head, CA_FORMAT_ENTRY)?;
712 let entry: CaFormatEntry = self.read_item()?;
713
714 let mut fd = parent_fd;
715 let mut child_pattern = Vec::new();
716 if match_pattern.len() > 0 {
717 if filename.is_empty() {
718 child_pattern = match_pattern.clone();
719 } else {
720 match match_filename(filename, entry.mode as u32 & libc::S_IFMT == libc::S_IFDIR, match_pattern) {
721 (MatchType::None, _) => fd = None,
722 (MatchType::Exclude, _) => fd = None,
723 (_, pattern) => child_pattern = pattern,
724 }
725 }
726 }
727
728 if fd.is_some() {
729 (self.callback)(&full_path)?;
730 }
731
732 match entry.mode as u32 & libc::S_IFMT {
733 libc::S_IFDIR => self.restore_dir_sequential(base_path, relative_path, &full_path, fd, &entry, &filename, &child_pattern),
734 libc::S_IFLNK => self.restore_symlink(fd, &full_path, &entry, &filename),
735 libc::S_IFSOCK => self.restore_socket(fd, &entry, &filename),
736 libc::S_IFIFO => self.restore_fifo(fd, &entry, &filename),
737 libc::S_IFBLK | libc::S_IFCHR => self.restore_device(fd, &entry, &filename),
738 libc::S_IFREG => self.restore_regular_file(fd, &full_path, &entry, &filename),
739 _ => Ok(()),
740 }
741 }
742
743 /// List/Dump archive content.
744 ///
745 /// Simply print the list of contained files. This dumps archive
746 /// format details when the verbose flag is set (useful for debug).
747 pub fn dump_entry<W: std::io::Write>(
748 &mut self,
749 path: &mut PathBuf,
750 verbose: bool,
751 output: &mut W,
752 ) -> Result<(), Error> {
753
754 let print_head = |head: &CaFormatHeader| {
755 println!("Type: {:016x}", head.htype);
756 println!("Size: {}", head.size);
757 };
758
759 let head: CaFormatHeader = self.read_item()?;
760 if verbose {
761 println!("Path: {:?}", path);
762 print_head(&head);
763 } else {
764 println!("{:?}", path);
765 }
766
767 if head.htype == PXAR_FORMAT_HARDLINK {
768 let (target, offset) = self.read_hardlink(head.size)?;
769 if verbose {
770 println!("Hardlink: {} {:?}", offset, target);
771 }
772 return Ok(());
773 }
774
775 check_ca_header::<CaFormatEntry>(&head, CA_FORMAT_ENTRY)?;
776 let entry: CaFormatEntry = self.read_item()?;
777
778 if verbose {
779 println!("Mode: {:08x} {:08x}", entry.mode, (entry.mode as u32) & libc::S_IFDIR);
780 }
781
782 let ifmt = (entry.mode as u32) & libc::S_IFMT;
783
784 if ifmt == libc::S_IFDIR {
785
786 let mut entry_count = 0;
787
788 loop {
789 let head: CaFormatHeader = self.read_item()?;
790 if verbose {
791 print_head(&head);
792 }
793
794 // This call covers all the cases of the match statement
795 // regarding extended attributes. These calls will never
796 // break on the loop and can therefore be handled separately.
797 // If the header was matched, true is returned and we can continue
798 if self.dump_if_attribute(&head, verbose)? {
799 continue;
800 }
801
802 match head.htype {
803 CA_FORMAT_FILENAME => {
804 let name = self.read_filename(head.size)?;
805 if verbose { println!("Name: {:?}", name); }
806 entry_count += 1;
807 path.push(&name);
808 self.dump_entry(path, verbose, output)?;
809 path.pop();
810 },
811 CA_FORMAT_GOODBYE => {
812 let table_size = (head.size - HEADER_SIZE) as usize;
813 if verbose {
814 println!("Goodbye: {:?}", path);
815 self.dump_goodby_entries(entry_count, table_size)?;
816 } else {
817 self.skip_bytes(table_size)?;
818 }
819 break;
820 },
821 _ => panic!("got unexpected header type inside directory"),
822 }
823 }
824 } else if (ifmt == libc::S_IFBLK) || (ifmt == libc::S_IFCHR) ||
825 (ifmt == libc::S_IFLNK) || (ifmt == libc::S_IFREG)
826 {
827 loop {
828 let head: CaFormatHeader = self.read_item()?;
829 if verbose {
830 print_head(&head);
831 }
832
833 // This call covers all the cases of the match statement
834 // regarding extended attributes. These calls will never
835 // break on the loop and can therefore be handled separately.
836 // If the header was matched, true is returned and we can continue
837 if self.dump_if_attribute(&head, verbose)? {
838 continue;
839 }
840
841 match head.htype {
842 CA_FORMAT_SYMLINK => {
843 let target = self.read_link(head.size)?;
844 if verbose {
845 println!("Symlink: {:?}", target);
846 }
847 break;
848 },
849 CA_FORMAT_DEVICE => {
850 let device: CaFormatDevice = self.read_item()?;
851 if verbose {
852 println!("Device: {}, {}", device.major, device.minor);
853 }
854 break;
855 },
856 CA_FORMAT_PAYLOAD => {
857 let payload_size = (head.size - HEADER_SIZE) as usize;
858 if verbose {
859 println!("Payload: {}", payload_size);
860 }
861 self.skip_bytes(payload_size)?;
862 break;
863 }
864 _ => {
865 panic!("got unexpected header type inside non-directory");
866 }
867 }
868 }
869 } else if ifmt == libc::S_IFIFO {
870 if verbose {
871 println!("Fifo:");
872 }
873 } else if ifmt == libc::S_IFSOCK {
874 if verbose {
875 println!("Socket:");
876 }
877 } else {
878 panic!("unknown st_mode");
879 }
880 Ok(())
881 }
882
883 fn dump_if_attribute(&mut self, header: &CaFormatHeader, verbose: bool) -> Result<bool, Error> {
884 match header.htype {
885 CA_FORMAT_XATTR => {
886 let xattr = self.read_xattr((header.size - HEADER_SIZE) as usize)?;
887 if verbose && self.has_features(CA_FORMAT_WITH_XATTRS) {
888 println!("XAttr: {:?}", xattr);
889 }
890 },
891 CA_FORMAT_FCAPS => {
892 let fcaps = self.read_fcaps((header.size - HEADER_SIZE) as usize)?;
893 if verbose && self.has_features(CA_FORMAT_WITH_FCAPS) {
894 println!("FCaps: {:?}", fcaps);
895 }
896 },
897 CA_FORMAT_ACL_USER => {
898 let user = self.read_item::<CaFormatACLUser>()?;
899 if verbose && self.has_features(CA_FORMAT_WITH_ACL) {
900 println!("ACLUser: {:?}", user);
901 }
902 },
903 CA_FORMAT_ACL_GROUP => {
904 let group = self.read_item::<CaFormatACLGroup>()?;
905 if verbose && self.has_features(CA_FORMAT_WITH_ACL) {
906 println!("ACLGroup: {:?}", group);
907 }
908 },
909 CA_FORMAT_ACL_GROUP_OBJ => {
910 let group_obj = self.read_item::<CaFormatACLGroupObj>()?;
911 if verbose && self.has_features(CA_FORMAT_WITH_ACL) {
912 println!("ACLGroupObj: {:?}", group_obj);
913 }
914 },
915 CA_FORMAT_ACL_DEFAULT => {
916 let default = self.read_item::<CaFormatACLDefault>()?;
917 if verbose && self.has_features(CA_FORMAT_WITH_ACL) {
918 println!("ACLDefault: {:?}", default);
919 }
920 },
921 CA_FORMAT_ACL_DEFAULT_USER => {
922 let default_user = self.read_item::<CaFormatACLUser>()?;
923 if verbose && self.has_features(CA_FORMAT_WITH_ACL) {
924 println!("ACLDefaultUser: {:?}", default_user);
925 }
926 },
927 CA_FORMAT_ACL_DEFAULT_GROUP => {
928 let default_group = self.read_item::<CaFormatACLGroup>()?;
929 if verbose && self.has_features(CA_FORMAT_WITH_ACL) {
930 println!("ACLDefaultGroup: {:?}", default_group);
931 }
932 },
933 CA_FORMAT_QUOTA_PROJID => {
934 let quota_projid = self.read_item::<CaFormatQuotaProjID>()?;
935 if verbose && self.has_features(CA_FORMAT_WITH_QUOTA_PROJID) {
936 println!("Quota project id: {:?}", quota_projid);
937 }
938 },
939 _ => return Ok(false),
940 }
941
942 Ok(true)
943 }
944
945 fn dump_goodby_entries(
946 &mut self,
947 entry_count: usize,
948 table_size: usize,
949 ) -> Result<(), Error> {
950
951 const GOODBYE_ITEM_SIZE: usize = std::mem::size_of::<CaFormatGoodbyeItem>();
952
953 if table_size < GOODBYE_ITEM_SIZE {
954 bail!("Goodbye table to small ({} < {})", table_size, GOODBYE_ITEM_SIZE);
955 }
956 if (table_size % GOODBYE_ITEM_SIZE) != 0 {
957 bail!("Goodbye table with strange size ({})", table_size);
958 }
959
960 let entries = table_size / GOODBYE_ITEM_SIZE;
961
962 if entry_count != (entries - 1) {
963 bail!("Goodbye table with wrong entry count ({} != {})", entry_count, entries - 1);
964 }
965
966 let mut count = 0;
967
968 loop {
969 let item: CaFormatGoodbyeItem = self.read_item()?;
970 count += 1;
971 if item.hash == CA_FORMAT_GOODBYE_TAIL_MARKER {
972 if count != entries {
973 bail!("unexpected goodbye tail marker");
974 }
975 println!("Goodby tail mark.");
976 break;
977 }
978 println!("Goodby item: offset {}, size {}, hash {:016x}", item.offset, item.size, item.hash);
979 if count >= entries {
980 bail!("too many goodbye items (no tail marker)");
981 }
982 }
983
984 Ok(())
985 }
986 }
987
988 fn match_filename(
989 filename: &OsStr,
990 is_dir: bool,
991 match_pattern: &Vec<PxarExcludePattern>
992 ) -> (MatchType, Vec<PxarExcludePattern>) {
993 let mut child_pattern = Vec::new();
994 let mut match_state = MatchType::None;
995 // read_filename() checks for nul bytes, so it is save to unwrap here
996 let name = CString::new(filename.as_bytes()).unwrap();
997
998 for pattern in match_pattern {
999 match pattern.matches_filename(&name, is_dir) {
1000 MatchType::None => {},
1001 // The logic is inverted here, since PxarExcludePattern assumes excludes not includes
1002 MatchType::Exclude => {
1003 match_state = MatchType::Include;
1004 let incl_pattern = PxarExcludePattern::from_line(b"**/*").unwrap().unwrap();
1005 child_pattern.push(incl_pattern.get_rest_pattern());
1006 },
1007 MatchType::Include => match_state = MatchType::Exclude,
1008 MatchType::PartialExclude => {
1009 if match_state != MatchType::Include && match_state != MatchType::Exclude {
1010 match_state = MatchType::PartialInclude;
1011 }
1012 child_pattern.push(pattern.get_rest_pattern());
1013 },
1014 MatchType::PartialInclude => {
1015 if match_state == MatchType::PartialInclude {
1016 match_state = MatchType::PartialExclude;
1017 }
1018 child_pattern.push(pattern.get_rest_pattern());
1019 },
1020 }
1021 }
1022
1023 (match_state, child_pattern)
1024 }
1025
1026 fn file_openat(parent: RawFd, filename: &OsStr, flags: OFlag, mode: Mode) -> Result<std::fs::File, Error> {
1027
1028 let fd = filename.with_nix_path(|cstr| {
1029 nix::fcntl::openat(parent, cstr.as_ref(), flags, mode)
1030 })??;
1031
1032 let file = unsafe { std::fs::File::from_raw_fd(fd) };
1033
1034 Ok(file)
1035 }
1036
1037 fn dir_mkdirat(parent: RawFd, filename: &OsStr, create_new: bool) -> Result<nix::dir::Dir, nix::Error> {
1038
1039 // call mkdirat first
1040 let res = filename.with_nix_path(|cstr| unsafe {
1041 libc::mkdirat(parent, cstr.as_ptr(), libc::S_IRWXU)
1042 })?;
1043
1044 match Errno::result(res) {
1045 Ok(_) => {},
1046 Err(err) => {
1047 if err == nix::Error::Sys(nix::errno::Errno::EEXIST) {
1048 if create_new { return Err(err); }
1049 } else {
1050 return Err(err);
1051 }
1052 }
1053 }
1054
1055 let dir = nix::dir::Dir::openat(parent, filename, OFlag::O_DIRECTORY, Mode::empty())?;
1056
1057 Ok(dir)
1058 }
1059
1060 fn hardlink(oldpath: &Path, newpath: &Path) -> Result<(), Error> {
1061 oldpath.with_nix_path(|oldpath| {
1062 newpath.with_nix_path(|newpath| {
1063 let res = unsafe { libc::link(oldpath.as_ptr(), newpath.as_ptr()) };
1064 Errno::result(res)?;
1065 Ok(())
1066 })?
1067 })?
1068 }
1069
1070 fn symlinkat(target: &Path, parent: RawFd, linkname: &OsStr) -> Result<(), Error> {
1071
1072 target.with_nix_path(|target| {
1073 linkname.with_nix_path(|linkname| {
1074 let res = unsafe { libc::symlinkat(target.as_ptr(), parent, linkname.as_ptr()) };
1075 Errno::result(res)?;
1076 Ok(())
1077 })?
1078 })?
1079 }
1080
1081 fn nsec_to_update_timespec(mtime_nsec: u64) -> [libc::timespec; 2] {
1082
1083 // restore mtime
1084 const UTIME_OMIT: i64 = ((1 << 30) - 2);
1085 const NANOS_PER_SEC: i64 = 1_000_000_000;
1086
1087 let sec = (mtime_nsec as i64) / NANOS_PER_SEC;
1088 let nsec = (mtime_nsec as i64) % NANOS_PER_SEC;
1089
1090 let times: [libc::timespec; 2] = [
1091 libc::timespec { tv_sec: 0, tv_nsec: UTIME_OMIT },
1092 libc::timespec { tv_sec: sec, tv_nsec: nsec },
1093 ];
1094
1095 times
1096 }
1097
1098 fn mode_user_to_acl_permissions(mode: u64) -> u64 {
1099 return (mode >> 6) & 7;
1100 }
1101
1102 fn mode_group_to_acl_permissions(mode: u64) -> u64 {
1103 return (mode >> 3) & 7;
1104 }
1105
1106 fn mode_other_to_acl_permissions(mode: u64) -> u64 {
1107 return mode & 7;
1108 }