1 use std
::collections
::{HashSet, HashMap}
;
2 use std
::convert
::TryFrom
;
3 use std
::ffi
::{CStr, CString, OsStr}
;
5 use std
::io
::{self, Read, Write}
;
6 use std
::os
::unix
::ffi
::OsStrExt
;
7 use std
::os
::unix
::io
::{AsRawFd, FromRawFd, IntoRawFd, RawFd}
;
8 use std
::path
::{Path, PathBuf}
;
10 use anyhow
::{bail, format_err, Error}
;
12 use nix
::errno
::Errno
;
13 use nix
::fcntl
::OFlag
;
14 use nix
::sys
::stat
::{FileStat, Mode}
;
16 use pathpatterns
::{MatchEntry, MatchList, MatchType, PatternFlag}
;
18 use pxar
::encoder
::LinkOffset
;
21 use proxmox
::sys
::error
::SysError
;
22 use proxmox
::tools
::fd
::RawFdNum
;
23 use proxmox
::tools
::vec
;
25 use crate::pxar
::catalog
::BackupCatalogWriter
;
26 use crate::pxar
::Flags
;
27 use crate::pxar
::tools
::assert_single_path_component
;
28 use crate::tools
::{acl, fs, xattr, Fd}
;
30 fn detect_fs_type(fd
: RawFd
) -> Result
<i64, Error
> {
31 let mut fs_stat
= std
::mem
::MaybeUninit
::uninit();
32 let res
= unsafe { libc::fstatfs(fd, fs_stat.as_mut_ptr()) }
;
34 let fs_stat
= unsafe { fs_stat.assume_init() }
;
40 pub fn is_virtual_file_system(magic
: i64) -> bool
{
41 use proxmox
::sys
::linux
::magic
::*;
51 FUSE_CTL_SUPER_MAGIC
|
73 fn new(path
: PathBuf
, error
: Error
) -> Self {
78 impl std
::error
::Error
for ArchiveError {}
80 impl fmt
::Display
for ArchiveError
{
81 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
82 write
!(f
, "error at {:?}: {}", self.path
, self.error
)
86 #[derive(Eq, PartialEq, Hash)]
92 /// In case we want to collect them or redirect them we can just add this here:
95 impl std
::io
::Write
for ErrorReporter
{
96 fn write(&mut self, data
: &[u8]) -> io
::Result
<usize> {
97 std
::io
::stderr().write(data
)
100 fn flush(&mut self) -> io
::Result
<()> {
101 std
::io
::stderr().flush()
105 struct Archiver
<'a
, 'b
> {
106 feature_flags
: Flags
,
107 fs_feature_flags
: Flags
,
109 patterns
: Vec
<MatchEntry
>,
110 callback
: &'a
mut dyn FnMut(&Path
) -> Result
<(), Error
>,
111 catalog
: Option
<&'b
mut dyn BackupCatalogWriter
>,
113 entry_counter
: usize,
115 current_st_dev
: libc
::dev_t
,
116 device_set
: Option
<HashSet
<u64>>,
117 hardlinks
: HashMap
<HardLinkInfo
, (PathBuf
, LinkOffset
)>,
118 errors
: ErrorReporter
,
119 file_copy_buffer
: Vec
<u8>,
122 type Encoder
<'a
, 'b
> = pxar
::encoder
::Encoder
<'a
, &'b
mut dyn pxar
::encoder
::SeqWrite
>;
124 pub fn create_archive
<T
, F
>(
127 mut patterns
: Vec
<MatchEntry
>,
128 feature_flags
: Flags
,
129 mut device_set
: Option
<HashSet
<u64>>,
130 skip_lost_and_found
: bool
,
133 catalog
: Option
<&mut dyn BackupCatalogWriter
>,
134 ) -> Result
<(), Error
>
136 T
: pxar
::encoder
::SeqWrite
,
137 F
: FnMut(&Path
) -> Result
<(), Error
>,
139 let fs_magic
= detect_fs_type(source_dir
.as_raw_fd())?
;
140 if is_virtual_file_system(fs_magic
) {
141 bail
!("refusing to backup a virtual file system");
144 let fs_feature_flags
= Flags
::from_magic(fs_magic
);
146 let stat
= nix
::sys
::stat
::fstat(source_dir
.as_raw_fd())?
;
147 let metadata
= get_metadata(
148 source_dir
.as_raw_fd(),
150 feature_flags
& fs_feature_flags
,
153 .map_err(|err
| format_err
!("failed to get metadata for source directory: {}", err
))?
;
155 if let Some(ref mut set
) = device_set
{
156 set
.insert(stat
.st_dev
);
159 let writer
= &mut writer
as &mut dyn pxar
::encoder
::SeqWrite
;
160 let mut encoder
= Encoder
::new(writer
, &metadata
)?
;
162 if skip_lost_and_found
{
163 patterns
.push(MatchEntry
::parse_pattern(
165 PatternFlag
::PATH_NAME
,
170 let mut archiver
= Archiver
{
174 callback
: &mut callback
,
177 path
: PathBuf
::new(),
180 current_st_dev
: stat
.st_dev
,
182 hardlinks
: HashMap
::new(),
183 errors
: ErrorReporter
,
184 file_copy_buffer
: vec
::undefined(4 * 1024 * 1024),
187 archiver
.archive_dir_contents(&mut encoder
, source_dir
, true)?
;
192 struct FileListEntry
{
198 impl<'a
, 'b
> Archiver
<'a
, 'b
> {
199 /// Get the currently effective feature flags. (Requested flags masked by the file system
201 fn flags(&self) -> Flags
{
202 self.feature_flags
& self.fs_feature_flags
205 fn wrap_err(&self, err
: Error
) -> Error
{
206 if err
.downcast_ref
::<ArchiveError
>().is_some() {
209 ArchiveError
::new(self.path
.clone(), err
).into()
213 fn archive_dir_contents(
215 encoder
: &mut Encoder
,
218 ) -> Result
<(), Error
> {
219 let entry_counter
= self.entry_counter
;
221 let old_patterns_count
= self.patterns
.len();
222 self.read_pxar_excludes(dir
.as_raw_fd())?
;
224 let file_list
= self.generate_directory_file_list(&mut dir
, is_root
)?
;
226 let dir_fd
= dir
.as_raw_fd();
228 let old_path
= std
::mem
::take(&mut self.path
);
230 for file_entry
in file_list
{
231 let file_name
= file_entry
.name
.to_bytes();
233 if is_root
&& file_name
== b
".pxarexclude-cli" {
234 self.encode_pxarexclude_cli(encoder
, &file_entry
.name
)?
;
238 (self.callback
)(Path
::new(OsStr
::from_bytes(file_name
)))?
;
239 self.path
= file_entry
.path
;
240 self.add_entry(encoder
, dir_fd
, &file_entry
.name
, &file_entry
.stat
)
241 .map_err(|err
| self.wrap_err(err
))?
;
243 self.path
= old_path
;
244 self.entry_counter
= entry_counter
;
245 self.patterns
.truncate(old_patterns_count
);
250 /// openat() wrapper which allows but logs `EACCES` and turns `ENOENT` into `None`.
252 /// The `existed` flag is set when iterating through a directory to note that we know the file
253 /// is supposed to exist and we should warn if it doesnt'.
260 ) -> Result
<Option
<Fd
>, Error
> {
262 &unsafe { RawFdNum::from_raw_fd(parent) }
,
267 Ok(fd
) => Ok(Some(fd
)),
268 Err(nix
::Error
::Sys(Errno
::ENOENT
)) => {
270 self.report_vanished_file()?
;
274 Err(nix
::Error
::Sys(Errno
::EACCES
)) => {
275 writeln
!(self.errors
, "failed to open file: {:?}: access denied", file_name
)?
;
278 Err(other
) => Err(Error
::from(other
)),
282 fn read_pxar_excludes(&mut self, parent
: RawFd
) -> Result
<(), Error
> {
283 let fd
= self.open_file(
285 c_str
!(".pxarexclude"),
286 OFlag
::O_RDONLY
| OFlag
::O_CLOEXEC
| OFlag
::O_NOCTTY
,
290 let old_pattern_count
= self.patterns
.len();
292 if let Some(fd
) = fd
{
293 let file
= unsafe { std::fs::File::from_raw_fd(fd.into_raw_fd()) }
;
296 for line
in io
::BufReader
::new(file
).lines() {
297 let line
= match line
{
302 "ignoring .pxarexclude after read error in {:?}: {}",
306 self.patterns
.truncate(old_pattern_count
);
311 let line
= line
.trim();
313 if line
.is_empty() || line
.starts_with('
#') {
317 match MatchEntry
::parse_pattern(line
, PatternFlag
::PATH_NAME
, MatchType
::Exclude
) {
318 Ok(pattern
) => self.patterns
.push(pattern
),
320 let _
= writeln
!(self.errors
, "bad pattern in {:?}: {}", self.path
, err
);
329 fn encode_pxarexclude_cli(
331 encoder
: &mut Encoder
,
333 ) -> Result
<(), Error
> {
334 let content
= generate_pxar_excludes_cli(&self.patterns
);
336 if let Some(ref mut catalog
) = self.catalog
{
337 catalog
.add_file(file_name
, content
.len() as u64, 0)?
;
340 let mut metadata
= Metadata
::default();
341 metadata
.stat
.mode
= pxar
::format
::mode
::IFREG
| 0o600;
343 let mut file
= encoder
.create_file(&metadata
, ".pxarexclude-cli", content
.len() as u64)?
;
344 file
.write_all(&content
)?
;
349 fn generate_directory_file_list(
353 ) -> Result
<Vec
<FileListEntry
>, Error
> {
354 let dir_fd
= dir
.as_raw_fd();
356 let mut file_list
= Vec
::new();
358 if is_root
&& !self.patterns
.is_empty() {
359 file_list
.push(FileListEntry
{
360 name
: CString
::new(".pxarexclude-cli").unwrap(),
361 path
: PathBuf
::new(),
362 stat
: unsafe { std::mem::zeroed() }
,
366 for file
in dir
.iter() {
369 let file_name
= file
.file_name().to_owned();
370 let file_name_bytes
= file_name
.to_bytes();
371 if file_name_bytes
== b
"." || file_name_bytes
== b
".." {
375 if is_root
&& file_name_bytes
== b
".pxarexclude-cli" {
379 if file_name_bytes
== b
".pxarexclude" {
383 let os_file_name
= OsStr
::from_bytes(file_name_bytes
);
384 assert_single_path_component(os_file_name
)?
;
385 let full_path
= self.path
.join(os_file_name
);
387 let stat
= match nix
::sys
::stat
::fstatat(
389 file_name
.as_c_str(),
390 nix
::fcntl
::AtFlags
::AT_SYMLINK_NOFOLLOW
,
393 Err(ref err
) if err
.not_found() => continue,
394 Err(err
) => bail
!("stat failed on {:?}: {}", full_path
, err
),
399 .matches(full_path
.as_os_str().as_bytes(), Some(stat
.st_mode
as u32))
400 == Some(MatchType
::Exclude
)
405 self.entry_counter
+= 1;
406 if self.entry_counter
> self.entry_limit
{
407 bail
!("exceeded allowed number of file entries (> {})",self.entry_limit
);
410 file_list
.push(FileListEntry
{
417 file_list
.sort_unstable_by(|a
, b
| a
.name
.cmp(&b
.name
));
422 fn report_vanished_file(&mut self) -> Result
<(), Error
> {
423 writeln
!(self.errors
, "warning: file vanished while reading: {:?}", self.path
)?
;
427 fn report_file_shrunk_while_reading(&mut self) -> Result
<(), Error
> {
430 "warning: file size shrunk while reading: {:?}, file will be padded with zeros!",
436 fn report_file_grew_while_reading(&mut self) -> Result
<(), Error
> {
439 "warning: file size increased while reading: {:?}, file will be truncated!",
447 encoder
: &mut Encoder
,
451 ) -> Result
<(), Error
> {
452 use pxar
::format
::mode
;
454 let file_mode
= stat
.st_mode
& libc
::S_IFMT
;
455 let open_mode
= if file_mode
== libc
::S_IFREG
|| file_mode
== libc
::S_IFDIR
{
461 let fd
= self.open_file(
464 open_mode
| OFlag
::O_RDONLY
| OFlag
::O_NOFOLLOW
| OFlag
::O_CLOEXEC
| OFlag
::O_NOCTTY
,
470 None
=> return Ok(()),
473 let metadata
= get_metadata(fd
.as_raw_fd(), &stat
, self.flags(), self.fs_magic
)?
;
477 .matches(self.path
.as_os_str().as_bytes(), Some(stat
.st_mode
as u32))
478 == Some(MatchType
::Exclude
)
483 let file_name
: &Path
= OsStr
::from_bytes(c_file_name
.to_bytes()).as_ref();
484 match metadata
.file_type() {
486 let link_info
= HardLinkInfo
{
491 if stat
.st_nlink
> 1 {
492 if let Some((path
, offset
)) = self.hardlinks
.get(&link_info
) {
493 if let Some(ref mut catalog
) = self.catalog
{
494 catalog
.add_hardlink(c_file_name
)?
;
497 encoder
.add_hardlink(file_name
, path
, *offset
)?
;
503 let file_size
= stat
.st_size
as u64;
504 if let Some(ref mut catalog
) = self.catalog
{
505 catalog
.add_file(c_file_name
, file_size
, stat
.st_mtime
as u64)?
;
508 let offset
: LinkOffset
=
509 self.add_regular_file(encoder
, fd
, file_name
, &metadata
, file_size
)?
;
511 if stat
.st_nlink
> 1 {
512 self.hardlinks
.insert(link_info
, (self.path
.clone(), offset
));
518 let dir
= Dir
::from_fd(fd
.into_raw_fd())?
;
520 if let Some(ref mut catalog
) = self.catalog
{
521 catalog
.start_directory(c_file_name
)?
;
523 let result
= self.add_directory(encoder
, dir
, c_file_name
, &metadata
, stat
);
524 if let Some(ref mut catalog
) = self.catalog
{
525 catalog
.end_directory()?
;
530 if let Some(ref mut catalog
) = self.catalog
{
531 catalog
.add_socket(c_file_name
)?
;
534 Ok(encoder
.add_socket(&metadata
, file_name
)?
)
537 if let Some(ref mut catalog
) = self.catalog
{
538 catalog
.add_fifo(c_file_name
)?
;
541 Ok(encoder
.add_fifo(&metadata
, file_name
)?
)
544 if let Some(ref mut catalog
) = self.catalog
{
545 catalog
.add_symlink(c_file_name
)?
;
548 self.add_symlink(encoder
, fd
, file_name
, &metadata
)
551 if let Some(ref mut catalog
) = self.catalog
{
552 catalog
.add_block_device(c_file_name
)?
;
555 self.add_device(encoder
, file_name
, &metadata
, &stat
)
558 if let Some(ref mut catalog
) = self.catalog
{
559 catalog
.add_char_device(c_file_name
)?
;
562 self.add_device(encoder
, file_name
, &metadata
, &stat
)
565 "encountered unknown file type: 0x{:x} (0o{:o})",
574 encoder
: &mut Encoder
,
579 ) -> Result
<(), Error
> {
580 let dir_name
= OsStr
::from_bytes(dir_name
.to_bytes());
582 let mut encoder
= encoder
.create_directory(dir_name
, &metadata
)?
;
584 let old_fs_magic
= self.fs_magic
;
585 let old_fs_feature_flags
= self.fs_feature_flags
;
586 let old_st_dev
= self.current_st_dev
;
588 let mut skip_contents
= false;
589 if old_st_dev
!= stat
.st_dev
{
590 self.fs_magic
= detect_fs_type(dir
.as_raw_fd())?
;
591 self.fs_feature_flags
= Flags
::from_magic(self.fs_magic
);
592 self.current_st_dev
= stat
.st_dev
;
594 if is_virtual_file_system(self.fs_magic
) {
595 skip_contents
= true;
596 } else if let Some(set
) = &self.device_set
{
597 skip_contents
= !set
.contains(&stat
.st_dev
);
601 let result
= if skip_contents
{
604 self.archive_dir_contents(&mut encoder
, dir
, false)
607 self.fs_magic
= old_fs_magic
;
608 self.fs_feature_flags
= old_fs_feature_flags
;
609 self.current_st_dev
= old_st_dev
;
617 encoder
: &mut Encoder
,
622 ) -> Result
<LinkOffset
, Error
> {
623 let mut file
= unsafe { std::fs::File::from_raw_fd(fd.into_raw_fd()) }
;
624 let mut remaining
= file_size
;
625 let mut out
= encoder
.create_file(metadata
, file_name
, file_size
)?
;
626 while remaining
!= 0 {
627 let mut got
= file
.read(&mut self.file_copy_buffer
[..])?
;
628 if got
as u64 > remaining
{
629 self.report_file_grew_while_reading()?
;
630 got
= remaining
as usize;
632 out
.write_all(&self.file_copy_buffer
[..got
])?
;
633 remaining
-= got
as u64;
636 self.report_file_shrunk_while_reading()?
;
637 let to_zero
= remaining
.min(self.file_copy_buffer
.len() as u64) as usize;
638 vec
::clear(&mut self.file_copy_buffer
[..to_zero
]);
639 while remaining
!= 0 {
640 let fill
= remaining
.min(self.file_copy_buffer
.len() as u64) as usize;
641 out
.write_all(&self.file_copy_buffer
[..fill
])?
;
642 remaining
-= fill
as u64;
646 Ok(out
.file_offset())
651 encoder
: &mut Encoder
,
655 ) -> Result
<(), Error
> {
656 let dest
= nix
::fcntl
::readlinkat(fd
.as_raw_fd(), &b
""[..])?
;
657 encoder
.add_symlink(metadata
, file_name
, dest
)?
;
663 encoder
: &mut Encoder
,
667 ) -> Result
<(), Error
> {
668 Ok(encoder
.add_device(
671 pxar
::format
::Device
::from_dev_t(stat
.st_rdev
),
676 fn get_metadata(fd
: RawFd
, stat
: &FileStat
, flags
: Flags
, fs_magic
: i64) -> Result
<Metadata
, Error
> {
677 // required for some of these
678 let proc_path
= Path
::new("/proc/self/fd/").join(fd
.to_string());
680 let mtime
= u64::try_from(stat
.st_mtime
* 1_000_000_000 + stat
.st_mtime_nsec
)
681 .map_err(|_
| format_err
!("file with negative mtime"))?
;
683 let mut meta
= Metadata
{
685 mode
: u64::from(stat
.st_mode
),
694 get_xattr_fcaps_acl(&mut meta
, fd
, &proc_path
, flags
)?
;
695 get_chattr(&mut meta
, fd
)?
;
696 get_fat_attr(&mut meta
, fd
, fs_magic
)?
;
697 get_quota_project_id(&mut meta
, fd
, flags
, fs_magic
)?
;
701 fn errno_is_unsupported(errno
: Errno
) -> bool
{
703 Errno
::ENOTTY
| Errno
::ENOSYS
| Errno
::EBADF
| Errno
::EOPNOTSUPP
| Errno
::EINVAL
=> true,
708 fn get_fcaps(meta
: &mut Metadata
, fd
: RawFd
, flags
: Flags
) -> Result
<(), Error
> {
709 if flags
.contains(Flags
::WITH_FCAPS
) {
713 match xattr
::fgetxattr(fd
, xattr
::xattr_name_fcaps()) {
715 meta
.fcaps
= Some(pxar
::format
::FCaps { data }
);
718 Err(Errno
::ENODATA
) => Ok(()),
719 Err(Errno
::EOPNOTSUPP
) => Ok(()),
720 Err(Errno
::EBADF
) => Ok(()), // symlinks
721 Err(err
) => bail
!("failed to read file capabilities: {}", err
),
725 fn get_xattr_fcaps_acl(
730 ) -> Result
<(), Error
> {
731 if flags
.contains(Flags
::WITH_XATTRS
) {
735 let xattrs
= match xattr
::flistxattr(fd
) {
737 Err(Errno
::EOPNOTSUPP
) => return Ok(()),
738 Err(Errno
::EBADF
) => return Ok(()), // symlinks
739 Err(err
) => bail
!("failed to read xattrs: {}", err
),
742 for attr
in &xattrs
{
743 if xattr
::is_security_capability(&attr
) {
744 get_fcaps(meta
, fd
, flags
)?
;
748 if xattr
::is_acl(&attr
) {
749 get_acl(meta
, proc_path
, flags
)?
;
753 if !xattr
::is_valid_xattr_name(&attr
) {
757 match xattr
::fgetxattr(fd
, attr
) {
760 .push(pxar
::format
::XAttr
::new(attr
.to_bytes(), data
)),
761 Err(Errno
::ENODATA
) => (), // it got removed while we were iterating...
762 Err(Errno
::EOPNOTSUPP
) => (), // shouldn't be possible so just ignore this
763 Err(Errno
::EBADF
) => (), // symlinks, shouldn't be able to reach this either
764 Err(err
) => bail
!("error reading extended attribute {:?}: {}", attr
, err
),
771 fn get_chattr(metadata
: &mut Metadata
, fd
: RawFd
) -> Result
<(), Error
> {
772 let mut attr
: usize = 0;
774 match unsafe { fs::read_attr_fd(fd, &mut attr) }
{
776 Err(nix
::Error
::Sys(errno
)) if errno_is_unsupported(errno
) => {
779 Err(err
) => bail
!("failed to read file attributes: {}", err
),
782 metadata
.stat
.flags
|= Flags
::from_chattr(attr
as u32).bits();
787 fn get_fat_attr(metadata
: &mut Metadata
, fd
: RawFd
, fs_magic
: i64) -> Result
<(), Error
> {
788 use proxmox
::sys
::linux
::magic
::*;
790 if fs_magic
!= MSDOS_SUPER_MAGIC
&& fs_magic
!= FUSE_SUPER_MAGIC
{
794 let mut attr
: u32 = 0;
796 match unsafe { fs::read_fat_attr_fd(fd, &mut attr) }
{
798 Err(nix
::Error
::Sys(errno
)) if errno_is_unsupported(errno
) => {
801 Err(err
) => bail
!("failed to read fat attributes: {}", err
),
804 metadata
.stat
.flags
|= Flags
::from_fat_attr(attr
).bits();
809 /// Read the quota project id for an inode, supported on ext4/XFS/FUSE/ZFS filesystems
810 fn get_quota_project_id(
811 metadata
: &mut Metadata
,
815 ) -> Result
<(), Error
> {
816 if !(metadata
.is_dir() || metadata
.is_regular_file()) {
820 if flags
.contains(Flags
::WITH_QUOTA_PROJID
) {
824 use proxmox
::sys
::linux
::magic
::*;
827 EXT4_SUPER_MAGIC
| XFS_SUPER_MAGIC
| FUSE_SUPER_MAGIC
| ZFS_SUPER_MAGIC
=> (),
831 let mut fsxattr
= fs
::FSXAttr
::default();
832 let res
= unsafe { fs::fs_ioc_fsgetxattr(fd, &mut fsxattr) }
;
834 // On some FUSE filesystems it can happen that ioctl is not supported.
835 // For these cases projid is set to 0 while the error is ignored.
836 if let Err(err
) = res
{
839 .ok_or_else(|| format_err
!("error while reading quota project id"))?
;
840 if errno_is_unsupported(errno
) {
843 bail
!("error while reading quota project id ({})", errno
);
847 let projid
= fsxattr
.fsx_projid
as u64;
849 metadata
.quota_project_id
= Some(pxar
::format
::QuotaProjectId { projid }
);
854 fn get_acl(metadata
: &mut Metadata
, proc_path
: &Path
, flags
: Flags
) -> Result
<(), Error
> {
855 if flags
.contains(Flags
::WITH_ACL
) {
859 if metadata
.is_symlink() {
863 get_acl_do(metadata
, proc_path
, acl
::ACL_TYPE_ACCESS
)?
;
865 if metadata
.is_dir() {
866 get_acl_do(metadata
, proc_path
, acl
::ACL_TYPE_DEFAULT
)?
;
873 metadata
: &mut Metadata
,
875 acl_type
: acl
::ACLType
,
876 ) -> Result
<(), Error
> {
877 // In order to be able to get ACLs with type ACL_TYPE_DEFAULT, we have
878 // to create a path for acl_get_file(). acl_get_fd() only allows to get
879 // ACL_TYPE_ACCESS attributes.
880 let acl
= match acl
::ACL
::get_file(&proc_path
, acl_type
) {
882 // Don't bail if underlying endpoint does not support acls
883 Err(Errno
::EOPNOTSUPP
) => return Ok(()),
884 // Don't bail if the endpoint cannot carry acls
885 Err(Errno
::EBADF
) => return Ok(()),
886 // Don't bail if there is no data
887 Err(Errno
::ENODATA
) => return Ok(()),
888 Err(err
) => bail
!("error while reading ACL - {}", err
),
891 process_acl(metadata
, acl
, acl_type
)
895 metadata
: &mut Metadata
,
897 acl_type
: acl
::ACLType
,
898 ) -> Result
<(), Error
> {
899 use pxar
::format
::acl
as pxar_acl
;
900 use pxar
::format
::acl
::{Group, GroupObject, Permissions, User}
;
902 let mut acl_user
= Vec
::new();
903 let mut acl_group
= Vec
::new();
904 let mut acl_group_obj
= None
;
905 let mut acl_default
= None
;
906 let mut user_obj_permissions
= None
;
907 let mut group_obj_permissions
= None
;
908 let mut other_permissions
= None
;
909 let mut mask_permissions
= None
;
911 for entry
in &mut acl
.entries() {
912 let tag
= entry
.get_tag_type()?
;
913 let permissions
= entry
.get_permissions()?
;
915 acl
::ACL_USER_OBJ
=> user_obj_permissions
= Some(Permissions(permissions
)),
916 acl
::ACL_GROUP_OBJ
=> group_obj_permissions
= Some(Permissions(permissions
)),
917 acl
::ACL_OTHER
=> other_permissions
= Some(Permissions(permissions
)),
918 acl
::ACL_MASK
=> mask_permissions
= Some(Permissions(permissions
)),
921 uid
: entry
.get_qualifier()?
,
922 permissions
: Permissions(permissions
),
926 acl_group
.push(Group
{
927 gid
: entry
.get_qualifier()?
,
928 permissions
: Permissions(permissions
),
931 _
=> bail
!("Unexpected ACL tag encountered!"),
939 acl
::ACL_TYPE_ACCESS
=> {
940 // The mask permissions are mapped to the stat group permissions
941 // in case that the ACL group permissions were set.
942 // Only in that case we need to store the group permissions,
943 // in the other cases they are identical to the stat group permissions.
944 if let (Some(gop
), true) = (group_obj_permissions
, mask_permissions
.is_some()) {
945 acl_group_obj
= Some(GroupObject { permissions: gop }
);
948 metadata
.acl
.users
= acl_user
;
949 metadata
.acl
.groups
= acl_group
;
951 acl
::ACL_TYPE_DEFAULT
=> {
952 if user_obj_permissions
!= None
953 || group_obj_permissions
!= None
954 || other_permissions
!= None
955 || mask_permissions
!= None
957 acl_default
= Some(pxar_acl
::Default
{
958 // The value is set to UINT64_MAX as placeholder if one
959 // of the permissions is not set
960 user_obj_permissions
: user_obj_permissions
.unwrap_or(Permissions
::NO_MASK
),
961 group_obj_permissions
: group_obj_permissions
.unwrap_or(Permissions
::NO_MASK
),
962 other_permissions
: other_permissions
.unwrap_or(Permissions
::NO_MASK
),
963 mask_permissions
: mask_permissions
.unwrap_or(Permissions
::NO_MASK
),
967 metadata
.acl
.default_users
= acl_user
;
968 metadata
.acl
.default_groups
= acl_group
;
970 _
=> bail
!("Unexpected ACL type encountered"),
973 metadata
.acl
.group_obj
= acl_group_obj
;
974 metadata
.acl
.default = acl_default
;
979 /// Note that our pattern lists are "positive". `MatchType::Include` means the file is included.
980 /// Since we are generating an *exclude* list, we need to invert this, so includes get a `'!'`
982 fn generate_pxar_excludes_cli(patterns
: &[MatchEntry
]) -> Vec
<u8> {
983 use pathpatterns
::{MatchFlag, MatchPattern}
;
985 let mut content
= Vec
::new();
987 for pattern
in patterns
{
988 match pattern
.match_type() {
989 MatchType
::Include
=> content
.push(b'
!'
),
990 MatchType
::Exclude
=> (),
993 match pattern
.pattern() {
994 MatchPattern
::Literal(lit
) => content
.extend(lit
),
995 MatchPattern
::Pattern(pat
) => content
.extend(pat
.pattern().to_bytes()),
998 if pattern
.match_flags() == MatchFlag
::MATCH_DIRECTORIES
&& content
.last() != Some(&b'
/'
) {
1002 content
.push(b'
\n'
);