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 crate::pxar
::tools
::perms_from_metadata
;
17 use crate::pxar
::Flags
;
18 use crate::tools
::{acl, fs, xattr}
;
24 fn allow_notsupp
<E
: SysError
>(err
: E
) -> Result
<(), E
> {
25 if err
.is_errno(Errno
::EOPNOTSUPP
) {
32 fn allow_notsupp_remember
<E
: SysError
>(err
: E
, not_supp
: &mut bool
) -> Result
<(), E
> {
33 if err
.is_errno(Errno
::EOPNOTSUPP
) {
41 fn timestamp_to_update_timespec(mtime
: &pxar
::format
::StatxTimestamp
) -> [libc
::timespec
; 2] {
43 const UTIME_OMIT
: i64 = (1 << 30) - 2;
52 tv_nsec
: mtime
.nanos
as _
,
58 // metadata application:
67 on_error
: &mut (dyn FnMut(Error
) -> Result
<(), Error
> + Send
),
68 ) -> Result
<(), Error
> {
69 let fd
= proxmox
::tools
::fd
::Fd
::openat(
70 &unsafe { RawFdNum::from_raw_fd(parent) }
,
72 OFlag
::O_PATH
| OFlag
::O_CLOEXEC
| OFlag
::O_NOFOLLOW
,
76 apply(flags
, metadata
, fd
.as_raw_fd(), path_info
, on_error
)
79 pub fn apply_initial_flags(
83 on_error
: &mut (dyn FnMut(Error
) -> Result
<(), Error
> + Send
),
84 ) -> Result
<(), Error
> {
85 let entry_flags
= Flags
::from_bits_truncate(metadata
.stat
.flags
);
88 entry_flags
.to_initial_chattr(),
89 flags
.to_initial_chattr(),
100 on_error
: &mut (dyn FnMut(Error
) -> Result
<(), Error
> + Send
),
101 ) -> Result
<(), Error
> {
102 let c_proc_path
= CString
::new(format
!("/proc/self/fd/{}", fd
)).unwrap();
105 // UID and GID first, as this fails if we lose access anyway.
106 c_result
!(libc
::chown(
107 c_proc_path
.as_ptr(),
112 .or_else(allow_notsupp
)
113 .map_err(|err
| format_err
!("failed to set ownership: {}", err
))
114 .or_else(&mut *on_error
)?
;
117 let mut skip_xattrs
= false;
118 apply_xattrs(flags
, c_proc_path
.as_ptr(), metadata
, &mut skip_xattrs
)
119 .or_else(&mut *on_error
)?
;
120 add_fcaps(flags
, c_proc_path
.as_ptr(), metadata
, &mut skip_xattrs
).or_else(&mut *on_error
)?
;
121 apply_acls(flags
, &c_proc_path
, metadata
, path_info
)
122 .map_err(|err
| format_err
!("failed to apply acls: {}", err
))
123 .or_else(&mut *on_error
)?
;
124 apply_quota_project_id(flags
, fd
, metadata
).or_else(&mut *on_error
)?
;
126 // Finally mode and time. We may lose access with mode, but the changing the mode also
128 if !metadata
.is_symlink() {
130 libc
::chmod(c_proc_path
.as_ptr(), perms_from_metadata(metadata
)?
.bits())
133 .or_else(allow_notsupp
)
134 .map_err(|err
| format_err
!("failed to change file mode: {}", err
))
135 .or_else(&mut *on_error
)?
;
138 let res
= c_result
!(unsafe {
141 c_proc_path
.as_ptr(),
142 timestamp_to_update_timespec(&metadata
.stat
.mtime
).as_ptr(),
148 Err(ref err
) if err
.is_errno(Errno
::EOPNOTSUPP
) => (),
150 on_error(format_err
!(
151 "failed to restore mtime attribute on {:?}: {}",
158 if metadata
.stat
.flags
!= 0 {
159 apply_flags(flags
, fd
, metadata
.stat
.flags
).or_else(&mut *on_error
)?
;
167 c_proc_path
: *const libc
::c_char
,
169 skip_xattrs
: &mut bool
,
170 ) -> Result
<(), Error
> {
171 if *skip_xattrs
|| !flags
.contains(Flags
::WITH_FCAPS
) {
174 let fcaps
= match metadata
.fcaps
.as_ref() {
175 Some(fcaps
) => fcaps
,
176 None
=> return Ok(()),
182 xattr
::xattr_name_fcaps().as_ptr(),
183 fcaps
.data
.as_ptr() as *const libc
::c_void
,
189 .or_else(|err
| allow_notsupp_remember(err
, skip_xattrs
))
190 .map_err(|err
| format_err
!("failed to apply file capabilities: {}", err
))?
;
197 c_proc_path
: *const libc
::c_char
,
199 skip_xattrs
: &mut bool
,
200 ) -> Result
<(), Error
> {
201 if *skip_xattrs
|| !flags
.contains(Flags
::WITH_XATTRS
) {
205 for xattr
in &metadata
.xattrs
{
210 if !xattr
::is_valid_xattr_name(xattr
.name()) {
211 eprintln
!("skipping invalid xattr named {:?}", xattr
.name());
218 xattr
.name().as_ptr() as *const libc
::c_char
,
219 xattr
.value().as_ptr() as *const libc
::c_void
,
225 .or_else(|err
| allow_notsupp_remember(err
, &mut *skip_xattrs
))
226 .map_err(|err
| format_err
!("failed to apply extended attributes: {}", err
))?
;
237 ) -> Result
<(), Error
> {
238 if !flags
.contains(Flags
::WITH_ACL
) || metadata
.acl
.is_empty() {
242 let mut acl
= acl
::ACL
::init(5)?
;
248 acl
::mode_user_to_acl_permissions(metadata
.stat
.mode
),
254 acl
::mode_other_to_acl_permissions(metadata
.stat
.mode
),
257 match metadata
.acl
.group_obj
.as_ref() {
262 acl
::mode_group_to_acl_permissions(metadata
.stat
.mode
),
264 acl
.add_entry_full(acl
::ACL_GROUP_OBJ
, None
, group_obj
.permissions
.0)?
;
267 let mode
= acl
::mode_group_to_acl_permissions(metadata
.stat
.mode
);
269 acl
.add_entry_full(acl
::ACL_GROUP_OBJ
, None
, mode
)?
;
271 if !metadata
.acl
.users
.is_empty() || !metadata
.acl
.groups
.is_empty() {
273 "Warning: {:?}: Missing GROUP_OBJ entry in ACL, resetting to value of MASK",
276 acl
.add_entry_full(acl
::ACL_MASK
, None
, mode
)?
;
281 for user
in &metadata
.acl
.users
{
282 acl
.add_entry_full(acl
::ACL_USER
, Some(user
.uid
), user
.permissions
.0)?
;
285 for group
in &metadata
.acl
.groups
{
286 acl
.add_entry_full(acl
::ACL_GROUP
, Some(group
.gid
), group
.permissions
.0)?
;
290 bail
!("Error while restoring ACL - ACL invalid");
293 acl
.set_file(c_proc_path
, acl
::ACL_TYPE_ACCESS
)?
;
297 if let Some(default) = metadata
.acl
.default.as_ref() {
298 let mut acl
= acl
::ACL
::init(5)?
;
300 acl
.add_entry_full(acl
::ACL_USER_OBJ
, None
, default.user_obj_permissions
.0)?
;
302 acl
.add_entry_full(acl
::ACL_GROUP_OBJ
, None
, default.group_obj_permissions
.0)?
;
304 acl
.add_entry_full(acl
::ACL_OTHER
, None
, default.other_permissions
.0)?
;
306 if default.mask_permissions
!= pxar
::format
::acl
::Permissions
::NO_MASK
{
307 acl
.add_entry_full(acl
::ACL_MASK
, None
, default.mask_permissions
.0)?
;
310 for user
in &metadata
.acl
.default_users
{
311 acl
.add_entry_full(acl
::ACL_USER
, Some(user
.uid
), user
.permissions
.0)?
;
314 for group
in &metadata
.acl
.default_groups
{
315 acl
.add_entry_full(acl
::ACL_GROUP
, Some(group
.gid
), group
.permissions
.0)?
;
319 bail
!("Error while restoring ACL - ACL invalid");
322 acl
.set_file(c_proc_path
, acl
::ACL_TYPE_DEFAULT
)?
;
328 fn apply_quota_project_id(flags
: Flags
, fd
: RawFd
, metadata
: &Metadata
) -> Result
<(), Error
> {
329 if !flags
.contains(Flags
::WITH_QUOTA_PROJID
) {
333 let projid
= match metadata
.quota_project_id
{
334 Some(projid
) => projid
,
335 None
=> return Ok(()),
338 let mut fsxattr
= fs
::FSXAttr
::default();
340 fs
::fs_ioc_fsgetxattr(fd
, &mut fsxattr
).map_err(|err
| {
342 "error while getting fsxattr to restore quota project id - {}",
347 fsxattr
.fsx_projid
= projid
.projid
as u32;
349 fs
::fs_ioc_fssetxattr(fd
, &fsxattr
).map_err(|err
| {
351 "error while setting fsxattr to restore quota project id - {}",
360 pub(crate) fn errno_is_unsupported(errno
: Errno
) -> bool
{
361 matches
!(errno
, Errno
::ENOTTY
| Errno
::ENOSYS
| Errno
::EBADF
| Errno
::EOPNOTSUPP
| Errno
::EINVAL
)
364 fn apply_chattr(fd
: RawFd
, chattr
: libc
::c_long
, mask
: libc
::c_long
) -> Result
<(), Error
> {
369 let mut fattr
: libc
::c_long
= 0;
370 match unsafe { fs::read_attr_fd(fd, &mut fattr) }
{
372 Err(nix
::Error
::Sys(errno
)) if errno_is_unsupported(errno
) => {
375 Err(err
) => bail
!("failed to read file attributes: {}", err
),
378 let attr
= (chattr
& mask
) | (fattr
& !mask
);
384 match unsafe { fs::write_attr_fd(fd, &attr) }
{
386 Err(nix
::Error
::Sys(errno
)) if errno_is_unsupported(errno
) => Ok(()),
387 Err(err
) => bail
!("failed to set file attributes: {}", err
),
391 fn apply_flags(flags
: Flags
, fd
: RawFd
, entry_flags
: u64) -> Result
<(), Error
> {
392 let entry_flags
= Flags
::from_bits_truncate(entry_flags
);
394 apply_chattr(fd
, entry_flags
.to_chattr(), flags
.to_chattr())?
;
396 let fatattr
= (flags
& entry_flags
).to_fat_attr();
398 match unsafe { fs::write_fat_attr_fd(fd, &fatattr) }
{
400 Err(nix
::Error
::Sys(errno
)) if errno_is_unsupported(errno
) => (),
401 Err(err
) => bail
!("failed to set file FAT attributes: {}", err
),