1 use std
::ffi
::{CStr, CString}
;
2 use std
::os
::unix
::io
::{AsRawFd, FromRawFd, RawFd}
;
4 use anyhow
::{bail, format_err, Error}
;
7 use nix
::sys
::stat
::Mode
;
11 use proxmox
::c_result
;
12 use proxmox
::sys
::error
::SysError
;
13 use proxmox
::tools
::fd
::RawFdNum
;
15 use crate::pxar
::tools
::perms_from_metadata
;
16 use crate::pxar
::Flags
;
17 use crate::tools
::{acl, fs, xattr}
;
23 fn allow_notsupp
<E
: SysError
>(err
: E
) -> Result
<(), E
> {
24 if err
.is_errno(Errno
::EOPNOTSUPP
) {
31 fn allow_notsupp_remember
<E
: SysError
>(err
: E
, not_supp
: &mut bool
) -> Result
<(), E
> {
32 if err
.is_errno(Errno
::EOPNOTSUPP
) {
40 fn timestamp_to_update_timespec(mtime
: &pxar
::format
::StatxTimestamp
) -> [libc
::timespec
; 2] {
42 const UTIME_OMIT
: i64 = (1 << 30) - 2;
51 tv_nsec
: mtime
.nanos
as _
,
57 // metadata application:
65 on_error
: &mut (dyn FnMut(Error
) -> Result
<(), Error
> + Send
),
66 ) -> Result
<(), Error
> {
67 let fd
= proxmox
::tools
::fd
::Fd
::openat(
68 &unsafe { RawFdNum::from_raw_fd(parent) }
,
70 OFlag
::O_PATH
| OFlag
::O_CLOEXEC
| OFlag
::O_NOFOLLOW
,
74 apply(flags
, metadata
, fd
.as_raw_fd(), file_name
, on_error
)
77 pub fn apply_initial_flags(
81 on_error
: &mut (dyn FnMut(Error
) -> Result
<(), Error
> + Send
),
82 ) -> Result
<(), Error
> {
83 let entry_flags
= Flags
::from_bits_truncate(metadata
.stat
.flags
);
86 entry_flags
.to_initial_chattr(),
87 flags
.to_initial_chattr(),
98 on_error
: &mut (dyn FnMut(Error
) -> Result
<(), Error
> + Send
),
99 ) -> Result
<(), Error
> {
100 let c_proc_path
= CString
::new(format
!("/proc/self/fd/{}", fd
)).unwrap();
103 // UID and GID first, as this fails if we lose access anyway.
104 c_result
!(libc
::chown(
105 c_proc_path
.as_ptr(),
110 .or_else(allow_notsupp
)
111 .map_err(|err
| format_err
!("failed to set ownership: {}", err
))
112 .or_else(&mut *on_error
)?
;
115 let mut skip_xattrs
= false;
116 apply_xattrs(flags
, c_proc_path
.as_ptr(), metadata
, &mut skip_xattrs
)
117 .or_else(&mut *on_error
)?
;
118 add_fcaps(flags
, c_proc_path
.as_ptr(), metadata
, &mut skip_xattrs
).or_else(&mut *on_error
)?
;
119 apply_acls(flags
, &c_proc_path
, metadata
)
120 .map_err(|err
| format_err
!("failed to apply acls: {}", err
))
121 .or_else(&mut *on_error
)?
;
122 apply_quota_project_id(flags
, fd
, metadata
).or_else(&mut *on_error
)?
;
124 // Finally mode and time. We may lose access with mode, but the changing the mode also
126 if !metadata
.is_symlink() {
128 libc
::chmod(c_proc_path
.as_ptr(), perms_from_metadata(metadata
)?
.bits())
131 .or_else(allow_notsupp
)
132 .map_err(|err
| format_err
!("failed to change file mode: {}", err
))
133 .or_else(&mut *on_error
)?
;
136 if metadata
.stat
.flags
!= 0 {
137 apply_flags(flags
, fd
, metadata
.stat
.flags
).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 {:?}: {}",
165 c_proc_path
: *const libc
::c_char
,
167 skip_xattrs
: &mut bool
,
168 ) -> Result
<(), Error
> {
169 if *skip_xattrs
|| !flags
.contains(Flags
::WITH_FCAPS
) {
172 let fcaps
= match metadata
.fcaps
.as_ref() {
173 Some(fcaps
) => fcaps
,
174 None
=> return Ok(()),
180 xattr
::xattr_name_fcaps().as_ptr(),
181 fcaps
.data
.as_ptr() as *const libc
::c_void
,
187 .or_else(|err
| allow_notsupp_remember(err
, skip_xattrs
))
188 .map_err(|err
| format_err
!("failed to apply file capabilities: {}", err
))?
;
195 c_proc_path
: *const libc
::c_char
,
197 skip_xattrs
: &mut bool
,
198 ) -> Result
<(), Error
> {
199 if *skip_xattrs
|| !flags
.contains(Flags
::WITH_XATTRS
) {
203 for xattr
in &metadata
.xattrs
{
208 if !xattr
::is_valid_xattr_name(xattr
.name()) {
209 eprintln
!("skipping invalid xattr named {:?}", xattr
.name());
216 xattr
.name().as_ptr() as *const libc
::c_char
,
217 xattr
.value().as_ptr() as *const libc
::c_void
,
223 .or_else(|err
| allow_notsupp_remember(err
, &mut *skip_xattrs
))
224 .map_err(|err
| format_err
!("failed to apply extended attributes: {}", err
))?
;
230 fn apply_acls(flags
: Flags
, c_proc_path
: &CStr
, metadata
: &Metadata
) -> Result
<(), Error
> {
231 if !flags
.contains(Flags
::WITH_ACL
) || metadata
.acl
.is_empty() {
235 let mut acl
= acl
::ACL
::init(5)?
;
241 acl
::mode_user_to_acl_permissions(metadata
.stat
.mode
),
247 acl
::mode_other_to_acl_permissions(metadata
.stat
.mode
),
250 match metadata
.acl
.group_obj
.as_ref() {
255 acl
::mode_group_to_acl_permissions(metadata
.stat
.mode
),
257 acl
.add_entry_full(acl
::ACL_GROUP_OBJ
, None
, group_obj
.permissions
.0)?
;
263 acl
::mode_group_to_acl_permissions(metadata
.stat
.mode
),
268 for user
in &metadata
.acl
.users
{
269 acl
.add_entry_full(acl
::ACL_USER
, Some(user
.uid
), user
.permissions
.0)?
;
272 for group
in &metadata
.acl
.groups
{
273 acl
.add_entry_full(acl
::ACL_GROUP
, Some(group
.gid
), group
.permissions
.0)?
;
277 bail
!("Error while restoring ACL - ACL invalid");
280 acl
.set_file(c_proc_path
, acl
::ACL_TYPE_ACCESS
)?
;
284 if let Some(default) = metadata
.acl
.default.as_ref() {
285 let mut acl
= acl
::ACL
::init(5)?
;
287 acl
.add_entry_full(acl
::ACL_USER_OBJ
, None
, default.user_obj_permissions
.0)?
;
289 acl
.add_entry_full(acl
::ACL_GROUP_OBJ
, None
, default.group_obj_permissions
.0)?
;
291 acl
.add_entry_full(acl
::ACL_OTHER
, None
, default.other_permissions
.0)?
;
293 if default.mask_permissions
!= pxar
::format
::acl
::Permissions
::NO_MASK
{
294 acl
.add_entry_full(acl
::ACL_MASK
, None
, default.mask_permissions
.0)?
;
297 for user
in &metadata
.acl
.default_users
{
298 acl
.add_entry_full(acl
::ACL_USER
, Some(user
.uid
), user
.permissions
.0)?
;
301 for group
in &metadata
.acl
.default_groups
{
302 acl
.add_entry_full(acl
::ACL_GROUP
, Some(group
.gid
), group
.permissions
.0)?
;
306 bail
!("Error while restoring ACL - ACL invalid");
309 acl
.set_file(c_proc_path
, acl
::ACL_TYPE_DEFAULT
)?
;
315 fn apply_quota_project_id(flags
: Flags
, fd
: RawFd
, metadata
: &Metadata
) -> Result
<(), Error
> {
316 if !flags
.contains(Flags
::WITH_QUOTA_PROJID
) {
320 let projid
= match metadata
.quota_project_id
{
321 Some(projid
) => projid
,
322 None
=> return Ok(()),
325 let mut fsxattr
= fs
::FSXAttr
::default();
327 fs
::fs_ioc_fsgetxattr(fd
, &mut fsxattr
).map_err(|err
| {
329 "error while getting fsxattr to restore quota project id - {}",
334 fsxattr
.fsx_projid
= projid
.projid
as u32;
336 fs
::fs_ioc_fssetxattr(fd
, &fsxattr
).map_err(|err
| {
338 "error while setting fsxattr to restore quota project id - {}",
347 pub(crate) fn errno_is_unsupported(errno
: Errno
) -> bool
{
349 Errno
::ENOTTY
| Errno
::ENOSYS
| Errno
::EBADF
| Errno
::EOPNOTSUPP
| Errno
::EINVAL
=> true,
354 fn apply_chattr(fd
: RawFd
, chattr
: libc
::c_long
, mask
: libc
::c_long
) -> Result
<(), Error
> {
359 let mut fattr
: libc
::c_long
= 0;
360 match unsafe { fs::read_attr_fd(fd, &mut fattr) }
{
362 Err(nix
::Error
::Sys(errno
)) if errno_is_unsupported(errno
) => {
365 Err(err
) => bail
!("failed to read file attributes: {}", err
),
368 let attr
= (chattr
& mask
) | (fattr
& !mask
);
369 match unsafe { fs::write_attr_fd(fd, &attr) }
{
371 Err(nix
::Error
::Sys(errno
)) if errno_is_unsupported(errno
) => Ok(()),
372 Err(err
) => bail
!("failed to set file attributes: {}", err
),
376 fn apply_flags(flags
: Flags
, fd
: RawFd
, entry_flags
: u64) -> Result
<(), Error
> {
377 let entry_flags
= Flags
::from_bits_truncate(entry_flags
);
379 apply_chattr(fd
, entry_flags
.to_chattr(), flags
.to_chattr())?
;
381 let fatattr
= (flags
& entry_flags
).to_fat_attr();
383 match unsafe { fs::write_fat_attr_fd(fd, &fatattr) }
{
385 Err(nix
::Error
::Sys(errno
)) if errno_is_unsupported(errno
) => (),
386 Err(err
) => bail
!("failed to set file attributes: {}", err
),