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
;
16 use pbs_tools
::{acl, fs, xattr}
;
18 use crate::pxar
::tools
::perms_from_metadata
;
19 use crate::pxar
::Flags
;
25 fn allow_notsupp
<E
: SysError
>(err
: E
) -> Result
<(), E
> {
26 if err
.is_errno(Errno
::EOPNOTSUPP
) {
33 fn allow_notsupp_remember
<E
: SysError
>(err
: E
, not_supp
: &mut bool
) -> Result
<(), E
> {
34 if err
.is_errno(Errno
::EOPNOTSUPP
) {
42 fn timestamp_to_update_timespec(mtime
: &pxar
::format
::StatxTimestamp
) -> [libc
::timespec
; 2] {
44 const UTIME_OMIT
: i64 = (1 << 30) - 2;
53 tv_nsec
: mtime
.nanos
as _
,
59 // metadata application:
68 on_error
: &mut (dyn FnMut(Error
) -> Result
<(), Error
> + Send
),
69 ) -> Result
<(), Error
> {
70 let fd
= proxmox
::tools
::fd
::Fd
::openat(
71 &unsafe { RawFdNum::from_raw_fd(parent) }
,
73 OFlag
::O_PATH
| OFlag
::O_CLOEXEC
| OFlag
::O_NOFOLLOW
,
77 apply(flags
, metadata
, fd
.as_raw_fd(), path_info
, on_error
)
80 pub fn apply_initial_flags(
84 on_error
: &mut (dyn FnMut(Error
) -> Result
<(), Error
> + Send
),
85 ) -> Result
<(), Error
> {
86 let entry_flags
= Flags
::from_bits_truncate(metadata
.stat
.flags
);
89 entry_flags
.to_initial_chattr(),
90 flags
.to_initial_chattr(),
101 on_error
: &mut (dyn FnMut(Error
) -> Result
<(), Error
> + Send
),
102 ) -> Result
<(), Error
> {
103 let c_proc_path
= CString
::new(format
!("/proc/self/fd/{}", fd
)).unwrap();
106 // UID and GID first, as this fails if we lose access anyway.
107 c_result
!(libc
::chown(
108 c_proc_path
.as_ptr(),
113 .or_else(allow_notsupp
)
114 .map_err(|err
| format_err
!("failed to set ownership: {}", err
))
115 .or_else(&mut *on_error
)?
;
118 let mut skip_xattrs
= false;
119 apply_xattrs(flags
, c_proc_path
.as_ptr(), metadata
, &mut skip_xattrs
)
120 .or_else(&mut *on_error
)?
;
121 add_fcaps(flags
, c_proc_path
.as_ptr(), metadata
, &mut skip_xattrs
).or_else(&mut *on_error
)?
;
122 apply_acls(flags
, &c_proc_path
, metadata
, path_info
)
123 .map_err(|err
| format_err
!("failed to apply acls: {}", err
))
124 .or_else(&mut *on_error
)?
;
125 apply_quota_project_id(flags
, fd
, metadata
).or_else(&mut *on_error
)?
;
127 // Finally mode and time. We may lose access with mode, but the changing the mode also
129 if !metadata
.is_symlink() {
131 libc
::chmod(c_proc_path
.as_ptr(), perms_from_metadata(metadata
)?
.bits())
134 .or_else(allow_notsupp
)
135 .map_err(|err
| format_err
!("failed to change file mode: {}", err
))
136 .or_else(&mut *on_error
)?
;
139 let res
= c_result
!(unsafe {
142 c_proc_path
.as_ptr(),
143 timestamp_to_update_timespec(&metadata
.stat
.mtime
).as_ptr(),
149 Err(ref err
) if err
.is_errno(Errno
::EOPNOTSUPP
) => (),
151 on_error(format_err
!(
152 "failed to restore mtime attribute on {:?}: {}",
159 if metadata
.stat
.flags
!= 0 {
160 apply_flags(flags
, fd
, metadata
.stat
.flags
).or_else(&mut *on_error
)?
;
168 c_proc_path
: *const libc
::c_char
,
170 skip_xattrs
: &mut bool
,
171 ) -> Result
<(), Error
> {
172 if *skip_xattrs
|| !flags
.contains(Flags
::WITH_FCAPS
) {
175 let fcaps
= match metadata
.fcaps
.as_ref() {
176 Some(fcaps
) => fcaps
,
177 None
=> return Ok(()),
183 xattr
::xattr_name_fcaps().as_ptr(),
184 fcaps
.data
.as_ptr() as *const libc
::c_void
,
190 .or_else(|err
| allow_notsupp_remember(err
, skip_xattrs
))
191 .map_err(|err
| format_err
!("failed to apply file capabilities: {}", err
))?
;
198 c_proc_path
: *const libc
::c_char
,
200 skip_xattrs
: &mut bool
,
201 ) -> Result
<(), Error
> {
202 if *skip_xattrs
|| !flags
.contains(Flags
::WITH_XATTRS
) {
206 for xattr
in &metadata
.xattrs
{
211 if !xattr
::is_valid_xattr_name(xattr
.name()) {
212 eprintln
!("skipping invalid xattr named {:?}", xattr
.name());
219 xattr
.name().as_ptr() as *const libc
::c_char
,
220 xattr
.value().as_ptr() as *const libc
::c_void
,
226 .or_else(|err
| allow_notsupp_remember(err
, &mut *skip_xattrs
))
227 .map_err(|err
| format_err
!("failed to apply extended attributes: {}", err
))?
;
238 ) -> Result
<(), Error
> {
239 if !flags
.contains(Flags
::WITH_ACL
) || metadata
.acl
.is_empty() {
243 let mut acl
= acl
::ACL
::init(5)?
;
249 acl
::mode_user_to_acl_permissions(metadata
.stat
.mode
),
255 acl
::mode_other_to_acl_permissions(metadata
.stat
.mode
),
258 match metadata
.acl
.group_obj
.as_ref() {
263 acl
::mode_group_to_acl_permissions(metadata
.stat
.mode
),
265 acl
.add_entry_full(acl
::ACL_GROUP_OBJ
, None
, group_obj
.permissions
.0)?
;
268 let mode
= acl
::mode_group_to_acl_permissions(metadata
.stat
.mode
);
270 acl
.add_entry_full(acl
::ACL_GROUP_OBJ
, None
, mode
)?
;
272 if !metadata
.acl
.users
.is_empty() || !metadata
.acl
.groups
.is_empty() {
274 "Warning: {:?}: Missing GROUP_OBJ entry in ACL, resetting to value of MASK",
277 acl
.add_entry_full(acl
::ACL_MASK
, None
, mode
)?
;
282 for user
in &metadata
.acl
.users
{
283 acl
.add_entry_full(acl
::ACL_USER
, Some(user
.uid
), user
.permissions
.0)?
;
286 for group
in &metadata
.acl
.groups
{
287 acl
.add_entry_full(acl
::ACL_GROUP
, Some(group
.gid
), group
.permissions
.0)?
;
291 bail
!("Error while restoring ACL - ACL invalid");
294 acl
.set_file(c_proc_path
, acl
::ACL_TYPE_ACCESS
)?
;
298 if let Some(default) = metadata
.acl
.default.as_ref() {
299 let mut acl
= acl
::ACL
::init(5)?
;
301 acl
.add_entry_full(acl
::ACL_USER_OBJ
, None
, default.user_obj_permissions
.0)?
;
303 acl
.add_entry_full(acl
::ACL_GROUP_OBJ
, None
, default.group_obj_permissions
.0)?
;
305 acl
.add_entry_full(acl
::ACL_OTHER
, None
, default.other_permissions
.0)?
;
307 if default.mask_permissions
!= pxar
::format
::acl
::Permissions
::NO_MASK
{
308 acl
.add_entry_full(acl
::ACL_MASK
, None
, default.mask_permissions
.0)?
;
311 for user
in &metadata
.acl
.default_users
{
312 acl
.add_entry_full(acl
::ACL_USER
, Some(user
.uid
), user
.permissions
.0)?
;
315 for group
in &metadata
.acl
.default_groups
{
316 acl
.add_entry_full(acl
::ACL_GROUP
, Some(group
.gid
), group
.permissions
.0)?
;
320 bail
!("Error while restoring ACL - ACL invalid");
323 acl
.set_file(c_proc_path
, acl
::ACL_TYPE_DEFAULT
)?
;
329 fn apply_quota_project_id(flags
: Flags
, fd
: RawFd
, metadata
: &Metadata
) -> Result
<(), Error
> {
330 if !flags
.contains(Flags
::WITH_QUOTA_PROJID
) {
334 let projid
= match metadata
.quota_project_id
{
335 Some(projid
) => projid
,
336 None
=> return Ok(()),
339 let mut fsxattr
= fs
::FSXAttr
::default();
341 fs
::fs_ioc_fsgetxattr(fd
, &mut fsxattr
).map_err(|err
| {
343 "error while getting fsxattr to restore quota project id - {}",
348 fsxattr
.fsx_projid
= projid
.projid
as u32;
350 fs
::fs_ioc_fssetxattr(fd
, &fsxattr
).map_err(|err
| {
352 "error while setting fsxattr to restore quota project id - {}",
361 pub(crate) fn errno_is_unsupported(errno
: Errno
) -> bool
{
362 matches
!(errno
, Errno
::ENOTTY
| Errno
::ENOSYS
| Errno
::EBADF
| Errno
::EOPNOTSUPP
| Errno
::EINVAL
)
365 fn apply_chattr(fd
: RawFd
, chattr
: libc
::c_long
, mask
: libc
::c_long
) -> Result
<(), Error
> {
370 let mut fattr
: libc
::c_long
= 0;
371 match unsafe { fs::read_attr_fd(fd, &mut fattr) }
{
373 Err(nix
::Error
::Sys(errno
)) if errno_is_unsupported(errno
) => {
376 Err(err
) => bail
!("failed to read file attributes: {}", err
),
379 let attr
= (chattr
& mask
) | (fattr
& !mask
);
385 match unsafe { fs::write_attr_fd(fd, &attr) }
{
387 Err(nix
::Error
::Sys(errno
)) if errno_is_unsupported(errno
) => Ok(()),
388 Err(err
) => bail
!("failed to set file attributes: {}", err
),
392 fn apply_flags(flags
: Flags
, fd
: RawFd
, entry_flags
: u64) -> Result
<(), Error
> {
393 let entry_flags
= Flags
::from_bits_truncate(entry_flags
);
395 apply_chattr(fd
, entry_flags
.to_chattr(), flags
.to_chattr())?
;
397 let fatattr
= (flags
& entry_flags
).to_fat_attr();
399 match unsafe { fs::write_fat_attr_fd(fd, &fatattr) }
{
401 Err(nix
::Error
::Sys(errno
)) if errno_is_unsupported(errno
) => (),
402 Err(err
) => bail
!("failed to set file FAT attributes: {}", err
),