1 //! *pxar* format decoder.
3 //! This module contain the code to decode *pxar* archive files.
5 use std
::ffi
::{OsStr, OsString}
;
6 use std
::io
::{Read, Write}
;
7 use std
::os
::unix
::ffi
::{OsStrExt, OsStringExt}
;
8 use std
::os
::unix
::io
::AsRawFd
;
9 use std
::os
::unix
::io
::FromRawFd
;
10 use std
::os
::unix
::io
::RawFd
;
11 use std
::path
::{Path, PathBuf}
;
13 use endian_trait
::Endian
;
14 use anyhow
::{bail, format_err, Error}
;
15 use nix
::errno
::Errno
;
16 use nix
::fcntl
::OFlag
;
17 use nix
::sys
::stat
::Mode
;
20 use proxmox
::tools
::io
::ReadExt
;
21 use proxmox
::tools
::vec
;
23 use super::dir_stack
::{PxarDir, PxarDirStack}
;
25 use super::format_definition
::*;
26 use super::match_pattern
::{MatchPattern, MatchPatternSlice, MatchType}
;
28 use crate::tools
::acl
;
30 use crate::tools
::xattr
;
32 // This one need Read, but works without Seek
33 pub struct SequentialDecoder
<R
: Read
> {
36 allow_existing_dirs
: bool
,
38 callback
: Option
<Box
<dyn Fn(&Path
) -> Result
<(), Error
> + Send
>>,
41 const HEADER_SIZE
: u64 = std
::mem
::size_of
::<PxarHeader
>() as u64;
43 impl<R
: Read
> SequentialDecoder
<R
> {
49 let skip_buffer
= vec
::undefined(64 * 1024);
54 allow_existing_dirs
: false,
60 pub fn set_callback
<F
: Fn(&Path
) -> Result
<(), Error
> + Send
+ '
static>(&mut self, callback
: F
) {
61 self.callback
= Some(Box
::new(callback
));
64 pub fn set_allow_existing_dirs(&mut self, allow
: bool
) {
65 self.allow_existing_dirs
= allow
;
68 pub(crate) fn get_reader_mut(&mut self) -> &mut R
{
72 pub(crate) fn read_item
<T
: Endian
>(&mut self) -> Result
<T
, Error
> {
73 let mut result
= std
::mem
::MaybeUninit
::<T
>::uninit();
76 std
::slice
::from_raw_parts_mut(result
.as_mut_ptr() as *mut u8, std
::mem
::size_of
::<T
>())
79 self.reader
.read_exact(buffer
)?
;
80 let result
= unsafe { result.assume_init() }
;
85 pub(crate) fn read_link(&mut self, size
: u64) -> Result
<PathBuf
, Error
> {
86 if size
< (HEADER_SIZE
+ 2) {
87 bail
!("dectected short link target.");
89 let target_len
= size
- HEADER_SIZE
;
91 if target_len
> (libc
::PATH_MAX
as u64) {
92 bail
!("link target too long ({}).", target_len
);
95 let mut buffer
= self.reader
.read_exact_allocated(target_len
as usize)?
;
97 let last_byte
= buffer
.pop().unwrap();
99 bail
!("link target not nul terminated.");
102 Ok(PathBuf
::from(std
::ffi
::OsString
::from_vec(buffer
)))
105 pub(crate) fn read_hardlink(&mut self, size
: u64) -> Result
<(PathBuf
, u64), Error
> {
106 if size
< (HEADER_SIZE
+ 8 + 2) {
107 bail
!("dectected short hardlink header.");
109 let offset
: u64 = self.read_item()?
;
110 let target
= self.read_link(size
- 8)?
;
112 for c
in target
.components() {
114 std
::path
::Component
::Normal(_
) => { /* OK */ }
115 _
=> bail
!("hardlink target contains invalid component {:?}", c
),
122 pub(crate) fn read_filename(&mut self, size
: u64) -> Result
<OsString
, Error
> {
123 if size
< (HEADER_SIZE
+ 2) {
124 bail
!("dectected short filename");
126 let name_len
= size
- HEADER_SIZE
;
128 if name_len
> ((libc
::FILENAME_MAX
as u64) + 1) {
129 bail
!("filename too long ({}).", name_len
);
132 let mut buffer
= self.reader
.read_exact_allocated(name_len
as usize)?
;
134 let last_byte
= buffer
.pop().unwrap();
135 if last_byte
!= 0u8 {
136 bail
!("filename entry not nul terminated.");
139 if buffer
== b
"." || buffer
== b
".." {
140 bail
!("found invalid filename '.' or '..'.");
143 if buffer
.iter().any(|b
| (*b
== b'
/'
|| *b
== b'
\0'
)) {
144 bail
!("found invalid filename with slashes or nul bytes.");
147 let name
= std
::ffi
::OsString
::from_vec(buffer
);
149 bail
!("found empty filename.");
155 fn has_features(&self, feature_flags
: u64) -> bool
{
156 (self.feature_flags
& feature_flags
) == feature_flags
159 fn read_xattr(&mut self, size
: usize) -> Result
<PxarXAttr
, Error
> {
160 let buffer
= self.reader
.read_exact_allocated(size
)?
;
162 let separator
= buffer
164 .position(|c
| *c
== b'
\0'
)
165 .ok_or_else(|| format_err
!("no value found in xattr"))?
;
167 let (name
, value
) = buffer
.split_at(separator
);
168 if !xattr
::is_valid_xattr_name(name
) || xattr
::is_security_capability(name
) {
169 bail
!("incorrect xattr name - {}.", String
::from_utf8_lossy(name
));
174 value
: value
[1..].to_vec(),
178 fn read_fcaps(&mut self, size
: usize) -> Result
<PxarFCaps
, Error
> {
179 let buffer
= self.reader
.read_exact_allocated(size
)?
;
181 Ok(PxarFCaps { data: buffer }
)
184 pub(crate) fn read_attributes(&mut self) -> Result
<(PxarHeader
, PxarAttributes
), Error
> {
185 let mut attr
= PxarAttributes
::default();
186 let mut head
: PxarHeader
= self.read_item()?
;
187 let mut size
= (head
.size
- HEADER_SIZE
) as usize;
191 if self.has_features(flags
::WITH_XATTRS
) {
192 attr
.xattrs
.push(self.read_xattr(size
)?
);
194 self.skip_bytes(size
)?
;
198 if self.has_features(flags
::WITH_FCAPS
) {
199 attr
.fcaps
= Some(self.read_fcaps(size
)?
);
201 self.skip_bytes(size
)?
;
205 if self.has_features(flags
::WITH_ACL
) {
206 attr
.acl_user
.push(self.read_item
::<PxarACLUser
>()?
);
208 self.skip_bytes(size
)?
;
212 if self.has_features(flags
::WITH_ACL
) {
213 attr
.acl_group
.push(self.read_item
::<PxarACLGroup
>()?
);
215 self.skip_bytes(size
)?
;
218 PXAR_ACL_GROUP_OBJ
=> {
219 if self.has_features(flags
::WITH_ACL
) {
220 attr
.acl_group_obj
= Some(self.read_item
::<PxarACLGroupObj
>()?
);
222 self.skip_bytes(size
)?
;
225 PXAR_ACL_DEFAULT
=> {
226 if self.has_features(flags
::WITH_ACL
) {
227 attr
.acl_default
= Some(self.read_item
::<PxarACLDefault
>()?
);
229 self.skip_bytes(size
)?
;
232 PXAR_ACL_DEFAULT_USER
=> {
233 if self.has_features(flags
::WITH_ACL
) {
234 attr
.acl_default_user
.push(self.read_item
::<PxarACLUser
>()?
);
236 self.skip_bytes(size
)?
;
239 PXAR_ACL_DEFAULT_GROUP
=> {
240 if self.has_features(flags
::WITH_ACL
) {
241 attr
.acl_default_group
242 .push(self.read_item
::<PxarACLGroup
>()?
);
244 self.skip_bytes(size
)?
;
247 PXAR_QUOTA_PROJID
=> {
248 if self.has_features(flags
::WITH_QUOTA_PROJID
) {
249 attr
.quota_projid
= Some(self.read_item
::<PxarQuotaProjID
>()?
);
251 self.skip_bytes(size
)?
;
256 head
= self.read_item()?
;
257 size
= (head
.size
- HEADER_SIZE
) as usize;
263 fn restore_attributes(
266 attr
: &PxarAttributes
,
268 ) -> Result
<(), Error
> {
269 self.restore_xattrs_fcaps_fd(fd
, &attr
.xattrs
, &attr
.fcaps
)?
;
271 let mut acl
= acl
::ACL
::init(5)?
;
275 acl
::mode_user_to_acl_permissions(entry
.mode
),
280 acl
::mode_other_to_acl_permissions(entry
.mode
),
282 match &attr
.acl_group_obj
{
287 acl
::mode_group_to_acl_permissions(entry
.mode
),
289 acl
.add_entry_full(acl
::ACL_GROUP_OBJ
, None
, group_obj
.permissions
)?
;
295 acl
::mode_group_to_acl_permissions(entry
.mode
),
299 for user
in &attr
.acl_user
{
300 acl
.add_entry_full(acl
::ACL_USER
, Some(user
.uid
), user
.permissions
)?
;
302 for group
in &attr
.acl_group
{
303 acl
.add_entry_full(acl
::ACL_GROUP
, Some(group
.gid
), group
.permissions
)?
;
305 let proc_path
= Path
::new("/proc/self/fd/").join(fd
.to_string());
307 bail
!("Error while restoring ACL - ACL invalid");
309 acl
.set_file(&proc_path
, acl
::ACL_TYPE_ACCESS
)?
;
311 if let Some(default) = &attr
.acl_default
{
312 let mut acl
= acl
::ACL
::init(5)?
;
313 acl
.add_entry_full(acl
::ACL_USER_OBJ
, None
, default.user_obj_permissions
)?
;
314 acl
.add_entry_full(acl
::ACL_GROUP_OBJ
, None
, default.group_obj_permissions
)?
;
315 acl
.add_entry_full(acl
::ACL_OTHER
, None
, default.other_permissions
)?
;
316 if default.mask_permissions
!= std
::u64::MAX
{
317 acl
.add_entry_full(acl
::ACL_MASK
, None
, default.mask_permissions
)?
;
319 for user
in &attr
.acl_default_user
{
320 acl
.add_entry_full(acl
::ACL_USER
, Some(user
.uid
), user
.permissions
)?
;
322 for group
in &attr
.acl_default_group
{
323 acl
.add_entry_full(acl
::ACL_GROUP
, Some(group
.gid
), group
.permissions
)?
;
326 bail
!("Error while restoring ACL - ACL invalid");
328 acl
.set_file(&proc_path
, acl
::ACL_TYPE_DEFAULT
)?
;
330 self.restore_quota_projid(fd
, &attr
.quota_projid
)?
;
335 // Restore xattrs and fcaps to the given RawFd.
336 fn restore_xattrs_fcaps_fd(
339 xattrs
: &[PxarXAttr
],
340 fcaps
: &Option
<PxarFCaps
>,
341 ) -> Result
<(), Error
> {
342 for xattr
in xattrs
{
343 if let Err(err
) = xattr
::fsetxattr(fd
, &xattr
) {
344 bail
!("fsetxattr failed with error: {}", err
);
347 if let Some(fcaps
) = fcaps
{
348 if let Err(err
) = xattr
::fsetxattr_fcaps(fd
, &fcaps
) {
349 bail
!("fsetxattr_fcaps failed with error: {}", err
);
356 fn restore_quota_projid(
359 projid
: &Option
<PxarQuotaProjID
>,
360 ) -> Result
<(), Error
> {
361 if let Some(projid
) = projid
{
362 let mut fsxattr
= fs
::FSXAttr
::default();
364 fs
::fs_ioc_fsgetxattr(fd
, &mut fsxattr
).map_err(|err
| {
366 "error while getting fsxattr to restore quota project id - {}",
371 fsxattr
.fsx_projid
= projid
.projid
as u32;
373 fs
::fs_ioc_fssetxattr(fd
, &fsxattr
).map_err(|err
| {
375 "error while setting fsxattr to restore quota project id - {}",
385 fn restore_mode(&mut self, entry
: &PxarEntry
, fd
: RawFd
) -> Result
<(), Error
> {
386 let mode
= Mode
::from_bits_truncate((entry
.mode
as u32) & 0o7777);
388 nix
::sys
::stat
::fchmod(fd
, mode
)?
;
398 ) -> Result
<(), Error
> {
399 let mode
= Mode
::from_bits_truncate((entry
.mode
as u32) & 0o7777);
401 // NOTE: we want :FchmodatFlags::NoFollowSymlink, but fchmodat does not support that
402 // on linux (see man fchmodat). Fortunately, we can simply avoid calling this on symlinks.
403 nix
::sys
::stat
::fchmodat(
407 nix
::sys
::stat
::FchmodatFlags
::FollowSymlink
,
413 fn restore_ugid(&mut self, entry
: &PxarEntry
, fd
: RawFd
) -> Result
<(), Error
> {
417 let res
= unsafe { libc::fchown(fd, uid, gid) }
;
428 ) -> Result
<(), Error
> {
432 let res
= filename
.with_nix_path(|cstr
| unsafe {
433 libc
::fchownat(dirfd
, cstr
.as_ptr(), uid
, gid
, libc
::AT_SYMLINK_NOFOLLOW
)
440 fn restore_mtime(&mut self, entry
: &PxarEntry
, fd
: RawFd
) -> Result
<(), Error
> {
441 let times
= nsec_to_update_timespec(entry
.mtime
);
443 let res
= unsafe { libc::futimens(fd, ×[0]) }
;
454 ) -> Result
<(), Error
> {
455 let times
= nsec_to_update_timespec(entry
.mtime
);
457 let res
= filename
.with_nix_path(|cstr
| unsafe {
458 libc
::utimensat(dirfd
, cstr
.as_ptr(), ×
[0], libc
::AT_SYMLINK_NOFOLLOW
)
465 fn restore_device_at(
471 ) -> Result
<(), Error
> {
472 let rdev
= nix
::sys
::stat
::makedev(device
.major
, device
.minor
);
473 let mode
= ((entry
.mode
as u32) & libc
::S_IFMT
) | 0o0600;
475 .with_nix_path(|cstr
| unsafe { libc::mknodat(dirfd, cstr.as_ptr(), mode, rdev) }
)?
;
481 fn restore_socket_at(&mut self, dirfd
: RawFd
, filename
: &OsStr
) -> Result
<(), Error
> {
482 let mode
= libc
::S_IFSOCK
| 0o0600;
484 .with_nix_path(|cstr
| unsafe { libc::mknodat(dirfd, cstr.as_ptr(), mode, 0) }
)?
;
490 fn restore_fifo_at(&mut self, dirfd
: RawFd
, filename
: &OsStr
) -> Result
<(), Error
> {
491 let mode
= libc
::S_IFIFO
| 0o0600;
493 filename
.with_nix_path(|cstr
| unsafe { libc::mkfifoat(dirfd, cstr.as_ptr(), mode) }
)?
;
499 pub(crate) fn skip_bytes(&mut self, count
: usize) -> Result
<(), Error
> {
502 let todo
= count
- done
;
503 let n
= if todo
> self.skip_buffer
.len() {
504 self.skip_buffer
.len()
508 let data
= &mut self.skip_buffer
[..n
];
509 self.reader
.read_exact(data
)?
;
517 parent_fd
: Option
<RawFd
>,
521 ) -> Result
<(), Error
> {
522 //fixme: create symlink
523 //fixme: restore permission, acls, xattr, ...
525 let head
: PxarHeader
= self.read_item()?
;
528 let target
= self.read_link(head
.size
)?
;
529 //println!("TARGET: {:?}", target);
530 if let Some(fd
) = parent_fd
{
531 if let Err(err
) = symlinkat(&target
, fd
, filename
) {
532 bail
!("create symlink {:?} failed - {}", full_path
, err
);
537 "got unknown header type inside symlink entry {:016x}",
542 if let Some(fd
) = parent_fd
{
543 // self.restore_mode_at(&entry, fd, filename)?; //not supported on symlinks
544 self.restore_ugid_at(&entry
, fd
, filename
)?
;
545 self.restore_mtime_at(&entry
, fd
, filename
)?
;
553 parent_fd
: Option
<RawFd
>,
556 ) -> Result
<(), Error
> {
557 if !self.has_features(flags
::WITH_SOCKETS
) {
560 if let Some(fd
) = parent_fd
{
561 self.restore_socket_at(fd
, filename
)?
;
562 self.restore_mode_at(&entry
, fd
, filename
)?
;
563 self.restore_ugid_at(&entry
, fd
, filename
)?
;
564 self.restore_mtime_at(&entry
, fd
, filename
)?
;
572 parent_fd
: Option
<RawFd
>,
575 ) -> Result
<(), Error
> {
576 if !self.has_features(flags
::WITH_FIFOS
) {
579 if let Some(fd
) = parent_fd
{
580 self.restore_fifo_at(fd
, filename
)?
;
581 self.restore_mode_at(&entry
, fd
, filename
)?
;
582 self.restore_ugid_at(&entry
, fd
, filename
)?
;
583 self.restore_mtime_at(&entry
, fd
, filename
)?
;
591 parent_fd
: Option
<RawFd
>,
594 ) -> Result
<(), Error
> {
595 let head
: PxarHeader
= self.read_item()?
;
596 if head
.htype
!= PXAR_DEVICE
{
598 "got unknown header type inside device entry {:016x}",
602 let device
: PxarDevice
= self.read_item()?
;
603 if !self.has_features(flags
::WITH_DEVICE_NODES
) {
606 if let Some(fd
) = parent_fd
{
607 self.restore_device_at(&entry
, fd
, filename
, &device
)?
;
608 self.restore_mode_at(&entry
, fd
, filename
)?
;
609 self.restore_ugid_at(&entry
, fd
, filename
)?
;
610 self.restore_mtime_at(&entry
, fd
, filename
)?
;
616 /// Restores a regular file with its content and associated attributes to the
617 /// folder provided by the raw filedescriptor.
618 /// If None is passed instead of a filedescriptor, the file is not restored but
619 /// the archive reader is skipping over it instead.
620 fn restore_regular_file(
622 parent_fd
: Option
<RawFd
>,
626 ) -> Result
<(), Error
> {
627 let (head
, attr
) = self
629 .map_err(|err
| format_err
!("Reading of file attributes failed - {}", err
))?
;
631 if let Some(fd
) = parent_fd
{
632 let flags
= OFlag
::O_CREAT
| OFlag
::O_WRONLY
| OFlag
::O_EXCL
;
633 let open_mode
= Mode
::from_bits_truncate(0o0600 | entry
.mode
as u32); //fixme: upper 32bits of entry.mode?
634 let mut file
= file_openat(fd
, filename
, flags
, open_mode
)
635 .map_err(|err
| format_err
!("open file {:?} failed - {}", full_path
, err
))?
;
637 if head
.htype
!= PXAR_PAYLOAD
{
638 bail
!("got unknown header type for file entry {:016x}", head
.htype
);
641 if head
.size
< HEADER_SIZE
{
642 bail
!("detected short payload");
644 let need
= (head
.size
- HEADER_SIZE
) as usize;
646 let mut read_buffer
= unsafe { vec::uninitialized(64 * 1024) }
;
649 let todo
= need
- done
;
650 let n
= if todo
> read_buffer
.len() {
655 let data
= &mut read_buffer
[..n
];
656 self.reader
.read_exact(data
)?
;
657 file
.write_all(data
)?
;
661 self.restore_ugid(&entry
, file
.as_raw_fd())?
;
662 // fcaps have to be restored after restore_ugid as chown clears security.capability xattr, see CVE-2015-1350
663 self.restore_attributes(file
.as_raw_fd(), &attr
, &entry
)?
;
664 self.restore_mode(&entry
, file
.as_raw_fd())?
;
665 self.restore_mtime(&entry
, file
.as_raw_fd())?
;
667 if head
.htype
!= PXAR_PAYLOAD
{
668 bail
!("got unknown header type for file entry {:016x}", head
.htype
);
670 if head
.size
< HEADER_SIZE
{
671 bail
!("detected short payload");
673 self.skip_bytes((head
.size
- HEADER_SIZE
) as usize)?
;
682 dirs
: &mut PxarDirStack
,
686 match_pattern
: &[MatchPatternSlice
],
687 ) -> Result
<(), Error
> {
688 let (mut head
, attr
) = self
690 .map_err(|err
| format_err
!("Reading of directory attributes failed - {}", err
))?
;
692 let dir
= PxarDir
::new(filename
, entry
, attr
);
694 if matched
== MatchType
::Positive
{
695 dirs
.create_all_dirs(!self.allow_existing_dirs
)?
;
698 while head
.htype
== PXAR_FILENAME
{
699 let name
= self.read_filename(head
.size
)?
;
700 self.restore_dir_entry(base_path
, dirs
, &name
, matched
, match_pattern
)?
;
701 head
= self.read_item()?
;
704 if head
.htype
!= PXAR_GOODBYE
{
706 "got unknown header type inside directory entry {:016x}",
711 if head
.size
< HEADER_SIZE
{
712 bail
!("detected short goodbye table");
714 self.skip_bytes((head
.size
- HEADER_SIZE
) as usize)?
;
718 .ok_or_else(|| format_err
!("Tried to pop beyond dir root - this should not happen!"))?
;
719 if let Some(d
) = last
.dir
{
720 let fd
= d
.as_raw_fd();
721 self.restore_ugid(&last
.entry
, fd
)?
;
722 // fcaps have to be restored after restore_ugid as chown clears security.capability xattr, see CVE-2015-1350
723 self.restore_attributes(fd
, &last
.attr
, &last
.entry
)?
;
724 self.restore_mode(&last
.entry
, fd
)?
;
725 self.restore_mtime(&last
.entry
, fd
)?
;
731 /// Restore an archive into the specified directory.
733 /// The directory is created if it does not exist.
734 pub fn restore(&mut self, path
: &Path
, match_pattern
: &[MatchPattern
]) -> Result
<(), Error
> {
735 let mut slices
= Vec
::new();
736 for pattern
in match_pattern
{
737 slices
.push(pattern
.as_slice());
739 std
::fs
::create_dir_all(path
)
740 .map_err(|err
| format_err
!("error while creating directory {:?} - {}", path
, err
))?
;
742 let dir
= nix
::dir
::Dir
::open(
744 nix
::fcntl
::OFlag
::O_DIRECTORY
,
745 nix
::sys
::stat
::Mode
::empty(),
747 .map_err(|err
| format_err
!("unable to open target directory {:?} - {}", path
, err
))?
;
748 let fd
= dir
.as_raw_fd();
749 let mut dirs
= PxarDirStack
::new(fd
);
750 // An empty match pattern list indicates to restore the full archive.
751 let matched
= if slices
.is_empty() {
757 let header
: PxarHeader
= self.read_item()?
;
758 check_ca_header
::<PxarEntry
>(&header
, PXAR_ENTRY
)?
;
759 let entry
: PxarEntry
= self.read_item()?
;
761 let (mut head
, attr
) = self
763 .map_err(|err
| format_err
!("Reading of directory attributes failed - {}", err
))?
;
765 while head
.htype
== PXAR_FILENAME
{
766 let name
= self.read_filename(head
.size
)?
;
767 self.restore_dir_entry(path
, &mut dirs
, &name
, matched
, &slices
)?
;
768 head
= self.read_item()?
;
771 if head
.htype
!= PXAR_GOODBYE
{
773 "got unknown header type inside directory entry {:016x}",
778 if head
.size
< HEADER_SIZE
{
779 bail
!("detected short goodbye table");
781 self.skip_bytes((head
.size
- HEADER_SIZE
) as usize)?
;
783 self.restore_ugid(&entry
, fd
)?
;
784 // fcaps have to be restored after restore_ugid as chown clears security.capability xattr, see CVE-2015-1350
785 self.restore_attributes(fd
, &attr
, &entry
)?
;
786 self.restore_mode(&entry
, fd
)?
;
787 self.restore_mtime(&entry
, fd
)?
;
792 fn restore_dir_entry(
795 dirs
: &mut PxarDirStack
,
797 parent_matched
: MatchType
,
798 match_pattern
: &[MatchPatternSlice
],
799 ) -> Result
<(), Error
> {
800 let relative_path
= dirs
.as_path_buf();
801 let full_path
= base_path
.join(&relative_path
).join(filename
);
803 let head
: PxarHeader
= self.read_item()?
;
804 if head
.htype
== PXAR_FORMAT_HARDLINK
{
805 let (target
, _offset
) = self.read_hardlink(head
.size
)?
;
806 let target_path
= base_path
.join(&target
);
807 if dirs
.last_dir_fd().is_some() {
808 if let Some(ref callback
) = self.callback
{
809 (callback
)(&full_path
)?
;
811 hardlink(&target_path
, &full_path
)?
;
816 check_ca_header
::<PxarEntry
>(&head
, PXAR_ENTRY
)?
;
817 let entry
: PxarEntry
= self.read_item()?
;
818 let ifmt
= entry
.mode
as u32 & libc
::S_IFMT
;
820 let mut child_pattern
= Vec
::new();
821 // If parent was a match, then children should be assumed to match too
822 // This is especially the case when the full archive is restored and
823 // there are no match pattern.
824 let mut matched
= parent_matched
;
825 if !match_pattern
.is_empty() {
826 match MatchPatternSlice
::match_filename_include(
827 &CString
::new(filename
.as_bytes())?
,
828 ifmt
== libc
::S_IFDIR
,
831 (MatchType
::None
, _
) => matched
= MatchType
::None
,
832 (MatchType
::Negative
, _
) => matched
= MatchType
::Negative
,
833 (MatchType
::Positive
, _
) => matched
= MatchType
::Positive
,
834 (match_type
, pattern
) => {
835 matched
= match_type
;
836 child_pattern
= pattern
;
841 let fd
= if matched
== MatchType
::Positive
{
842 Some(dirs
.create_all_dirs(!self.allow_existing_dirs
)?
)
848 if let Some(ref callback
) = self.callback
{
849 (callback
)(&full_path
)?
;
855 self.restore_dir(base_path
, dirs
, entry
, &filename
, matched
, &child_pattern
)
857 libc
::S_IFLNK
=> self.restore_symlink(fd
, &full_path
, &entry
, &filename
),
858 libc
::S_IFSOCK
=> self.restore_socket(fd
, &entry
, &filename
),
859 libc
::S_IFIFO
=> self.restore_fifo(fd
, &entry
, &filename
),
860 libc
::S_IFBLK
| libc
::S_IFCHR
=> self.restore_device(fd
, &entry
, &filename
),
861 libc
::S_IFREG
=> self.restore_regular_file(fd
, &full_path
, &entry
, &filename
),
866 /// List/Dump archive content.
868 /// Simply print the list of contained files. This dumps archive
869 /// format details when the verbose flag is set (useful for debug).
870 pub fn dump_entry
<W
: std
::io
::Write
>(
875 ) -> Result
<(), Error
> {
876 let print_head
= |head
: &PxarHeader
| {
877 println
!("Type: {:016x}", head
.htype
);
878 println
!("Size: {}", head
.size
);
881 let head
: PxarHeader
= self.read_item()?
;
883 println
!("Path: {:?}", path
);
886 println
!("{:?}", path
);
889 if head
.htype
== PXAR_FORMAT_HARDLINK
{
890 let (target
, offset
) = self.read_hardlink(head
.size
)?
;
892 println
!("Hardlink: {} {:?}", offset
, target
);
897 check_ca_header
::<PxarEntry
>(&head
, PXAR_ENTRY
)?
;
898 let entry
: PxarEntry
= self.read_item()?
;
902 "Mode: {:08x} {:08x}",
904 (entry
.mode
as u32) & libc
::S_IFDIR
908 let ifmt
= (entry
.mode
as u32) & libc
::S_IFMT
;
910 if ifmt
== libc
::S_IFDIR
{
911 let mut entry_count
= 0;
914 let head
: PxarHeader
= self.read_item()?
;
919 // This call covers all the cases of the match statement
920 // regarding extended attributes. These calls will never
921 // break on the loop and can therefore be handled separately.
922 // If the header was matched, true is returned and we can continue
923 if self.dump_if_attribute(&head
, verbose
)?
{
929 let name
= self.read_filename(head
.size
)?
;
931 println
!("Name: {:?}", name
);
935 self.dump_entry(path
, verbose
, output
)?
;
939 let table_size
= (head
.size
- HEADER_SIZE
) as usize;
941 println
!("Goodbye: {:?}", path
);
942 self.dump_goodby_entries(entry_count
, table_size
)?
;
944 self.skip_bytes(table_size
)?
;
948 _
=> panic
!("got unexpected header type inside directory"),
951 } else if (ifmt
== libc
::S_IFBLK
)
952 || (ifmt
== libc
::S_IFCHR
)
953 || (ifmt
== libc
::S_IFLNK
)
954 || (ifmt
== libc
::S_IFREG
)
957 let head
: PxarHeader
= self.read_item()?
;
962 // This call covers all the cases of the match statement
963 // regarding extended attributes. These calls will never
964 // break on the loop and can therefore be handled separately.
965 // If the header was matched, true is returned and we can continue
966 if self.dump_if_attribute(&head
, verbose
)?
{
972 let target
= self.read_link(head
.size
)?
;
974 println
!("Symlink: {:?}", target
);
979 let device
: PxarDevice
= self.read_item()?
;
981 println
!("Device: {}, {}", device
.major
, device
.minor
);
986 let payload_size
= (head
.size
- HEADER_SIZE
) as usize;
988 println
!("Payload: {}", payload_size
);
990 self.skip_bytes(payload_size
)?
;
994 panic
!("got unexpected header type inside non-directory");
998 } else if ifmt
== libc
::S_IFIFO
{
1002 } else if ifmt
== libc
::S_IFSOCK
{
1004 println
!("Socket:");
1007 panic
!("unknown st_mode");
1012 fn dump_if_attribute(&mut self, header
: &PxarHeader
, verbose
: bool
) -> Result
<bool
, Error
> {
1013 match header
.htype
{
1015 let xattr
= self.read_xattr((header
.size
- HEADER_SIZE
) as usize)?
;
1016 if verbose
&& self.has_features(flags
::WITH_XATTRS
) {
1017 println
!("XAttr: {:?}", xattr
);
1021 let fcaps
= self.read_fcaps((header
.size
- HEADER_SIZE
) as usize)?
;
1022 if verbose
&& self.has_features(flags
::WITH_FCAPS
) {
1023 println
!("FCaps: {:?}", fcaps
);
1027 let user
= self.read_item
::<PxarACLUser
>()?
;
1028 if verbose
&& self.has_features(flags
::WITH_ACL
) {
1029 println
!("ACLUser: {:?}", user
);
1033 let group
= self.read_item
::<PxarACLGroup
>()?
;
1034 if verbose
&& self.has_features(flags
::WITH_ACL
) {
1035 println
!("ACLGroup: {:?}", group
);
1038 PXAR_ACL_GROUP_OBJ
=> {
1039 let group_obj
= self.read_item
::<PxarACLGroupObj
>()?
;
1040 if verbose
&& self.has_features(flags
::WITH_ACL
) {
1041 println
!("ACLGroupObj: {:?}", group_obj
);
1044 PXAR_ACL_DEFAULT
=> {
1045 let default = self.read_item
::<PxarACLDefault
>()?
;
1046 if verbose
&& self.has_features(flags
::WITH_ACL
) {
1047 println
!("ACLDefault: {:?}", default);
1050 PXAR_ACL_DEFAULT_USER
=> {
1051 let default_user
= self.read_item
::<PxarACLUser
>()?
;
1052 if verbose
&& self.has_features(flags
::WITH_ACL
) {
1053 println
!("ACLDefaultUser: {:?}", default_user
);
1056 PXAR_ACL_DEFAULT_GROUP
=> {
1057 let default_group
= self.read_item
::<PxarACLGroup
>()?
;
1058 if verbose
&& self.has_features(flags
::WITH_ACL
) {
1059 println
!("ACLDefaultGroup: {:?}", default_group
);
1062 PXAR_QUOTA_PROJID
=> {
1063 let quota_projid
= self.read_item
::<PxarQuotaProjID
>()?
;
1064 if verbose
&& self.has_features(flags
::WITH_QUOTA_PROJID
) {
1065 println
!("Quota project id: {:?}", quota_projid
);
1068 _
=> return Ok(false),
1074 fn dump_goodby_entries(&mut self, entry_count
: usize, table_size
: usize) -> Result
<(), Error
> {
1075 const GOODBYE_ITEM_SIZE
: usize = std
::mem
::size_of
::<PxarGoodbyeItem
>();
1077 if table_size
< GOODBYE_ITEM_SIZE
{
1079 "Goodbye table to small ({} < {})",
1084 if (table_size
% GOODBYE_ITEM_SIZE
) != 0 {
1085 bail
!("Goodbye table with strange size ({})", table_size
);
1088 let entries
= table_size
/ GOODBYE_ITEM_SIZE
;
1090 if entry_count
!= (entries
- 1) {
1092 "Goodbye table with wrong entry count ({} != {})",
1101 let item
: PxarGoodbyeItem
= self.read_item()?
;
1103 if item
.hash
== PXAR_GOODBYE_TAIL_MARKER
{
1104 if count
!= entries
{
1105 bail
!("unexpected goodbye tail marker");
1107 println
!("Goodby tail mark.");
1111 "Goodby item: offset {}, size {}, hash {:016x}",
1112 item
.offset
, item
.size
, item
.hash
1114 if count
>= entries
{
1115 bail
!("too many goodbye items (no tail marker)");
1128 ) -> Result
<std
::fs
::File
, Error
> {
1130 filename
.with_nix_path(|cstr
| nix
::fcntl
::openat(parent
, cstr
, flags
, mode
))??
;
1132 let file
= unsafe { std::fs::File::from_raw_fd(fd) }
;
1137 fn hardlink(oldpath
: &Path
, newpath
: &Path
) -> Result
<(), Error
> {
1138 oldpath
.with_nix_path(|oldpath
| {
1139 newpath
.with_nix_path(|newpath
| {
1140 let res
= unsafe { libc::link(oldpath.as_ptr(), newpath.as_ptr()) }
;
1141 Errno
::result(res
)?
;
1147 fn symlinkat(target
: &Path
, parent
: RawFd
, linkname
: &OsStr
) -> Result
<(), Error
> {
1148 target
.with_nix_path(|target
| {
1149 linkname
.with_nix_path(|linkname
| {
1150 let res
= unsafe { libc::symlinkat(target.as_ptr(), parent, linkname.as_ptr()) }
;
1151 Errno
::result(res
)?
;
1157 fn nsec_to_update_timespec(mtime_nsec
: u64) -> [libc
::timespec
; 2] {
1159 const UTIME_OMIT
: i64 = ((1 << 30) - 2);
1160 const NANOS_PER_SEC
: i64 = 1_000_000_000;
1162 let sec
= (mtime_nsec
as i64) / NANOS_PER_SEC
;
1163 let nsec
= (mtime_nsec
as i64) % NANOS_PER_SEC
;
1165 let times
: [libc
::timespec
; 2] = [
1168 tv_nsec
: UTIME_OMIT
,