1 //! *pxar* format decoder.
3 //! This module contain the code to decode *pxar* archive files.
6 use endian_trait
::Endian
;
8 use super::format_definition
::*;
9 use super::exclude_pattern
::*;
11 use std
::io
::{Read, Write}
;
12 use std
::path
::{Path, PathBuf}
;
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}
;
21 use nix
::fcntl
::OFlag
;
22 use nix
::sys
::stat
::Mode
;
23 use nix
::errno
::Errno
;
26 use proxmox
::tools
::io
::ReadExt
;
27 use proxmox
::tools
::vec
;
30 use crate::tools
::acl
;
31 use crate::tools
::xattr
;
33 // This one need Read, but works without Seek
34 pub struct SequentialDecoder
<'a
, R
: Read
, F
: Fn(&Path
) -> Result
<(), Error
>> {
41 const HEADER_SIZE
: u64 = std
::mem
::size_of
::<CaFormatHeader
>() as u64;
43 impl <'a
, R
: Read
, F
: Fn(&Path
) -> Result
<(), Error
>> SequentialDecoder
<'a
, R
, F
> {
45 pub fn new(reader
: &'a
mut R
, feature_flags
: u64, callback
: F
) -> Self {
46 let skip_buffer
= vec
::undefined(64*1024);
56 pub (crate) fn get_reader_mut(&mut self) -> & mut R
{
60 pub (crate) fn read_item
<T
: Endian
>(&mut self) -> Result
<T
, Error
> {
62 let mut result
: T
= unsafe { std::mem::uninitialized() }
;
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
>()
69 self.reader
.read_exact(buffer
)?
;
74 fn read_link(&mut self, size
: u64) -> Result
<PathBuf
, Error
> {
75 if size
< (HEADER_SIZE
+ 2) {
76 bail
!("dectected short link target.");
78 let target_len
= size
- HEADER_SIZE
;
80 if target_len
> (libc
::PATH_MAX
as u64) {
81 bail
!("link target too long ({}).", target_len
);
84 let mut buffer
= self.reader
.read_exact_allocated(target_len
as usize)?
;
86 let last_byte
= buffer
.pop().unwrap();
88 bail
!("link target not nul terminated.");
91 Ok(PathBuf
::from(std
::ffi
::OsString
::from_vec(buffer
)))
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.");
98 let offset
: u64 = self.read_item()?
;
99 let target
= self.read_link(size
- 8)?
;
101 for c
in target
.components() {
103 std
::path
::Component
::Normal(_
) => { /* OK */ }
,
105 bail
!("hardlink target contains invalid component {:?}", c
);
113 pub (crate) fn read_filename(&mut self, size
: u64) -> Result
<OsString
, Error
> {
114 if size
< (HEADER_SIZE
+ 2) {
115 bail
!("dectected short filename");
117 let name_len
= size
- HEADER_SIZE
;
119 if name_len
> ((libc
::FILENAME_MAX
as u64) + 1) {
120 bail
!("filename too long ({}).", name_len
);
123 let mut buffer
= self.reader
.read_exact_allocated(name_len
as usize)?
;
125 let last_byte
= buffer
.pop().unwrap();
126 if last_byte
!= 0u8 {
127 bail
!("filename entry not nul terminated.");
130 if buffer
== b
"." || buffer
== b
".." {
131 bail
!("found invalid filename '.' or '..'.");
134 if buffer
.iter().find(|b
| (**b
== b'
/'
|| **b
== b'
\0'
)).is_some() {
135 bail
!("found invalid filename with slashes or nul bytes.");
138 let name
= std
::ffi
::OsString
::from_vec(buffer
);
140 bail
!("found empty filename.");
146 fn has_features(&self, feature_flags
: u64) -> bool
{
147 (self.feature_flags
& feature_flags
) == feature_flags
150 fn read_xattr(&mut self, size
: usize) -> Result
<CaFormatXAttr
, Error
> {
151 let buffer
= self.reader
.read_exact_allocated(size
)?
;
153 let separator
= buffer
.iter().position(|c
| *c
== b'
\0'
)
154 .ok_or_else(|| format_err
!("no value found in xattr"))?
;
156 let (name
, value
) = buffer
.split_at(separator
);
157 if !xattr
::is_valid_xattr_name(name
) ||
158 xattr
::is_security_capability(name
)
160 bail
!("incorrect xattr name - {}.", String
::from_utf8_lossy(name
));
165 value
: value
[1..].to_vec(),
169 fn read_fcaps(&mut self, size
: usize) -> Result
<CaFormatFCaps
, Error
> {
170 let buffer
= self.reader
.read_exact_allocated(size
)?
;
172 Ok(CaFormatFCaps { data: buffer }
)
175 fn restore_attributes(
177 entry
: &CaFormatEntry
,
179 ) -> Result
<CaFormatHeader
, Error
> {
180 let mut xattrs
= Vec
::new();
181 let mut fcaps
= None
;
182 let mut quota_projid
= None
;
184 let mut acl_user
= Vec
::new();
185 let mut acl_group
= Vec
::new();
186 let mut acl_group_obj
= None
;
188 let mut acl_default
= None
;
189 let mut acl_default_user
= Vec
::new();
190 let mut acl_default_group
= Vec
::new();
192 let mut head
: CaFormatHeader
= self.read_item()?
;
193 let mut size
= (head
.size
- HEADER_SIZE
) as usize;
197 if self.has_features(CA_FORMAT_WITH_XATTRS
) {
198 xattrs
.push(self.read_xattr(size
)?
);
200 self.skip_bytes(size
)?
;
204 if self.has_features(CA_FORMAT_WITH_FCAPS
) {
205 fcaps
= Some(self.read_fcaps(size
)?
);
207 self.skip_bytes(size
)?
;
210 CA_FORMAT_ACL_USER
=> {
211 if self.has_features(CA_FORMAT_WITH_ACL
) {
212 acl_user
.push(self.read_item
::<CaFormatACLUser
>()?
);
214 self.skip_bytes(size
)?
;
217 CA_FORMAT_ACL_GROUP
=> {
218 if self.has_features(CA_FORMAT_WITH_ACL
) {
219 acl_group
.push(self.read_item
::<CaFormatACLGroup
>()?
);
221 self.skip_bytes(size
)?
;
224 CA_FORMAT_ACL_GROUP_OBJ
=> {
225 if self.has_features(CA_FORMAT_WITH_ACL
) {
226 acl_group_obj
= Some(self.read_item
::<CaFormatACLGroupObj
>()?
);
228 self.skip_bytes(size
)?
;
231 CA_FORMAT_ACL_DEFAULT
=> {
232 if self.has_features(CA_FORMAT_WITH_ACL
) {
233 acl_default
= Some(self.read_item
::<CaFormatACLDefault
>()?
);
235 self.skip_bytes(size
)?
;
238 CA_FORMAT_ACL_DEFAULT_USER
=> {
239 if self.has_features(CA_FORMAT_WITH_ACL
) {
240 acl_default_user
.push(self.read_item
::<CaFormatACLUser
>()?
);
242 self.skip_bytes(size
)?
;
245 CA_FORMAT_ACL_DEFAULT_GROUP
=> {
246 if self.has_features(CA_FORMAT_WITH_ACL
) {
247 acl_default_group
.push(self.read_item
::<CaFormatACLGroup
>()?
);
249 self.skip_bytes(size
)?
;
252 CA_FORMAT_QUOTA_PROJID
=> {
253 if self.has_features(CA_FORMAT_WITH_QUOTA_PROJID
) {
254 quota_projid
= Some(self.read_item
::<CaFormatQuotaProjID
>()?
);
256 self.skip_bytes(size
)?
;
261 head
= self.read_item()?
;
262 size
= (head
.size
- HEADER_SIZE
) as usize;
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
)?
;
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
{
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
)?
;
278 acl
.add_entry_full(acl
::ACL_GROUP_OBJ
, None
, mode_group_to_acl_permissions(entry
.mode
))?
;
281 for user
in acl_user
{
282 acl
.add_entry_full(acl
::ACL_USER
, Some(user
.uid
), user
.permissions
)?
;
284 for group
in acl_group
{
285 acl
.add_entry_full(acl
::ACL_GROUP
, Some(group
.gid
), group
.permissions
)?
;
287 let proc_path
= Path
::new("/proc/self/fd/").join(fd
.to_string());
289 bail
!("Error while restoring ACL - ACL invalid");
291 acl
.set_file(&proc_path
, acl
::ACL_TYPE_ACCESS
)?
;
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
)?
;
301 for user
in acl_default_user
{
302 acl
.add_entry_full(acl
::ACL_USER
, Some(user
.uid
), user
.permissions
)?
;
304 for group
in acl_default_group
{
305 acl
.add_entry_full(acl
::ACL_GROUP
, Some(group
.gid
), group
.permissions
)?
;
308 bail
!("Error while restoring ACL - ACL invalid");
310 acl
.set_file(&proc_path
, acl
::ACL_TYPE_DEFAULT
)?
;
312 self.restore_quota_projid(fd
, quota_projid
)?
;
318 // Restore xattrs and fcaps to the given RawFd.
319 fn restore_xattrs_fcaps_fd(
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
);
330 if let Some(fcaps
) = fcaps
{
331 if let Err(err
) = xattr
::fsetxattr_fcaps(fd
, fcaps
) {
332 bail
!("fsetxattr_fcaps failed with error: {}", err
);
339 fn restore_quota_projid(
342 projid
: Option
<CaFormatQuotaProjID
>
343 ) -> Result
<(), Error
> {
344 if let Some(projid
) = projid
{
345 let mut fsxattr
= fs
::FSXAttr
::default();
347 fs
::fs_ioc_fsgetxattr(fd
, &mut fsxattr
)
348 .map_err(|err
| format_err
!("error while getting fsxattr to restore quota project id - {}", err
))?
;
350 fsxattr
.fsx_projid
= projid
.projid
as u32;
352 fs
::fs_ioc_fssetxattr(fd
, &fsxattr
)
353 .map_err(|err
| format_err
!("error while setting fsxattr to restore quota project id - {}", err
))?
;
360 fn restore_mode(&mut self, entry
: &CaFormatEntry
, fd
: RawFd
) -> Result
<(), Error
> {
362 let mode
= Mode
::from_bits_truncate((entry
.mode
as u32) & 0o7777);
364 nix
::sys
::stat
::fchmod(fd
, mode
)?
;
369 fn restore_mode_at(&mut self, entry
: &CaFormatEntry
, dirfd
: RawFd
, filename
: &OsStr
) -> Result
<(), Error
> {
371 let mode
= Mode
::from_bits_truncate((entry
.mode
as u32) & 0o7777);
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
)?
;
380 fn restore_ugid(&mut self, entry
: &CaFormatEntry
, fd
: RawFd
) -> Result
<(), Error
> {
382 let uid
= entry
.uid
as u32;
383 let gid
= entry
.gid
as u32;
385 let res
= unsafe { libc::fchown(fd, uid, gid) }
;
391 fn restore_ugid_at(&mut self, entry
: &CaFormatEntry
, dirfd
: RawFd
, filename
: &OsStr
) -> Result
<(), Error
> {
393 let uid
= entry
.uid
as u32;
394 let gid
= entry
.gid
as u32;
396 let res
= filename
.with_nix_path(|cstr
| unsafe {
397 libc
::fchownat(dirfd
, cstr
.as_ptr(), uid
, gid
, libc
::AT_SYMLINK_NOFOLLOW
)
404 fn restore_mtime(&mut self, entry
: &CaFormatEntry
, fd
: RawFd
) -> Result
<(), Error
> {
406 let times
= nsec_to_update_timespec(entry
.mtime
);
408 let res
= unsafe { libc::futimens(fd, ×[0]) }
;
414 fn restore_mtime_at(&mut self, entry
: &CaFormatEntry
, dirfd
: RawFd
, filename
: &OsStr
) -> Result
<(), Error
> {
416 let times
= nsec_to_update_timespec(entry
.mtime
);
418 let res
= filename
.with_nix_path(|cstr
| unsafe {
419 libc
::utimensat(dirfd
, cstr
.as_ptr(), ×
[0], libc
::AT_SYMLINK_NOFOLLOW
)
426 fn restore_device_at(&mut self, entry
: &CaFormatEntry
, dirfd
: RawFd
, filename
: &OsStr
, device
: &CaFormatDevice
) -> Result
<(), Error
> {
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
)
438 fn restore_socket_at(&mut self, dirfd
: RawFd
, filename
: &OsStr
) -> Result
<(), Error
> {
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)
449 fn restore_fifo_at(&mut self, dirfd
: RawFd
, filename
: &OsStr
) -> Result
<(), Error
> {
451 let mode
= libc
::S_IFIFO
| 0o0600;
452 let res
= filename
.with_nix_path(|cstr
| unsafe {
453 libc
::mkfifoat(dirfd
, cstr
.as_ptr(), mode
)
460 fn skip_bytes(&mut self, count
: usize) -> Result
<(), Error
> {
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
)?
;
474 parent_fd
: Option
<RawFd
>,
476 entry
: &CaFormatEntry
,
478 ) -> Result
<(), Error
> {
479 //fixme: create symlink
480 //fixme: restore permission, acls, xattr, ...
482 let head
: CaFormatHeader
= self.read_item()?
;
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
);
494 bail
!("got unknown header type inside symlink entry {:016x}", head
.htype
);
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
)?
;
509 parent_fd
: Option
<RawFd
>,
510 entry
: &CaFormatEntry
,
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
)?
;
525 parent_fd
: Option
<RawFd
>,
526 entry
: &CaFormatEntry
,
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
)?
;
541 parent_fd
: Option
<RawFd
>,
542 entry
: &CaFormatEntry
,
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
);
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
)?
;
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(
566 parent_fd
: Option
<RawFd
>,
568 entry
: &CaFormatEntry
,
570 ) -> Result
<(), Error
> {
571 let mut read_buffer
: [u8; 64*1024] = unsafe { std::mem::uninitialized() }
;
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
))?
;
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
))?
;
584 if head
.htype
!= CA_FORMAT_PAYLOAD
{
585 bail
!("got unknown header type for file entry {:016x}", head
.htype
);
588 if head
.size
< HEADER_SIZE
{
589 bail
!("detected short payload");
591 let need
= (head
.size
- HEADER_SIZE
) as usize;
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
)?
;
603 self.restore_mode(&entry
, file
.as_raw_fd())?
;
604 self.restore_mtime(&entry
, file
.as_raw_fd())?
;
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
);
611 if head
.size
< HEADER_SIZE
{
612 bail
!("detected short payload");
614 self.skip_bytes((head
.size
- HEADER_SIZE
) as usize)?
;
620 fn restore_dir_sequential(
623 relative_path
: &mut PathBuf
,
625 parent_fd
: Option
<RawFd
>,
626 entry
: &CaFormatEntry
,
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())?
635 dir_mkdirat(pfd
, filename
, true)
636 .map_err(|err
| format_err
!("unable to open directory {:?} - {}", full_path
, err
))?
638 (Some(dir
.as_raw_fd()), Some(dir
))
643 if let Some(fd
) = fd
{
644 self.restore_ugid(&entry
, fd
)?
;
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
))?
;
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
)?
;
655 head
= self.read_item()?
;
658 if head
.htype
!= CA_FORMAT_GOODBYE
{
659 bail
!("got unknown header type inside directory entry {:016x}", head
.htype
);
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)?
;
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
)?
;
675 /// Restore an archive into the specified directory.
677 /// The directory is created if it does not exist.
678 pub fn restore(&mut self, path
: &Path
, match_pattern
: &Vec
<PxarExcludePattern
>) -> Result
<(), Error
> {
680 let _
= std
::fs
::create_dir(path
);
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());
686 let mut relative_path
= PathBuf
::new();
687 self.restore_sequential(path
, &mut relative_path
, &OsString
::new(), fd
, match_pattern
)
690 fn restore_sequential(
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
);
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
)?
;
711 check_ca_header
::<CaFormatEntry
>(&head
, CA_FORMAT_ENTRY
)?
;
712 let entry
: CaFormatEntry
= self.read_item()?
;
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();
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
,
729 (self.callback
)(&full_path
)?
;
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
),
743 /// List/Dump archive content.
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
>(
752 ) -> Result
<(), Error
> {
754 let print_head
= |head
: &CaFormatHeader
| {
755 println
!("Type: {:016x}", head
.htype
);
756 println
!("Size: {}", head
.size
);
759 let head
: CaFormatHeader
= self.read_item()?
;
761 println
!("Path: {:?}", path
);
764 println
!("{:?}", path
);
767 if head
.htype
== PXAR_FORMAT_HARDLINK
{
768 let (target
, offset
) = self.read_hardlink(head
.size
)?
;
770 println
!("Hardlink: {} {:?}", offset
, target
);
775 check_ca_header
::<CaFormatEntry
>(&head
, CA_FORMAT_ENTRY
)?
;
776 let entry
: CaFormatEntry
= self.read_item()?
;
779 println
!("Mode: {:08x} {:08x}", entry
.mode
, (entry
.mode
as u32) & libc
::S_IFDIR
);
782 let ifmt
= (entry
.mode
as u32) & libc
::S_IFMT
;
784 if ifmt
== libc
::S_IFDIR
{
786 let mut entry_count
= 0;
789 let head
: CaFormatHeader
= self.read_item()?
;
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
)?
{
803 CA_FORMAT_FILENAME
=> {
804 let name
= self.read_filename(head
.size
)?
;
805 if verbose { println!("Name: {:?}
", name); }
808 self.dump_entry(path, verbose, output)?;
811 CA_FORMAT_GOODBYE => {
812 let table_size = (head.size - HEADER_SIZE) as usize;
814 println!("Goodbye
: {:?}
", path);
815 self.dump_goodby_entries(entry_count, table_size)?;
817 self.skip_bytes(table_size)?;
821 _ => panic!("got unexpected header
type inside directory
"),
824 } else if (ifmt == libc::S_IFBLK) || (ifmt == libc::S_IFCHR) ||
825 (ifmt == libc::S_IFLNK) || (ifmt == libc::S_IFREG)
828 let head: CaFormatHeader = self.read_item()?;
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)? {
842 CA_FORMAT_SYMLINK => {
843 let target = self.read_link(head.size)?;
845 println!("Symlink
: {:?}
", target);
849 CA_FORMAT_DEVICE => {
850 let device: CaFormatDevice = self.read_item()?;
852 println!("Device
: {}
, {}
", device.major, device.minor);
856 CA_FORMAT_PAYLOAD => {
857 let payload_size = (head.size - HEADER_SIZE) as usize;
859 println!("Payload
: {}
", payload_size);
861 self.skip_bytes(payload_size)?;
865 panic!("got unexpected header
type inside non
-directory
");
869 } else if ifmt == libc::S_IFIFO {
873 } else if ifmt == libc::S_IFSOCK {
878 panic!("unknown st_mode
");
883 fn dump_if_attribute(&mut self, header: &CaFormatHeader, verbose: bool) -> Result<bool, Error> {
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);
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);
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);
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);
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);
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);
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);
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);
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);
939 _ => return Ok(false),
945 fn dump_goodby_entries(
949 ) -> Result<(), Error> {
951 const GOODBYE_ITEM_SIZE: usize = std::mem::size_of::<CaFormatGoodbyeItem>();
953 if table_size < GOODBYE_ITEM_SIZE {
954 bail!("Goodbye table to
small ({}
< {}
)", table_size, GOODBYE_ITEM_SIZE);
956 if (table_size % GOODBYE_ITEM_SIZE) != 0 {
957 bail!("Goodbye table with strange
size ({}
)", table_size);
960 let entries = table_size / GOODBYE_ITEM_SIZE;
962 if entry_count != (entries - 1) {
963 bail!("Goodbye table with wrong entry
count ({}
!= {}
)", entry_count, entries - 1);
969 let item: CaFormatGoodbyeItem = self.read_item()?;
971 if item.hash == CA_FORMAT_GOODBYE_TAIL_MARKER {
972 if count != entries {
973 bail!("unexpected goodbye tail marker
");
975 println!("Goodby tail mark
.");
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
)");
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();
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());
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;
1012 child_pattern.push(pattern.get_rest_pattern());
1014 MatchType::PartialInclude => {
1015 if match_state == MatchType::PartialInclude {
1016 match_state = MatchType::PartialExclude;
1018 child_pattern.push(pattern.get_rest_pattern());
1023 (match_state, child_pattern)
1026 fn file_openat(parent: RawFd, filename: &OsStr, flags: OFlag, mode: Mode) -> Result<std::fs::File, Error> {
1028 let fd = filename.with_nix_path(|cstr| {
1029 nix::fcntl::openat(parent, cstr.as_ref(), flags, mode)
1032 let file = unsafe { std::fs::File::from_raw_fd(fd) };
1037 fn dir_mkdirat(parent: RawFd, filename: &OsStr, create_new: bool) -> Result<nix::dir::Dir, nix::Error> {
1039 // call mkdirat first
1040 let res = filename.with_nix_path(|cstr| unsafe {
1041 libc::mkdirat(parent, cstr.as_ptr(), libc::S_IRWXU)
1044 match Errno::result(res) {
1047 if err == nix::Error::Sys(nix::errno::Errno::EEXIST) {
1048 if create_new { return Err(err); }
1055 let dir = nix::dir::Dir::openat(parent, filename, OFlag::O_DIRECTORY, Mode::empty())?;
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)?;
1070 fn symlinkat(target: &Path, parent: RawFd, linkname: &OsStr) -> Result<(), Error> {
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)?;
1081 fn nsec_to_update_timespec(mtime_nsec: u64) -> [libc::timespec; 2] {
1084 const UTIME_OMIT: i64 = ((1 << 30) - 2);
1085 const NANOS_PER_SEC: i64 = 1_000_000_000;
1087 let sec = (mtime_nsec as i64) / NANOS_PER_SEC;
1088 let nsec = (mtime_nsec as i64) % NANOS_PER_SEC;
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 },
1098 fn mode_user_to_acl_permissions(mode: u64) -> u64 {
1099 return (mode >> 6) & 7;
1102 fn mode_group_to_acl_permissions(mode: u64) -> u64 {
1103 return (mode >> 3) & 7;
1106 fn mode_other_to_acl_permissions(mode: u64) -> u64 {