1 use std
::ffi
::{CStr, CString}
;
2 use std
::os
::unix
::io
::{AsRawFd, FromRawFd, RawFd}
;
5 use anyhow
::{bail, format_err, Error}
;
8 use nix
::sys
::stat
::Mode
;
12 use proxmox
::c_result
;
13 use proxmox
::sys
::error
::SysError
;
14 use proxmox
::tools
::fd
::RawFdNum
;
18 use crate::pxar
::tools
::perms_from_metadata
;
19 use crate::pxar
::Flags
;
20 use crate::tools
::{acl, xattr}
;
26 fn allow_notsupp
<E
: SysError
>(err
: E
) -> Result
<(), E
> {
27 if err
.is_errno(Errno
::EOPNOTSUPP
) {
34 fn allow_notsupp_remember
<E
: SysError
>(err
: E
, not_supp
: &mut bool
) -> Result
<(), E
> {
35 if err
.is_errno(Errno
::EOPNOTSUPP
) {
43 fn timestamp_to_update_timespec(mtime
: &pxar
::format
::StatxTimestamp
) -> [libc
::timespec
; 2] {
45 const UTIME_OMIT
: i64 = (1 << 30) - 2;
54 tv_nsec
: mtime
.nanos
as _
,
60 // metadata application:
69 on_error
: &mut (dyn FnMut(Error
) -> Result
<(), Error
> + Send
),
70 ) -> Result
<(), Error
> {
71 let fd
= proxmox
::tools
::fd
::Fd
::openat(
72 &unsafe { RawFdNum::from_raw_fd(parent) }
,
74 OFlag
::O_PATH
| OFlag
::O_CLOEXEC
| OFlag
::O_NOFOLLOW
,
78 apply(flags
, metadata
, fd
.as_raw_fd(), path_info
, on_error
)
81 pub fn apply_initial_flags(
85 on_error
: &mut (dyn FnMut(Error
) -> Result
<(), Error
> + Send
),
86 ) -> Result
<(), Error
> {
87 let entry_flags
= Flags
::from_bits_truncate(metadata
.stat
.flags
);
90 entry_flags
.to_initial_chattr(),
91 flags
.to_initial_chattr(),
102 on_error
: &mut (dyn FnMut(Error
) -> Result
<(), Error
> + Send
),
103 ) -> Result
<(), Error
> {
104 let c_proc_path
= CString
::new(format
!("/proc/self/fd/{}", fd
)).unwrap();
107 // UID and GID first, as this fails if we lose access anyway.
108 c_result
!(libc
::chown(
109 c_proc_path
.as_ptr(),
114 .or_else(allow_notsupp
)
115 .map_err(|err
| format_err
!("failed to set ownership: {}", err
))
116 .or_else(&mut *on_error
)?
;
119 let mut skip_xattrs
= false;
120 apply_xattrs(flags
, c_proc_path
.as_ptr(), metadata
, &mut skip_xattrs
)
121 .or_else(&mut *on_error
)?
;
122 add_fcaps(flags
, c_proc_path
.as_ptr(), metadata
, &mut skip_xattrs
).or_else(&mut *on_error
)?
;
123 apply_acls(flags
, &c_proc_path
, metadata
, path_info
)
124 .map_err(|err
| format_err
!("failed to apply acls: {}", err
))
125 .or_else(&mut *on_error
)?
;
126 apply_quota_project_id(flags
, fd
, metadata
).or_else(&mut *on_error
)?
;
128 // Finally mode and time. We may lose access with mode, but the changing the mode also
130 if !metadata
.is_symlink() {
132 libc
::chmod(c_proc_path
.as_ptr(), perms_from_metadata(metadata
)?
.bits())
135 .or_else(allow_notsupp
)
136 .map_err(|err
| format_err
!("failed to change file mode: {}", err
))
137 .or_else(&mut *on_error
)?
;
140 let res
= c_result
!(unsafe {
143 c_proc_path
.as_ptr(),
144 timestamp_to_update_timespec(&metadata
.stat
.mtime
).as_ptr(),
150 Err(ref err
) if err
.is_errno(Errno
::EOPNOTSUPP
) => (),
152 on_error(format_err
!(
153 "failed to restore mtime attribute on {:?}: {}",
160 if metadata
.stat
.flags
!= 0 {
161 apply_flags(flags
, fd
, metadata
.stat
.flags
).or_else(&mut *on_error
)?
;
169 c_proc_path
: *const libc
::c_char
,
171 skip_xattrs
: &mut bool
,
172 ) -> Result
<(), Error
> {
173 if *skip_xattrs
|| !flags
.contains(Flags
::WITH_FCAPS
) {
176 let fcaps
= match metadata
.fcaps
.as_ref() {
177 Some(fcaps
) => fcaps
,
178 None
=> return Ok(()),
184 xattr
::xattr_name_fcaps().as_ptr(),
185 fcaps
.data
.as_ptr() as *const libc
::c_void
,
191 .or_else(|err
| allow_notsupp_remember(err
, skip_xattrs
))
192 .map_err(|err
| format_err
!("failed to apply file capabilities: {}", err
))?
;
199 c_proc_path
: *const libc
::c_char
,
201 skip_xattrs
: &mut bool
,
202 ) -> Result
<(), Error
> {
203 if *skip_xattrs
|| !flags
.contains(Flags
::WITH_XATTRS
) {
207 for xattr
in &metadata
.xattrs
{
212 if !xattr
::is_valid_xattr_name(xattr
.name()) {
213 eprintln
!("skipping invalid xattr named {:?}", xattr
.name());
220 xattr
.name().as_ptr() as *const libc
::c_char
,
221 xattr
.value().as_ptr() as *const libc
::c_void
,
227 .or_else(|err
| allow_notsupp_remember(err
, &mut *skip_xattrs
))
228 .map_err(|err
| format_err
!("failed to apply extended attributes: {}", err
))?
;
239 ) -> Result
<(), Error
> {
240 if !flags
.contains(Flags
::WITH_ACL
) || metadata
.acl
.is_empty() {
244 let mut acl
= acl
::ACL
::init(5)?
;
250 acl
::mode_user_to_acl_permissions(metadata
.stat
.mode
),
256 acl
::mode_other_to_acl_permissions(metadata
.stat
.mode
),
259 match metadata
.acl
.group_obj
.as_ref() {
264 acl
::mode_group_to_acl_permissions(metadata
.stat
.mode
),
266 acl
.add_entry_full(acl
::ACL_GROUP_OBJ
, None
, group_obj
.permissions
.0)?
;
269 let mode
= acl
::mode_group_to_acl_permissions(metadata
.stat
.mode
);
271 acl
.add_entry_full(acl
::ACL_GROUP_OBJ
, None
, mode
)?
;
273 if !metadata
.acl
.users
.is_empty() || !metadata
.acl
.groups
.is_empty() {
275 "Warning: {:?}: Missing GROUP_OBJ entry in ACL, resetting to value of MASK",
278 acl
.add_entry_full(acl
::ACL_MASK
, None
, mode
)?
;
283 for user
in &metadata
.acl
.users
{
284 acl
.add_entry_full(acl
::ACL_USER
, Some(user
.uid
), user
.permissions
.0)?
;
287 for group
in &metadata
.acl
.groups
{
288 acl
.add_entry_full(acl
::ACL_GROUP
, Some(group
.gid
), group
.permissions
.0)?
;
292 bail
!("Error while restoring ACL - ACL invalid");
295 acl
.set_file(c_proc_path
, acl
::ACL_TYPE_ACCESS
)?
;
299 if let Some(default) = metadata
.acl
.default.as_ref() {
300 let mut acl
= acl
::ACL
::init(5)?
;
302 acl
.add_entry_full(acl
::ACL_USER_OBJ
, None
, default.user_obj_permissions
.0)?
;
304 acl
.add_entry_full(acl
::ACL_GROUP_OBJ
, None
, default.group_obj_permissions
.0)?
;
306 acl
.add_entry_full(acl
::ACL_OTHER
, None
, default.other_permissions
.0)?
;
308 if default.mask_permissions
!= pxar
::format
::acl
::Permissions
::NO_MASK
{
309 acl
.add_entry_full(acl
::ACL_MASK
, None
, default.mask_permissions
.0)?
;
312 for user
in &metadata
.acl
.default_users
{
313 acl
.add_entry_full(acl
::ACL_USER
, Some(user
.uid
), user
.permissions
.0)?
;
316 for group
in &metadata
.acl
.default_groups
{
317 acl
.add_entry_full(acl
::ACL_GROUP
, Some(group
.gid
), group
.permissions
.0)?
;
321 bail
!("Error while restoring ACL - ACL invalid");
324 acl
.set_file(c_proc_path
, acl
::ACL_TYPE_DEFAULT
)?
;
330 fn apply_quota_project_id(flags
: Flags
, fd
: RawFd
, metadata
: &Metadata
) -> Result
<(), Error
> {
331 if !flags
.contains(Flags
::WITH_QUOTA_PROJID
) {
335 let projid
= match metadata
.quota_project_id
{
336 Some(projid
) => projid
,
337 None
=> return Ok(()),
340 let mut fsxattr
= fs
::FSXAttr
::default();
342 fs
::fs_ioc_fsgetxattr(fd
, &mut fsxattr
).map_err(|err
| {
344 "error while getting fsxattr to restore quota project id - {}",
349 fsxattr
.fsx_projid
= projid
.projid
as u32;
351 fs
::fs_ioc_fssetxattr(fd
, &fsxattr
).map_err(|err
| {
353 "error while setting fsxattr to restore quota project id - {}",
362 pub(crate) fn errno_is_unsupported(errno
: Errno
) -> bool
{
363 matches
!(errno
, Errno
::ENOTTY
| Errno
::ENOSYS
| Errno
::EBADF
| Errno
::EOPNOTSUPP
| Errno
::EINVAL
)
366 fn apply_chattr(fd
: RawFd
, chattr
: libc
::c_long
, mask
: libc
::c_long
) -> Result
<(), Error
> {
371 let mut fattr
: libc
::c_long
= 0;
372 match unsafe { fs::read_attr_fd(fd, &mut fattr) }
{
374 Err(nix
::Error
::Sys(errno
)) if errno_is_unsupported(errno
) => {
377 Err(err
) => bail
!("failed to read file attributes: {}", err
),
380 let attr
= (chattr
& mask
) | (fattr
& !mask
);
386 match unsafe { fs::write_attr_fd(fd, &attr) }
{
388 Err(nix
::Error
::Sys(errno
)) if errno_is_unsupported(errno
) => Ok(()),
389 Err(err
) => bail
!("failed to set file attributes: {}", err
),
393 fn apply_flags(flags
: Flags
, fd
: RawFd
, entry_flags
: u64) -> Result
<(), Error
> {
394 let entry_flags
= Flags
::from_bits_truncate(entry_flags
);
396 apply_chattr(fd
, entry_flags
.to_chattr(), flags
.to_chattr())?
;
398 let fatattr
= (flags
& entry_flags
).to_fat_attr();
400 match unsafe { fs::write_fat_attr_fd(fd, &fatattr) }
{
402 Err(nix
::Error
::Sys(errno
)) if errno_is_unsupported(errno
) => (),
403 Err(err
) => bail
!("failed to set file FAT attributes: {}", err
),