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
::*;
10 use std
::io
::{Read, Write}
;
11 use std
::path
::{Path, PathBuf}
;
13 use std
::os
::unix
::io
::AsRawFd
;
14 use std
::os
::unix
::io
::RawFd
;
15 use std
::os
::unix
::io
::FromRawFd
;
16 use std
::os
::unix
::ffi
::{OsStringExt}
;
17 use std
::ffi
::{OsStr, OsString}
;
19 use nix
::fcntl
::OFlag
;
20 use nix
::sys
::stat
::Mode
;
21 use nix
::errno
::Errno
;
24 use crate::tools
::io
::ops
::*;
25 use crate::tools
::vec
;
27 use crate::tools
::acl
;
28 use crate::tools
::xattr
;
30 // This one need Read, but works without Seek
31 pub struct SequentialDecoder
<'a
, R
: Read
> {
37 const HEADER_SIZE
: u64 = std
::mem
::size_of
::<CaFormatHeader
>() as u64;
39 impl <'a
, R
: Read
> SequentialDecoder
<'a
, R
> {
41 pub fn new(reader
: &'a
mut R
, feature_flags
: u64) -> Self {
42 let skip_buffer
= vec
::undefined(64*1024);
51 pub (crate) fn get_reader_mut(&mut self) -> & mut R
{
55 pub (crate) fn read_item
<T
: Endian
>(&mut self) -> Result
<T
, Error
> {
57 let mut result
: T
= unsafe { std::mem::uninitialized() }
;
59 let buffer
= unsafe { std
::slice
::from_raw_parts_mut(
60 &mut result
as *mut T
as *mut u8,
61 std
::mem
::size_of
::<T
>()
64 self.reader
.read_exact(buffer
)?
;
69 fn read_link(&mut self, size
: u64) -> Result
<PathBuf
, Error
> {
70 if size
< (HEADER_SIZE
+ 2) {
71 bail
!("dectected short link target.");
73 let target_len
= size
- HEADER_SIZE
;
75 if target_len
> (libc
::PATH_MAX
as u64) {
76 bail
!("link target too long ({}).", target_len
);
79 let mut buffer
= self.reader
.read_exact_allocated(target_len
as usize)?
;
81 let last_byte
= buffer
.pop().unwrap();
83 bail
!("link target not nul terminated.");
86 Ok(PathBuf
::from(std
::ffi
::OsString
::from_vec(buffer
)))
89 fn read_hardlink(&mut self, size
: u64) -> Result
<(PathBuf
, u64), Error
> {
90 if size
< (HEADER_SIZE
+ 8 + 2) {
91 bail
!("dectected short hardlink header.");
93 let offset
: u64 = self.read_item()?
;
94 let target
= self.read_link(size
- 8)?
;
96 for c
in target
.components() {
98 std
::path
::Component
::Normal(_
) => { /* OK */ }
,
100 bail
!("hardlink target contains invalid component {:?}", c
);
108 pub (crate) fn read_filename(&mut self, size
: u64) -> Result
<OsString
, Error
> {
109 if size
< (HEADER_SIZE
+ 2) {
110 bail
!("dectected short filename");
112 let name_len
= size
- HEADER_SIZE
;
114 if name_len
> ((libc
::FILENAME_MAX
as u64) + 1) {
115 bail
!("filename too long ({}).", name_len
);
118 let mut buffer
= self.reader
.read_exact_allocated(name_len
as usize)?
;
120 let last_byte
= buffer
.pop().unwrap();
121 if last_byte
!= 0u8 {
122 bail
!("filename entry not nul terminated.");
125 if (buffer
.len() == 1 && buffer
[0] == b'
.'
) || (buffer
.len() == 2 && buffer
[0] == b'
.'
&& buffer
[1] == b'
.'
) {
126 bail
!("found invalid filename with slashes.");
129 if buffer
.iter().find(|b
| (**b
== b'
/'
)).is_some() {
130 bail
!("found invalid filename with slashes.");
133 let name
= std
::ffi
::OsString
::from_vec(buffer
);
135 bail
!("found empty filename.");
141 fn has_features(&self, feature_flags
: u64) -> bool
{
142 (self.feature_flags
& feature_flags
) == feature_flags
145 fn read_xattr(&mut self, size
: usize) -> Result
<CaFormatXAttr
, Error
> {
146 let buffer
= self.reader
.read_exact_allocated(size
)?
;
148 let separator
= buffer
.iter().position(|c
| *c
== b'
\0'
)
149 .ok_or_else(|| format_err
!("no value found in xattr"))?
;
151 let (name
, value
) = buffer
.split_at(separator
);
152 if !xattr
::is_valid_xattr_name(name
) ||
153 xattr
::is_security_capability(name
)
155 bail
!("incorrect xattr name - {}.", String
::from_utf8_lossy(name
));
160 value
: value
[1..].to_vec(),
164 fn read_fcaps(&mut self, size
: usize) -> Result
<CaFormatFCaps
, Error
> {
165 let buffer
= self.reader
.read_exact_allocated(size
)?
;
167 Ok(CaFormatFCaps { data: buffer }
)
170 fn restore_attributes(&mut self, entry
: &CaFormatEntry
, fd
: RawFd
) -> Result
<CaFormatHeader
, Error
> {
171 let mut xattrs
= Vec
::new();
172 let mut fcaps
= None
;
173 let mut quota_projid
= None
;
175 let mut acl_user
= Vec
::new();
176 let mut acl_group
= Vec
::new();
177 let mut acl_group_obj
= None
;
179 let mut acl_default
= None
;
180 let mut acl_default_user
= Vec
::new();
181 let mut acl_default_group
= Vec
::new();
183 let mut head
: CaFormatHeader
= self.read_item()?
;
184 let mut size
= (head
.size
- HEADER_SIZE
) as usize;
188 if self.has_features(CA_FORMAT_WITH_XATTRS
) {
189 xattrs
.push(self.read_xattr(size
)?
);
191 self.skip_bytes(size
)?
;
195 if self.has_features(CA_FORMAT_WITH_FCAPS
) {
196 fcaps
= Some(self.read_fcaps(size
)?
);
198 self.skip_bytes(size
)?
;
201 CA_FORMAT_ACL_USER
=> {
202 if self.has_features(CA_FORMAT_WITH_ACL
) {
203 acl_user
.push(self.read_item
::<CaFormatACLUser
>()?
);
205 self.skip_bytes(size
)?
;
208 CA_FORMAT_ACL_GROUP
=> {
209 if self.has_features(CA_FORMAT_WITH_ACL
) {
210 acl_group
.push(self.read_item
::<CaFormatACLGroup
>()?
);
212 self.skip_bytes(size
)?
;
215 CA_FORMAT_ACL_GROUP_OBJ
=> {
216 if self.has_features(CA_FORMAT_WITH_ACL
) {
217 acl_group_obj
= Some(self.read_item
::<CaFormatACLGroupObj
>()?
);
219 self.skip_bytes(size
)?
;
222 CA_FORMAT_ACL_DEFAULT
=> {
223 if self.has_features(CA_FORMAT_WITH_ACL
) {
224 acl_default
= Some(self.read_item
::<CaFormatACLDefault
>()?
);
226 self.skip_bytes(size
)?
;
229 CA_FORMAT_ACL_DEFAULT_USER
=> {
230 if self.has_features(CA_FORMAT_WITH_ACL
) {
231 acl_default_user
.push(self.read_item
::<CaFormatACLUser
>()?
);
233 self.skip_bytes(size
)?
;
236 CA_FORMAT_ACL_DEFAULT_GROUP
=> {
237 if self.has_features(CA_FORMAT_WITH_ACL
) {
238 acl_default_group
.push(self.read_item
::<CaFormatACLGroup
>()?
);
240 self.skip_bytes(size
)?
;
243 CA_FORMAT_QUOTA_PROJID
=> {
244 if self.has_features(CA_FORMAT_WITH_QUOTA_PROJID
) {
245 quota_projid
= Some(self.read_item
::<CaFormatQuotaProjID
>()?
);
247 self.skip_bytes(size
)?
;
252 head
= self.read_item()?
;
253 size
= (head
.size
- HEADER_SIZE
) as usize;
255 self.restore_xattrs_fcaps_fd(fd
, xattrs
, fcaps
)?
;
257 let mut acl
= acl
::ACL
::init(5)?
;
258 acl
.add_entry_full(acl
::ACL_USER_OBJ
, None
, mode_user_to_acl_permissions(entry
.mode
))?
;
259 acl
.add_entry_full(acl
::ACL_OTHER
, None
, mode_other_to_acl_permissions(entry
.mode
))?
;
260 match acl_group_obj
{
262 acl
.add_entry_full(acl
::ACL_MASK
, None
, mode_group_to_acl_permissions(entry
.mode
))?
;
263 acl
.add_entry_full(acl
::ACL_GROUP_OBJ
, None
, group_obj
.permissions
)?
;
266 acl
.add_entry_full(acl
::ACL_GROUP_OBJ
, None
, mode_group_to_acl_permissions(entry
.mode
))?
;
269 for user
in acl_user
{
270 acl
.add_entry_full(acl
::ACL_USER
, Some(user
.uid
), user
.permissions
)?
;
272 for group
in acl_group
{
273 acl
.add_entry_full(acl
::ACL_GROUP
, Some(group
.gid
), group
.permissions
)?
;
275 let proc_path
= Path
::new("/proc/self/fd/").join(fd
.to_string());
277 bail
!("Error while restoring ACL - ACL invalid");
279 acl
.set_file(&proc_path
, acl
::ACL_TYPE_ACCESS
)?
;
281 if let Some(default) = acl_default
{
282 let mut acl
= acl
::ACL
::init(5)?
;
283 acl
.add_entry_full(acl
::ACL_USER_OBJ
, None
, default.user_obj_permissions
)?
;
284 acl
.add_entry_full(acl
::ACL_GROUP_OBJ
, None
, default.group_obj_permissions
)?
;
285 acl
.add_entry_full(acl
::ACL_OTHER
, None
, default.other_permissions
)?
;
286 if default.mask_permissions
!= std
::u64::MAX
{
287 acl
.add_entry_full(acl
::ACL_MASK
, None
, default.mask_permissions
)?
;
289 for user
in acl_default_user
{
290 acl
.add_entry_full(acl
::ACL_USER
, Some(user
.uid
), user
.permissions
)?
;
292 for group
in acl_default_group
{
293 acl
.add_entry_full(acl
::ACL_GROUP
, Some(group
.gid
), group
.permissions
)?
;
296 bail
!("Error while restoring ACL - ACL invalid");
298 acl
.set_file(&proc_path
, acl
::ACL_TYPE_DEFAULT
)?
;
300 self.restore_quota_projid(fd
, quota_projid
)?
;
305 fn restore_xattrs_fcaps_fd(&mut self, fd
: RawFd
, xattrs
: Vec
<CaFormatXAttr
>, fcaps
: Option
<CaFormatFCaps
>) -> Result
<(), Error
> {
306 for xattr
in xattrs
{
307 if let Err(err
) = xattr
::fsetxattr(fd
, xattr
) {
308 bail
!("fsetxattr failed with error: {}", err
);
311 if let Some(fcaps
) = fcaps
{
312 if let Err(err
) = xattr
::fsetxattr_fcaps(fd
, fcaps
) {
313 bail
!("fsetxattr_fcaps failed with error: {}", err
);
320 fn restore_quota_projid(&mut self, fd
: RawFd
, projid
: Option
<CaFormatQuotaProjID
>) -> Result
<(), Error
> {
321 if let Some(projid
) = projid
{
322 let mut fsxattr
= fs
::FSXAttr
::default();
324 fs
::fs_ioc_fsgetxattr(fd
, &mut fsxattr
)
325 .map_err(|err
| format_err
!("error while getting fsxattr to restore quota project id - {}", err
))?
;
327 fsxattr
.fsx_projid
= projid
.projid
as u32;
329 fs
::fs_ioc_fssetxattr(fd
, &fsxattr
)
330 .map_err(|err
| format_err
!("error while setting fsxattr to restore quota project id - {}", err
))?
;
337 fn restore_mode(&mut self, entry
: &CaFormatEntry
, fd
: RawFd
) -> Result
<(), Error
> {
339 let mode
= Mode
::from_bits_truncate((entry
.mode
as u32) & 0o7777);
341 nix
::sys
::stat
::fchmod(fd
, mode
)?
;
346 fn restore_mode_at(&mut self, entry
: &CaFormatEntry
, dirfd
: RawFd
, filename
: &OsStr
) -> Result
<(), Error
> {
348 let mode
= Mode
::from_bits_truncate((entry
.mode
as u32) & 0o7777);
350 // NOTE: we want :FchmodatFlags::NoFollowSymlink, but fchmodat does not support that
351 // on linux (see man fchmodat). Fortunately, we can simply avoid calling this on symlinks.
352 nix
::sys
::stat
::fchmodat(Some(dirfd
), filename
, mode
, nix
::sys
::stat
::FchmodatFlags
::FollowSymlink
)?
;
357 fn restore_ugid(&mut self, entry
: &CaFormatEntry
, fd
: RawFd
) -> Result
<(), Error
> {
359 let uid
= entry
.uid
as u32;
360 let gid
= entry
.gid
as u32;
362 let res
= unsafe { libc::fchown(fd, uid, gid) }
;
368 fn restore_ugid_at(&mut self, entry
: &CaFormatEntry
, dirfd
: RawFd
, filename
: &OsStr
) -> Result
<(), Error
> {
370 let uid
= entry
.uid
as u32;
371 let gid
= entry
.gid
as u32;
373 let res
= filename
.with_nix_path(|cstr
| unsafe {
374 libc
::fchownat(dirfd
, cstr
.as_ptr(), uid
, gid
, libc
::AT_SYMLINK_NOFOLLOW
)
381 fn restore_mtime(&mut self, entry
: &CaFormatEntry
, fd
: RawFd
) -> Result
<(), Error
> {
383 let times
= nsec_to_update_timespec(entry
.mtime
);
385 let res
= unsafe { libc::futimens(fd, ×[0]) }
;
391 fn restore_mtime_at(&mut self, entry
: &CaFormatEntry
, dirfd
: RawFd
, filename
: &OsStr
) -> Result
<(), Error
> {
393 let times
= nsec_to_update_timespec(entry
.mtime
);
395 let res
= filename
.with_nix_path(|cstr
| unsafe {
396 libc
::utimensat(dirfd
, cstr
.as_ptr(), ×
[0], libc
::AT_SYMLINK_NOFOLLOW
)
403 fn restore_device_at(&mut self, entry
: &CaFormatEntry
, dirfd
: RawFd
, filename
: &OsStr
, device
: &CaFormatDevice
) -> Result
<(), Error
> {
405 let rdev
= nix
::sys
::stat
::makedev(device
.major
, device
.minor
);
406 let mode
= ((entry
.mode
as u32) & libc
::S_IFMT
) | 0o0600;
407 let res
= filename
.with_nix_path(|cstr
| unsafe {
408 libc
::mknodat(dirfd
, cstr
.as_ptr(), mode
, rdev
)
415 fn restore_socket_at(&mut self, dirfd
: RawFd
, filename
: &OsStr
) -> Result
<(), Error
> {
417 let mode
= libc
::S_IFSOCK
| 0o0600;
418 let res
= filename
.with_nix_path(|cstr
| unsafe {
419 libc
::mknodat(dirfd
, cstr
.as_ptr(), mode
, 0)
426 fn restore_fifo_at(&mut self, dirfd
: RawFd
, filename
: &OsStr
) -> Result
<(), Error
> {
428 let mode
= libc
::S_IFIFO
| 0o0600;
429 let res
= filename
.with_nix_path(|cstr
| unsafe {
430 libc
::mkfifoat(dirfd
, cstr
.as_ptr(), mode
)
437 fn skip_bytes(&mut self, count
: usize) -> Result
<(), Error
> {
440 let todo
= count
- done
;
441 let n
= if todo
> self.skip_buffer
.len() { self.skip_buffer.len() }
else { todo }
;
442 let data
= &mut self.skip_buffer
[..n
];
443 self.reader
.read_exact(data
)?
;
449 /// Restore an archive into the specified directory.
451 /// The directory is created if it does not exist.
456 ) -> Result
<(), Error
>
457 where F
: Fn(&Path
) -> Result
<(), Error
>
460 let _
= std
::fs
::create_dir(path
);
462 let dir
= match nix
::dir
::Dir
::open(path
, nix
::fcntl
::OFlag
::O_DIRECTORY
, nix
::sys
::stat
::Mode
::empty()) {
464 Err(err
) => bail
!("unable to open target directory {:?} - {}", path
, err
),
467 let mut relative_path
= PathBuf
::new();
468 self.restore_sequential(path
, &mut relative_path
, &OsString
::new(), &dir
, callback
)
471 fn restore_sequential
<F
>(
474 relative_path
: &mut PathBuf
,
475 filename
: &OsStr
, // repeats path last relative_path component
476 parent
: &nix
::dir
::Dir
,
478 ) -> Result
<(), Error
>
479 where F
: Fn(&Path
) -> Result
<(), Error
>
482 let parent_fd
= parent
.as_raw_fd();
484 let full_path
= base_path
.join(&relative_path
);
486 (callback
)(&full_path
)?
;
488 let head
: CaFormatHeader
= self.read_item()?
;
490 if head
.htype
== PXAR_FORMAT_HARDLINK
{
491 let (target
, _offset
) = self.read_hardlink(head
.size
)?
;
492 let target_path
= base_path
.join(&target
);
493 //println!("HARDLINK: {} {:?} -> {:?}", offset, full_path, target_path);
494 hardlink(&target_path
, &full_path
)?
;
498 check_ca_header
::<CaFormatEntry
>(&head
, CA_FORMAT_ENTRY
)?
;
499 let entry
: CaFormatEntry
= self.read_item()?
;
502 let mode
= entry
.mode
as u32; //fixme: upper 32bits?
504 let ifmt
= mode
& libc
::S_IFMT
;
506 if ifmt
== libc
::S_IFDIR
{
508 if filename
.is_empty() {
509 dir
= nix
::dir
::Dir
::openat(parent_fd
, ".", OFlag
::O_DIRECTORY
, Mode
::empty())?
;
511 dir
= match dir_mkdirat(parent_fd
, filename
, true) {
513 Err(err
) => bail
!("unable to open directory {:?} - {}", full_path
, err
),
517 self.restore_ugid(&entry
, dir
.as_raw_fd())?
;
518 // fcaps have to be restored after restore_ugid as chown clears security.capability xattr, see CVE-2015-1350
519 let mut head
= match self.restore_attributes(&entry
, dir
.as_raw_fd()) {
521 Err(err
) => bail
!("Restoring of directory attributes failed - {}", err
),
524 while head
.htype
== CA_FORMAT_FILENAME
{
525 let name
= self.read_filename(head
.size
)?
;
526 relative_path
.push(&name
);
527 self.restore_sequential(base_path
, relative_path
, &name
, &dir
, callback
)?
;
530 head
= self.read_item()?
;
533 if head
.htype
!= CA_FORMAT_GOODBYE
{
534 bail
!("got unknown header type inside directory entry {:016x}", head
.htype
);
537 //println!("Skip Goodbye");
538 if head
.size
< HEADER_SIZE { bail!("detected short goodbye table"); }
540 self.skip_bytes((head
.size
- HEADER_SIZE
) as usize)?
;
542 self.restore_mode(&entry
, dir
.as_raw_fd())?
;
543 self.restore_mtime(&entry
, dir
.as_raw_fd())?
;
548 if filename
.is_empty() {
549 bail
!("got empty file name at {:?}", full_path
)
552 if ifmt
== libc
::S_IFLNK
{
553 // fixme: create symlink
554 //fixme: restore permission, acls, xattr, ...
556 let head
: CaFormatHeader
= self.read_item()?
;
558 CA_FORMAT_SYMLINK
=> {
559 let target
= self.read_link(head
.size
)?
;
560 //println!("TARGET: {:?}", target);
561 if let Err(err
) = symlinkat(&target
, parent_fd
, filename
) {
562 bail
!("create symlink {:?} failed - {}", full_path
, err
);
566 bail
!("got unknown header type inside symlink entry {:016x}", head
.htype
);
570 // self.restore_mode_at(&entry, parent_fd, filename)?; //not supported on symlinks
571 self.restore_ugid_at(&entry
, parent_fd
, filename
)?
;
572 self.restore_mtime_at(&entry
, parent_fd
, filename
)?
;
577 if ifmt
== libc
::S_IFSOCK
{
579 self.restore_socket_at(parent_fd
, filename
)?
;
581 self.restore_mode_at(&entry
, parent_fd
, filename
)?
;
582 self.restore_ugid_at(&entry
, parent_fd
, filename
)?
;
583 self.restore_mtime_at(&entry
, parent_fd
, filename
)?
;
588 if ifmt
== libc
::S_IFIFO
{
590 self.restore_fifo_at(parent_fd
, filename
)?
;
592 self.restore_mode_at(&entry
, parent_fd
, filename
)?
;
593 self.restore_ugid_at(&entry
, parent_fd
, filename
)?
;
594 self.restore_mtime_at(&entry
, parent_fd
, filename
)?
;
599 if (ifmt
== libc
::S_IFBLK
) || (ifmt
== libc
::S_IFCHR
) {
601 let head
: CaFormatHeader
= self.read_item()?
;
603 CA_FORMAT_DEVICE
=> {
604 let device
: CaFormatDevice
= self.read_item()?
;
605 self.restore_device_at(&entry
, parent_fd
, filename
, &device
)?
;
608 bail
!("got unknown header type inside device entry {:016x}", head
.htype
);
612 self.restore_mode_at(&entry
, parent_fd
, filename
)?
;
613 self.restore_ugid_at(&entry
, parent_fd
, filename
)?
;
614 self.restore_mtime_at(&entry
, parent_fd
, filename
)?
;
619 if ifmt
== libc
::S_IFREG
{
621 let mut read_buffer
: [u8; 64*1024] = unsafe { std::mem::uninitialized() }
;
623 let flags
= OFlag
::O_CREAT
|OFlag
::O_WRONLY
|OFlag
::O_EXCL
;
624 let open_mode
= Mode
::from_bits_truncate(0o0600 | mode
);
626 let mut file
= match file_openat(parent_fd
, filename
, flags
, open_mode
) {
628 Err(err
) => bail
!("open file {:?} failed - {}", full_path
, err
),
631 self.restore_ugid(&entry
, file
.as_raw_fd())?
;
632 // fcaps have to be restored after restore_ugid as chown clears security.capability xattr, see CVE-2015-1350
633 let head
= match self.restore_attributes(&entry
, file
.as_raw_fd()) {
635 Err(err
) => bail
!("Restoring of file attributes failed - {}", err
),
638 if head
.htype
!= CA_FORMAT_PAYLOAD
{
639 bail
!("got unknown header type for file entry {:016x}", head
.htype
);
642 if head
.size
< HEADER_SIZE
{
643 bail
!("detected short payload");
645 let need
= (head
.size
- HEADER_SIZE
) as usize;
646 //self.reader.seek(SeekFrom::Current(need as i64))?;
650 let todo
= need
- done
;
651 let n
= if todo
> read_buffer
.len() { read_buffer.len() }
else { todo }
;
652 let data
= &mut read_buffer
[..n
];
653 self.reader
.read_exact(data
)?
;
654 file
.write_all(data
)?
;
658 self.restore_mode(&entry
, file
.as_raw_fd())?
;
659 self.restore_mtime(&entry
, file
.as_raw_fd())?
;
667 /// List/Dump archive content.
669 /// Simply print the list of contained files. This dumps archive
670 /// format details when the verbose flag is set (useful for debug).
671 pub fn dump_entry
<W
: std
::io
::Write
>(
676 ) -> Result
<(), Error
> {
678 let print_head
= |head
: &CaFormatHeader
| {
679 println
!("Type: {:016x}", head
.htype
);
680 println
!("Size: {}", head
.size
);
683 let head
: CaFormatHeader
= self.read_item()?
;
685 println
!("Path: {:?}", path
);
688 println
!("{:?}", path
);
691 if head
.htype
== PXAR_FORMAT_HARDLINK
{
692 let (target
, offset
) = self.read_hardlink(head
.size
)?
;
694 println
!("Hardlink: {} {:?}", offset
, target
);
699 check_ca_header
::<CaFormatEntry
>(&head
, CA_FORMAT_ENTRY
)?
;
700 let entry
: CaFormatEntry
= self.read_item()?
;
703 println
!("Mode: {:08x} {:08x}", entry
.mode
, (entry
.mode
as u32) & libc
::S_IFDIR
);
706 let ifmt
= (entry
.mode
as u32) & libc
::S_IFMT
;
708 if ifmt
== libc
::S_IFDIR
{
710 let mut entry_count
= 0;
713 let head
: CaFormatHeader
= self.read_item()?
;
718 // This call covers all the cases of the match statement
719 // regarding extended attributes. These calls will never
720 // break on the loop and can therefore be handled separately.
721 // If the header was matched, true is returned and we can continue
722 if self.dump_if_attribute(&head
, verbose
)?
{
727 CA_FORMAT_FILENAME
=> {
728 let name
= self.read_filename(head
.size
)?
;
729 if verbose { println!("Name: {:?}
", name); }
732 self.dump_entry(path, verbose, output)?;
735 CA_FORMAT_GOODBYE => {
736 let table_size = (head.size - HEADER_SIZE) as usize;
738 println!("Goodbye
: {:?}
", path);
739 self.dump_goodby_entries(entry_count, table_size)?;
741 self.skip_bytes(table_size)?;
745 _ => panic!("got unexpected header
type inside directory
"),
748 } else if (ifmt == libc::S_IFBLK) || (ifmt == libc::S_IFCHR) ||
749 (ifmt == libc::S_IFLNK) || (ifmt == libc::S_IFREG)
752 let head: CaFormatHeader = self.read_item()?;
757 // This call covers all the cases of the match statement
758 // regarding extended attributes. These calls will never
759 // break on the loop and can therefore be handled separately.
760 // If the header was matched, true is returned and we can continue
761 if self.dump_if_attribute(&head, verbose)? {
766 CA_FORMAT_SYMLINK => {
767 let target = self.read_link(head.size)?;
769 println!("Symlink
: {:?}
", target);
773 CA_FORMAT_DEVICE => {
774 let device: CaFormatDevice = self.read_item()?;
776 println!("Device
: {}
, {}
", device.major, device.minor);
780 CA_FORMAT_PAYLOAD => {
781 let payload_size = (head.size - HEADER_SIZE) as usize;
783 println!("Payload
: {}
", payload_size);
785 self.skip_bytes(payload_size)?;
789 panic!("got unexpected header
type inside non
-directory
");
793 } else if ifmt == libc::S_IFIFO {
797 } else if ifmt == libc::S_IFSOCK {
802 panic!("unknown st_mode
");
807 fn dump_if_attribute(&mut self, header: &CaFormatHeader, verbose: bool) -> Result<bool, Error> {
810 let xattr = self.read_xattr((header.size - HEADER_SIZE) as usize)?;
811 if verbose && self.has_features(CA_FORMAT_WITH_XATTRS) {
812 println!("XAttr
: {:?}
", xattr);
816 let fcaps = self.read_fcaps((header.size - HEADER_SIZE) as usize)?;
817 if verbose && self.has_features(CA_FORMAT_WITH_FCAPS) {
818 println!("FCaps
: {:?}
", fcaps);
821 CA_FORMAT_ACL_USER => {
822 let user = self.read_item::<CaFormatACLUser>()?;
823 if verbose && self.has_features(CA_FORMAT_WITH_ACL) {
824 println!("ACLUser
: {:?}
", user);
827 CA_FORMAT_ACL_GROUP => {
828 let group = self.read_item::<CaFormatACLGroup>()?;
829 if verbose && self.has_features(CA_FORMAT_WITH_ACL) {
830 println!("ACLGroup
: {:?}
", group);
833 CA_FORMAT_ACL_GROUP_OBJ => {
834 let group_obj = self.read_item::<CaFormatACLGroupObj>()?;
835 if verbose && self.has_features(CA_FORMAT_WITH_ACL) {
836 println!("ACLGroupObj
: {:?}
", group_obj);
839 CA_FORMAT_ACL_DEFAULT => {
840 let default = self.read_item::<CaFormatACLDefault>()?;
841 if verbose && self.has_features(CA_FORMAT_WITH_ACL) {
842 println!("ACLDefault
: {:?}
", default);
845 CA_FORMAT_ACL_DEFAULT_USER => {
846 let default_user = self.read_item::<CaFormatACLUser>()?;
847 if verbose && self.has_features(CA_FORMAT_WITH_ACL) {
848 println!("ACLDefaultUser
: {:?}
", default_user);
851 CA_FORMAT_ACL_DEFAULT_GROUP => {
852 let default_group = self.read_item::<CaFormatACLGroup>()?;
853 if verbose && self.has_features(CA_FORMAT_WITH_ACL) {
854 println!("ACLDefaultGroup
: {:?}
", default_group);
857 CA_FORMAT_QUOTA_PROJID => {
858 let quota_projid = self.read_item::<CaFormatQuotaProjID>()?;
859 if verbose && self.has_features(CA_FORMAT_WITH_QUOTA_PROJID) {
860 println!("Quota project id
: {:?}
", quota_projid);
863 _ => return Ok(false),
869 fn dump_goodby_entries(
873 ) -> Result<(), Error> {
875 const GOODBYE_ITEM_SIZE: usize = std::mem::size_of::<CaFormatGoodbyeItem>();
877 if table_size < GOODBYE_ITEM_SIZE {
878 bail!("Goodbye table to
small ({}
< {}
)", table_size, GOODBYE_ITEM_SIZE);
880 if (table_size % GOODBYE_ITEM_SIZE) != 0 {
881 bail!("Goodbye table with strange
size ({}
)", table_size);
884 let entries = table_size / GOODBYE_ITEM_SIZE;
886 if entry_count != (entries - 1) {
887 bail!("Goodbye table with wrong entry
count ({}
!= {}
)", entry_count, entries - 1);
893 let item: CaFormatGoodbyeItem = self.read_item()?;
895 if item.hash == CA_FORMAT_GOODBYE_TAIL_MARKER {
896 if count != entries {
897 bail!("unexpected goodbye tail marker
");
899 println!("Goodby tail mark
.");
902 println!("Goodby item
: offset {}
, size {}
, hash {:016x}
", item.offset, item.size, item.hash);
903 if count >= entries {
904 bail!("too many goodbye
items (no tail marker
)");
912 fn file_openat(parent: RawFd, filename: &OsStr, flags: OFlag, mode: Mode) -> Result<std::fs::File, Error> {
914 let fd = filename.with_nix_path(|cstr| {
915 nix::fcntl::openat(parent, cstr.as_ref(), flags, mode)
918 let file = unsafe { std::fs::File::from_raw_fd(fd) };
923 fn dir_mkdirat(parent: RawFd, filename: &OsStr, create_new: bool) -> Result<nix::dir::Dir, nix::Error> {
925 // call mkdirat first
926 let res = filename.with_nix_path(|cstr| unsafe {
927 libc::mkdirat(parent, cstr.as_ptr(), libc::S_IRWXU)
930 match Errno::result(res) {
933 if err == nix::Error::Sys(nix::errno::Errno::EEXIST) {
934 if create_new { return Err(err); }
941 let dir = nix::dir::Dir::openat(parent, filename, OFlag::O_DIRECTORY, Mode::empty())?;
946 fn hardlink(oldpath: &Path, newpath: &Path) -> Result<(), Error> {
947 oldpath.with_nix_path(|oldpath| {
948 newpath.with_nix_path(|newpath| {
949 let res = unsafe { libc::link(oldpath.as_ptr(), newpath.as_ptr()) };
956 fn symlinkat(target: &Path, parent: RawFd, linkname: &OsStr) -> Result<(), Error> {
958 target.with_nix_path(|target| {
959 linkname.with_nix_path(|linkname| {
960 let res = unsafe { libc::symlinkat(target.as_ptr(), parent, linkname.as_ptr()) };
967 fn nsec_to_update_timespec(mtime_nsec: u64) -> [libc::timespec; 2] {
970 const UTIME_OMIT: i64 = ((1 << 30) - 2);
971 const NANOS_PER_SEC: i64 = 1_000_000_000;
973 let sec = (mtime_nsec as i64) / NANOS_PER_SEC;
974 let nsec = (mtime_nsec as i64) % NANOS_PER_SEC;
976 let times: [libc::timespec; 2] = [
977 libc::timespec { tv_sec: 0, tv_nsec: UTIME_OMIT },
978 libc::timespec { tv_sec: sec, tv_nsec: nsec },
984 fn mode_user_to_acl_permissions(mode: u64) -> u64 {
985 return (mode >> 6) & 7;
988 fn mode_group_to_acl_permissions(mode: u64) -> u64 {
989 return (mode >> 3) & 7;
992 fn mode_other_to_acl_permissions(mode: u64) -> u64 {