1 //! *pxar* format decoder.
3 //! This module contain the code to decode *pxar* archive files.
4 use std
::ffi
::{CStr, CString}
;
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
!("detected 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
!("detected 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
!("detected 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
+ 1);
168 let c_name
= unsafe { CStr::from_bytes_with_nul_unchecked(name) }
;
169 if !xattr
::is_valid_xattr_name(c_name
) || xattr
::is_security_capability(c_name
) {
170 bail
!("incorrect xattr name - {:?}.", c_name
);
175 value
: value
[1..].to_vec(),
179 fn read_fcaps(&mut self, size
: usize) -> Result
<PxarFCaps
, Error
> {
180 let buffer
= self.reader
.read_exact_allocated(size
)?
;
182 Ok(PxarFCaps { data: buffer }
)
185 pub(crate) fn read_attributes(&mut self) -> Result
<(PxarHeader
, PxarAttributes
), Error
> {
186 let mut attr
= PxarAttributes
::default();
187 let mut head
: PxarHeader
= self.read_item()?
;
188 let mut size
= (head
.size
- HEADER_SIZE
) as usize;
192 if self.has_features(flags
::WITH_XATTRS
) {
193 attr
.xattrs
.push(self.read_xattr(size
)?
);
195 self.skip_bytes(size
)?
;
199 if self.has_features(flags
::WITH_FCAPS
) {
200 attr
.fcaps
= Some(self.read_fcaps(size
)?
);
202 self.skip_bytes(size
)?
;
206 if self.has_features(flags
::WITH_ACL
) {
207 attr
.acl_user
.push(self.read_item
::<PxarACLUser
>()?
);
209 self.skip_bytes(size
)?
;
213 if self.has_features(flags
::WITH_ACL
) {
214 attr
.acl_group
.push(self.read_item
::<PxarACLGroup
>()?
);
216 self.skip_bytes(size
)?
;
219 PXAR_ACL_GROUP_OBJ
=> {
220 if self.has_features(flags
::WITH_ACL
) {
221 attr
.acl_group_obj
= Some(self.read_item
::<PxarACLGroupObj
>()?
);
223 self.skip_bytes(size
)?
;
226 PXAR_ACL_DEFAULT
=> {
227 if self.has_features(flags
::WITH_ACL
) {
228 attr
.acl_default
= Some(self.read_item
::<PxarACLDefault
>()?
);
230 self.skip_bytes(size
)?
;
233 PXAR_ACL_DEFAULT_USER
=> {
234 if self.has_features(flags
::WITH_ACL
) {
235 attr
.acl_default_user
.push(self.read_item
::<PxarACLUser
>()?
);
237 self.skip_bytes(size
)?
;
240 PXAR_ACL_DEFAULT_GROUP
=> {
241 if self.has_features(flags
::WITH_ACL
) {
242 attr
.acl_default_group
243 .push(self.read_item
::<PxarACLGroup
>()?
);
245 self.skip_bytes(size
)?
;
248 PXAR_QUOTA_PROJID
=> {
249 if self.has_features(flags
::WITH_QUOTA_PROJID
) {
250 attr
.quota_projid
= Some(self.read_item
::<PxarQuotaProjID
>()?
);
252 self.skip_bytes(size
)?
;
257 head
= self.read_item()?
;
258 size
= (head
.size
- HEADER_SIZE
) as usize;
264 fn restore_attributes(
267 attr
: &PxarAttributes
,
269 ) -> Result
<(), Error
> {
270 self.restore_xattrs_fcaps_fd(fd
, &attr
.xattrs
, &attr
.fcaps
)?
;
272 let mut acl
= acl
::ACL
::init(5)?
;
276 acl
::mode_user_to_acl_permissions(entry
.mode
),
281 acl
::mode_other_to_acl_permissions(entry
.mode
),
283 match &attr
.acl_group_obj
{
288 acl
::mode_group_to_acl_permissions(entry
.mode
),
290 acl
.add_entry_full(acl
::ACL_GROUP_OBJ
, None
, group_obj
.permissions
)?
;
296 acl
::mode_group_to_acl_permissions(entry
.mode
),
300 for user
in &attr
.acl_user
{
301 acl
.add_entry_full(acl
::ACL_USER
, Some(user
.uid
), user
.permissions
)?
;
303 for group
in &attr
.acl_group
{
304 acl
.add_entry_full(acl
::ACL_GROUP
, Some(group
.gid
), group
.permissions
)?
;
306 let proc_path
= Path
::new("/proc/self/fd/").join(fd
.to_string());
308 bail
!("Error while restoring ACL - ACL invalid");
310 acl
.set_file(&proc_path
, acl
::ACL_TYPE_ACCESS
)?
;
312 if let Some(default) = &attr
.acl_default
{
313 let mut acl
= acl
::ACL
::init(5)?
;
314 acl
.add_entry_full(acl
::ACL_USER_OBJ
, None
, default.user_obj_permissions
)?
;
315 acl
.add_entry_full(acl
::ACL_GROUP_OBJ
, None
, default.group_obj_permissions
)?
;
316 acl
.add_entry_full(acl
::ACL_OTHER
, None
, default.other_permissions
)?
;
317 if default.mask_permissions
!= std
::u64::MAX
{
318 acl
.add_entry_full(acl
::ACL_MASK
, None
, default.mask_permissions
)?
;
320 for user
in &attr
.acl_default_user
{
321 acl
.add_entry_full(acl
::ACL_USER
, Some(user
.uid
), user
.permissions
)?
;
323 for group
in &attr
.acl_default_group
{
324 acl
.add_entry_full(acl
::ACL_GROUP
, Some(group
.gid
), group
.permissions
)?
;
327 bail
!("Error while restoring ACL - ACL invalid");
329 acl
.set_file(&proc_path
, acl
::ACL_TYPE_DEFAULT
)?
;
331 self.restore_quota_projid(fd
, &attr
.quota_projid
)?
;
336 // Restore xattrs and fcaps to the given RawFd.
337 fn restore_xattrs_fcaps_fd(
340 xattrs
: &[PxarXAttr
],
341 fcaps
: &Option
<PxarFCaps
>,
342 ) -> Result
<(), Error
> {
343 for xattr
in xattrs
{
344 let name
= CString
::new(&xattr
.name
[..])
345 .map_err(|_
| format_err
!("invalid xattr name with zeroes"))?
;
346 if let Err(err
) = xattr
::fsetxattr(fd
, &name
, &xattr
.value
) {
347 bail
!("fsetxattr failed with error: {}", err
);
350 if let Some(fcaps
) = fcaps
{
351 if let Err(err
) = xattr
::fsetxattr_fcaps(fd
, &fcaps
.data
) {
352 bail
!("fsetxattr_fcaps failed with error: {}", err
);
359 fn restore_quota_projid(
362 projid
: &Option
<PxarQuotaProjID
>,
363 ) -> Result
<(), Error
> {
364 if let Some(projid
) = projid
{
365 let mut fsxattr
= fs
::FSXAttr
::default();
367 fs
::fs_ioc_fsgetxattr(fd
, &mut fsxattr
).map_err(|err
| {
369 "error while getting fsxattr to restore quota project id - {}",
374 fsxattr
.fsx_projid
= projid
.projid
as u32;
376 fs
::fs_ioc_fssetxattr(fd
, &fsxattr
).map_err(|err
| {
378 "error while setting fsxattr to restore quota project id - {}",
388 fn restore_mode(&mut self, entry
: &PxarEntry
, fd
: RawFd
) -> Result
<(), Error
> {
389 let mode
= Mode
::from_bits_truncate((entry
.mode
as u32) & 0o7777);
391 nix
::sys
::stat
::fchmod(fd
, mode
)?
;
401 ) -> Result
<(), Error
> {
402 let mode
= Mode
::from_bits_truncate((entry
.mode
as u32) & 0o7777);
404 // NOTE: we want :FchmodatFlags::NoFollowSymlink, but fchmodat does not support that
405 // on linux (see man fchmodat). Fortunately, we can simply avoid calling this on symlinks.
406 nix
::sys
::stat
::fchmodat(
410 nix
::sys
::stat
::FchmodatFlags
::FollowSymlink
,
416 fn restore_ugid(&mut self, entry
: &PxarEntry
, fd
: RawFd
) -> Result
<(), Error
> {
420 let res
= unsafe { libc::fchown(fd, uid, gid) }
;
431 ) -> Result
<(), Error
> {
435 let res
= filename
.with_nix_path(|cstr
| unsafe {
436 libc
::fchownat(dirfd
, cstr
.as_ptr(), uid
, gid
, libc
::AT_SYMLINK_NOFOLLOW
)
443 fn restore_mtime(&mut self, entry
: &PxarEntry
, fd
: RawFd
) -> Result
<(), Error
> {
444 let times
= nsec_to_update_timespec(entry
.mtime
);
446 let res
= unsafe { libc::futimens(fd, ×[0]) }
;
457 ) -> Result
<(), Error
> {
458 let times
= nsec_to_update_timespec(entry
.mtime
);
460 let res
= filename
.with_nix_path(|cstr
| unsafe {
461 libc
::utimensat(dirfd
, cstr
.as_ptr(), ×
[0], libc
::AT_SYMLINK_NOFOLLOW
)
468 fn restore_device_at(
474 ) -> Result
<(), Error
> {
475 let rdev
= nix
::sys
::stat
::makedev(device
.major
, device
.minor
);
476 let mode
= ((entry
.mode
as u32) & libc
::S_IFMT
) | 0o0600;
478 .with_nix_path(|cstr
| unsafe { libc::mknodat(dirfd, cstr.as_ptr(), mode, rdev) }
)?
;
484 fn restore_socket_at(&mut self, dirfd
: RawFd
, filename
: &OsStr
) -> Result
<(), Error
> {
485 let mode
= libc
::S_IFSOCK
| 0o0600;
487 .with_nix_path(|cstr
| unsafe { libc::mknodat(dirfd, cstr.as_ptr(), mode, 0) }
)?
;
493 fn restore_fifo_at(&mut self, dirfd
: RawFd
, filename
: &OsStr
) -> Result
<(), Error
> {
494 let mode
= libc
::S_IFIFO
| 0o0600;
496 filename
.with_nix_path(|cstr
| unsafe { libc::mkfifoat(dirfd, cstr.as_ptr(), mode) }
)?
;
502 pub(crate) fn skip_bytes(&mut self, count
: usize) -> Result
<(), Error
> {
505 let todo
= count
- done
;
506 let n
= if todo
> self.skip_buffer
.len() {
507 self.skip_buffer
.len()
511 let data
= &mut self.skip_buffer
[..n
];
512 self.reader
.read_exact(data
)?
;
520 parent_fd
: Option
<RawFd
>,
524 ) -> Result
<(), Error
> {
525 //fixme: create symlink
526 //fixme: restore permission, acls, xattr, ...
528 let head
: PxarHeader
= self.read_item()?
;
531 let target
= self.read_link(head
.size
)?
;
532 //println!("TARGET: {:?}", target);
533 if let Some(fd
) = parent_fd
{
534 if let Err(err
) = symlinkat(&target
, fd
, filename
) {
535 bail
!("create symlink {:?} failed - {}", full_path
, err
);
540 "got unknown header type inside symlink entry {:016x}",
545 if let Some(fd
) = parent_fd
{
546 // self.restore_mode_at(&entry, fd, filename)?; //not supported on symlinks
547 self.restore_ugid_at(&entry
, fd
, filename
)?
;
548 self.restore_mtime_at(&entry
, fd
, filename
)?
;
556 parent_fd
: Option
<RawFd
>,
559 ) -> Result
<(), Error
> {
560 if !self.has_features(flags
::WITH_SOCKETS
) {
563 if let Some(fd
) = parent_fd
{
564 self.restore_socket_at(fd
, filename
)?
;
565 self.restore_mode_at(&entry
, fd
, filename
)?
;
566 self.restore_ugid_at(&entry
, fd
, filename
)?
;
567 self.restore_mtime_at(&entry
, fd
, filename
)?
;
575 parent_fd
: Option
<RawFd
>,
578 ) -> Result
<(), Error
> {
579 if !self.has_features(flags
::WITH_FIFOS
) {
582 if let Some(fd
) = parent_fd
{
583 self.restore_fifo_at(fd
, filename
)?
;
584 self.restore_mode_at(&entry
, fd
, filename
)?
;
585 self.restore_ugid_at(&entry
, fd
, filename
)?
;
586 self.restore_mtime_at(&entry
, fd
, filename
)?
;
594 parent_fd
: Option
<RawFd
>,
597 ) -> Result
<(), Error
> {
598 let head
: PxarHeader
= self.read_item()?
;
599 if head
.htype
!= PXAR_DEVICE
{
601 "got unknown header type inside device entry {:016x}",
605 let device
: PxarDevice
= self.read_item()?
;
606 if !self.has_features(flags
::WITH_DEVICE_NODES
) {
609 if let Some(fd
) = parent_fd
{
610 self.restore_device_at(&entry
, fd
, filename
, &device
)?
;
611 self.restore_mode_at(&entry
, fd
, filename
)?
;
612 self.restore_ugid_at(&entry
, fd
, filename
)?
;
613 self.restore_mtime_at(&entry
, fd
, filename
)?
;
619 /// Restores a regular file with its content and associated attributes to the
620 /// folder provided by the raw filedescriptor.
621 /// If None is passed instead of a filedescriptor, the file is not restored but
622 /// the archive reader is skipping over it instead.
623 fn restore_regular_file(
625 parent_fd
: Option
<RawFd
>,
629 ) -> Result
<(), Error
> {
630 let (head
, attr
) = self
632 .map_err(|err
| format_err
!("Reading of file attributes failed - {}", err
))?
;
634 if let Some(fd
) = parent_fd
{
635 let flags
= OFlag
::O_CREAT
| OFlag
::O_WRONLY
| OFlag
::O_EXCL
;
636 let open_mode
= Mode
::from_bits_truncate(0o0600 | entry
.mode
as u32); //fixme: upper 32bits of entry.mode?
637 let mut file
= file_openat(fd
, filename
, flags
, open_mode
)
638 .map_err(|err
| format_err
!("open file {:?} failed - {}", full_path
, err
))?
;
640 if head
.htype
!= PXAR_PAYLOAD
{
641 bail
!("got unknown header type for file entry {:016x}", head
.htype
);
644 if head
.size
< HEADER_SIZE
{
645 bail
!("detected short payload");
647 let need
= (head
.size
- HEADER_SIZE
) as usize;
649 let mut read_buffer
= unsafe { vec::uninitialized(64 * 1024) }
;
652 let todo
= need
- done
;
653 let n
= if todo
> read_buffer
.len() {
658 let data
= &mut read_buffer
[..n
];
659 self.reader
.read_exact(data
)?
;
660 file
.write_all(data
)?
;
664 self.restore_ugid(&entry
, file
.as_raw_fd())?
;
665 // fcaps have to be restored after restore_ugid as chown clears security.capability xattr, see CVE-2015-1350
666 self.restore_attributes(file
.as_raw_fd(), &attr
, &entry
)?
;
667 self.restore_mode(&entry
, file
.as_raw_fd())?
;
668 self.restore_mtime(&entry
, file
.as_raw_fd())?
;
670 if head
.htype
!= PXAR_PAYLOAD
{
671 bail
!("got unknown header type for file entry {:016x}", head
.htype
);
673 if head
.size
< HEADER_SIZE
{
674 bail
!("detected short payload");
676 self.skip_bytes((head
.size
- HEADER_SIZE
) as usize)?
;
685 dirs
: &mut PxarDirStack
,
689 match_pattern
: &[MatchPatternSlice
],
690 ) -> Result
<(), Error
> {
691 let (mut head
, attr
) = self
693 .map_err(|err
| format_err
!("Reading of directory attributes failed - {}", err
))?
;
695 let dir
= PxarDir
::new(filename
, entry
, attr
);
697 if matched
== MatchType
::Positive
{
698 dirs
.create_all_dirs(!self.allow_existing_dirs
)?
;
701 while head
.htype
== PXAR_FILENAME
{
702 let name
= self.read_filename(head
.size
)?
;
703 self.restore_dir_entry(base_path
, dirs
, &name
, matched
, match_pattern
)?
;
704 head
= self.read_item()?
;
707 if head
.htype
!= PXAR_GOODBYE
{
709 "got unknown header type inside directory entry {:016x}",
714 if head
.size
< HEADER_SIZE
{
715 bail
!("detected short goodbye table");
717 self.skip_bytes((head
.size
- HEADER_SIZE
) as usize)?
;
721 .ok_or_else(|| format_err
!("Tried to pop beyond dir root - this should not happen!"))?
;
722 if let Some(d
) = last
.dir
{
723 let fd
= d
.as_raw_fd();
724 self.restore_ugid(&last
.entry
, fd
)?
;
725 // fcaps have to be restored after restore_ugid as chown clears security.capability xattr, see CVE-2015-1350
726 self.restore_attributes(fd
, &last
.attr
, &last
.entry
)?
;
727 self.restore_mode(&last
.entry
, fd
)?
;
728 self.restore_mtime(&last
.entry
, fd
)?
;
734 /// Restore an archive into the specified directory.
736 /// The directory is created if it does not exist.
737 pub fn restore(&mut self, path
: &Path
, match_pattern
: &[MatchPattern
]) -> Result
<(), Error
> {
738 let mut slices
= Vec
::new();
739 for pattern
in match_pattern
{
740 slices
.push(pattern
.as_slice());
742 std
::fs
::create_dir_all(path
)
743 .map_err(|err
| format_err
!("error while creating directory {:?} - {}", path
, err
))?
;
745 let dir
= nix
::dir
::Dir
::open(
747 nix
::fcntl
::OFlag
::O_DIRECTORY
,
748 nix
::sys
::stat
::Mode
::empty(),
750 .map_err(|err
| format_err
!("unable to open target directory {:?} - {}", path
, err
))?
;
751 let fd
= dir
.as_raw_fd();
752 let mut dirs
= PxarDirStack
::new(fd
);
753 // An empty match pattern list indicates to restore the full archive.
754 let matched
= if slices
.is_empty() {
760 let header
: PxarHeader
= self.read_item()?
;
761 check_ca_header
::<PxarEntry
>(&header
, PXAR_ENTRY
)?
;
762 let entry
: PxarEntry
= self.read_item()?
;
764 let (mut head
, attr
) = self
766 .map_err(|err
| format_err
!("Reading of directory attributes failed - {}", err
))?
;
768 while head
.htype
== PXAR_FILENAME
{
769 let name
= self.read_filename(head
.size
)?
;
770 self.restore_dir_entry(path
, &mut dirs
, &name
, matched
, &slices
)?
;
771 head
= self.read_item()?
;
774 if head
.htype
!= PXAR_GOODBYE
{
776 "got unknown header type inside directory entry {:016x}",
781 if head
.size
< HEADER_SIZE
{
782 bail
!("detected short goodbye table");
784 self.skip_bytes((head
.size
- HEADER_SIZE
) as usize)?
;
786 self.restore_ugid(&entry
, fd
)?
;
787 // fcaps have to be restored after restore_ugid as chown clears security.capability xattr, see CVE-2015-1350
788 self.restore_attributes(fd
, &attr
, &entry
)?
;
789 self.restore_mode(&entry
, fd
)?
;
790 self.restore_mtime(&entry
, fd
)?
;
795 fn restore_dir_entry(
798 dirs
: &mut PxarDirStack
,
800 parent_matched
: MatchType
,
801 match_pattern
: &[MatchPatternSlice
],
802 ) -> Result
<(), Error
> {
803 let relative_path
= dirs
.as_path_buf();
804 let full_path
= base_path
.join(&relative_path
).join(filename
);
806 let head
: PxarHeader
= self.read_item()?
;
807 if head
.htype
== PXAR_FORMAT_HARDLINK
{
808 let (target
, _offset
) = self.read_hardlink(head
.size
)?
;
809 let target_path
= base_path
.join(&target
);
810 if dirs
.last_dir_fd().is_some() {
811 if let Some(ref callback
) = self.callback
{
812 (callback
)(&full_path
)?
;
814 hardlink(&target_path
, &full_path
)?
;
819 check_ca_header
::<PxarEntry
>(&head
, PXAR_ENTRY
)?
;
820 let entry
: PxarEntry
= self.read_item()?
;
821 let ifmt
= entry
.mode
as u32 & libc
::S_IFMT
;
823 let mut child_pattern
= Vec
::new();
824 // If parent was a match, then children should be assumed to match too
825 // This is especially the case when the full archive is restored and
826 // there are no match pattern.
827 let mut matched
= parent_matched
;
828 if !match_pattern
.is_empty() {
829 match MatchPatternSlice
::match_filename_include(
830 &CString
::new(filename
.as_bytes())?
,
831 ifmt
== libc
::S_IFDIR
,
834 (MatchType
::None
, _
) => matched
= MatchType
::None
,
835 (MatchType
::Negative
, _
) => matched
= MatchType
::Negative
,
836 (MatchType
::Positive
, _
) => matched
= MatchType
::Positive
,
837 (match_type
, pattern
) => {
838 matched
= match_type
;
839 child_pattern
= pattern
;
844 let fd
= if matched
== MatchType
::Positive
{
845 Some(dirs
.create_all_dirs(!self.allow_existing_dirs
)?
)
851 if let Some(ref callback
) = self.callback
{
852 (callback
)(&full_path
)?
;
858 self.restore_dir(base_path
, dirs
, entry
, &filename
, matched
, &child_pattern
)
860 libc
::S_IFLNK
=> self.restore_symlink(fd
, &full_path
, &entry
, &filename
),
861 libc
::S_IFSOCK
=> self.restore_socket(fd
, &entry
, &filename
),
862 libc
::S_IFIFO
=> self.restore_fifo(fd
, &entry
, &filename
),
863 libc
::S_IFBLK
| libc
::S_IFCHR
=> self.restore_device(fd
, &entry
, &filename
),
864 libc
::S_IFREG
=> self.restore_regular_file(fd
, &full_path
, &entry
, &filename
),
869 /// List/Dump archive content.
871 /// Simply print the list of contained files. This dumps archive
872 /// format details when the verbose flag is set (useful for debug).
873 pub fn dump_entry
<W
: std
::io
::Write
>(
878 ) -> Result
<(), Error
> {
879 let print_head
= |head
: &PxarHeader
| {
880 println
!("Type: {:016x}", head
.htype
);
881 println
!("Size: {}", head
.size
);
884 let head
: PxarHeader
= self.read_item()?
;
886 println
!("Path: {:?}", path
);
889 println
!("{:?}", path
);
892 if head
.htype
== PXAR_FORMAT_HARDLINK
{
893 let (target
, offset
) = self.read_hardlink(head
.size
)?
;
895 println
!("Hardlink: {} {:?}", offset
, target
);
900 check_ca_header
::<PxarEntry
>(&head
, PXAR_ENTRY
)?
;
901 let entry
: PxarEntry
= self.read_item()?
;
905 "Mode: {:08x} {:08x}",
907 (entry
.mode
as u32) & libc
::S_IFDIR
911 let ifmt
= (entry
.mode
as u32) & libc
::S_IFMT
;
913 if ifmt
== libc
::S_IFDIR
{
914 let mut entry_count
= 0;
917 let head
: PxarHeader
= self.read_item()?
;
922 // This call covers all the cases of the match statement
923 // regarding extended attributes. These calls will never
924 // break on the loop and can therefore be handled separately.
925 // If the header was matched, true is returned and we can continue
926 if self.dump_if_attribute(&head
, verbose
)?
{
932 let name
= self.read_filename(head
.size
)?
;
934 println
!("Name: {:?}", name
);
938 self.dump_entry(path
, verbose
, output
)?
;
942 let table_size
= (head
.size
- HEADER_SIZE
) as usize;
944 println
!("Goodbye: {:?}", path
);
945 self.dump_goodby_entries(entry_count
, table_size
)?
;
947 self.skip_bytes(table_size
)?
;
951 _
=> panic
!("got unexpected header type inside directory"),
954 } else if (ifmt
== libc
::S_IFBLK
)
955 || (ifmt
== libc
::S_IFCHR
)
956 || (ifmt
== libc
::S_IFLNK
)
957 || (ifmt
== libc
::S_IFREG
)
960 let head
: PxarHeader
= self.read_item()?
;
965 // This call covers all the cases of the match statement
966 // regarding extended attributes. These calls will never
967 // break on the loop and can therefore be handled separately.
968 // If the header was matched, true is returned and we can continue
969 if self.dump_if_attribute(&head
, verbose
)?
{
975 let target
= self.read_link(head
.size
)?
;
977 println
!("Symlink: {:?}", target
);
982 let device
: PxarDevice
= self.read_item()?
;
984 println
!("Device: {}, {}", device
.major
, device
.minor
);
989 let payload_size
= (head
.size
- HEADER_SIZE
) as usize;
991 println
!("Payload: {}", payload_size
);
993 self.skip_bytes(payload_size
)?
;
997 panic
!("got unexpected header type inside non-directory");
1001 } else if ifmt
== libc
::S_IFIFO
{
1005 } else if ifmt
== libc
::S_IFSOCK
{
1007 println
!("Socket:");
1010 panic
!("unknown st_mode");
1015 fn dump_if_attribute(&mut self, header
: &PxarHeader
, verbose
: bool
) -> Result
<bool
, Error
> {
1016 match header
.htype
{
1018 let xattr
= self.read_xattr((header
.size
- HEADER_SIZE
) as usize)?
;
1019 if verbose
&& self.has_features(flags
::WITH_XATTRS
) {
1020 println
!("XAttr: {:?}", xattr
);
1024 let fcaps
= self.read_fcaps((header
.size
- HEADER_SIZE
) as usize)?
;
1025 if verbose
&& self.has_features(flags
::WITH_FCAPS
) {
1026 println
!("FCaps: {:?}", fcaps
);
1030 let user
= self.read_item
::<PxarACLUser
>()?
;
1031 if verbose
&& self.has_features(flags
::WITH_ACL
) {
1032 println
!("ACLUser: {:?}", user
);
1036 let group
= self.read_item
::<PxarACLGroup
>()?
;
1037 if verbose
&& self.has_features(flags
::WITH_ACL
) {
1038 println
!("ACLGroup: {:?}", group
);
1041 PXAR_ACL_GROUP_OBJ
=> {
1042 let group_obj
= self.read_item
::<PxarACLGroupObj
>()?
;
1043 if verbose
&& self.has_features(flags
::WITH_ACL
) {
1044 println
!("ACLGroupObj: {:?}", group_obj
);
1047 PXAR_ACL_DEFAULT
=> {
1048 let default = self.read_item
::<PxarACLDefault
>()?
;
1049 if verbose
&& self.has_features(flags
::WITH_ACL
) {
1050 println
!("ACLDefault: {:?}", default);
1053 PXAR_ACL_DEFAULT_USER
=> {
1054 let default_user
= self.read_item
::<PxarACLUser
>()?
;
1055 if verbose
&& self.has_features(flags
::WITH_ACL
) {
1056 println
!("ACLDefaultUser: {:?}", default_user
);
1059 PXAR_ACL_DEFAULT_GROUP
=> {
1060 let default_group
= self.read_item
::<PxarACLGroup
>()?
;
1061 if verbose
&& self.has_features(flags
::WITH_ACL
) {
1062 println
!("ACLDefaultGroup: {:?}", default_group
);
1065 PXAR_QUOTA_PROJID
=> {
1066 let quota_projid
= self.read_item
::<PxarQuotaProjID
>()?
;
1067 if verbose
&& self.has_features(flags
::WITH_QUOTA_PROJID
) {
1068 println
!("Quota project id: {:?}", quota_projid
);
1071 _
=> return Ok(false),
1077 fn dump_goodby_entries(&mut self, entry_count
: usize, table_size
: usize) -> Result
<(), Error
> {
1078 const GOODBYE_ITEM_SIZE
: usize = std
::mem
::size_of
::<PxarGoodbyeItem
>();
1080 if table_size
< GOODBYE_ITEM_SIZE
{
1082 "Goodbye table to small ({} < {})",
1087 if (table_size
% GOODBYE_ITEM_SIZE
) != 0 {
1088 bail
!("Goodbye table with strange size ({})", table_size
);
1091 let entries
= table_size
/ GOODBYE_ITEM_SIZE
;
1093 if entry_count
!= (entries
- 1) {
1095 "Goodbye table with wrong entry count ({} != {})",
1104 let item
: PxarGoodbyeItem
= self.read_item()?
;
1106 if item
.hash
== PXAR_GOODBYE_TAIL_MARKER
{
1107 if count
!= entries
{
1108 bail
!("unexpected goodbye tail marker");
1110 println
!("Goodby tail mark.");
1114 "Goodby item: offset {}, size {}, hash {:016x}",
1115 item
.offset
, item
.size
, item
.hash
1117 if count
>= entries
{
1118 bail
!("too many goodbye items (no tail marker)");
1131 ) -> Result
<std
::fs
::File
, Error
> {
1133 filename
.with_nix_path(|cstr
| nix
::fcntl
::openat(parent
, cstr
, flags
, mode
))??
;
1135 let file
= unsafe { std::fs::File::from_raw_fd(fd) }
;
1140 fn hardlink(oldpath
: &Path
, newpath
: &Path
) -> Result
<(), Error
> {
1141 oldpath
.with_nix_path(|oldpath
| {
1142 newpath
.with_nix_path(|newpath
| {
1143 let res
= unsafe { libc::link(oldpath.as_ptr(), newpath.as_ptr()) }
;
1144 Errno
::result(res
)?
;
1150 fn symlinkat(target
: &Path
, parent
: RawFd
, linkname
: &OsStr
) -> Result
<(), Error
> {
1151 target
.with_nix_path(|target
| {
1152 linkname
.with_nix_path(|linkname
| {
1153 let res
= unsafe { libc::symlinkat(target.as_ptr(), parent, linkname.as_ptr()) }
;
1154 Errno
::result(res
)?
;
1160 fn nsec_to_update_timespec(mtime_nsec
: u64) -> [libc
::timespec
; 2] {
1162 const UTIME_OMIT
: i64 = (1 << 30) - 2;
1163 const NANOS_PER_SEC
: i64 = 1_000_000_000;
1165 let sec
= (mtime_nsec
as i64) / NANOS_PER_SEC
;
1166 let nsec
= (mtime_nsec
as i64) % NANOS_PER_SEC
;
1168 let times
: [libc
::timespec
; 2] = [
1171 tv_nsec
: UTIME_OMIT
,