pxar: restore file attributes, improve errors
and use the correct integer types for these operations Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
ec2434fe3c
commit
032cd1b862
@ -23,6 +23,7 @@ use proxmox::tools::fd::RawFdNum;
|
||||
use proxmox::tools::vec;
|
||||
|
||||
use crate::pxar::catalog::BackupCatalogWriter;
|
||||
use crate::pxar::metadata::errno_is_unsupported;
|
||||
use crate::pxar::Flags;
|
||||
use crate::pxar::tools::assert_single_path_component;
|
||||
use crate::tools::{acl, fs, xattr, Fd};
|
||||
@ -698,13 +699,6 @@ fn get_metadata(fd: RawFd, stat: &FileStat, flags: Flags, fs_magic: i64) -> Resu
|
||||
Ok(meta)
|
||||
}
|
||||
|
||||
fn errno_is_unsupported(errno: Errno) -> bool {
|
||||
match errno {
|
||||
Errno::ENOTTY | Errno::ENOSYS | Errno::EBADF | Errno::EOPNOTSUPP | Errno::EINVAL => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_fcaps(meta: &mut Metadata, fd: RawFd, flags: Flags) -> Result<(), Error> {
|
||||
if flags.contains(Flags::WITH_FCAPS) {
|
||||
return Ok(());
|
||||
@ -769,7 +763,7 @@ fn get_xattr_fcaps_acl(
|
||||
}
|
||||
|
||||
fn get_chattr(metadata: &mut Metadata, fd: RawFd) -> Result<(), Error> {
|
||||
let mut attr: usize = 0;
|
||||
let mut attr: libc::c_long = 0;
|
||||
|
||||
match unsafe { fs::read_attr_fd(fd, &mut attr) } {
|
||||
Ok(_) => (),
|
||||
@ -779,7 +773,7 @@ fn get_chattr(metadata: &mut Metadata, fd: RawFd) -> Result<(), Error> {
|
||||
Err(err) => bail!("failed to read file attributes: {}", err),
|
||||
}
|
||||
|
||||
metadata.stat.flags |= Flags::from_chattr(attr as u32).bits();
|
||||
metadata.stat.flags |= Flags::from_chattr(attr).bits();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -230,7 +230,8 @@ impl Extractor {
|
||||
dir.metadata(),
|
||||
fd,
|
||||
&CString::new(dir.file_name().as_bytes())?,
|
||||
)?;
|
||||
)
|
||||
.map_err(|err| format_err!("failed to apply directory metadata: {}", err))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -241,7 +242,9 @@ impl Extractor {
|
||||
}
|
||||
|
||||
fn parent_fd(&mut self) -> Result<RawFd, Error> {
|
||||
self.dir_stack.last_dir_fd(self.allow_existing_dirs)
|
||||
self.dir_stack
|
||||
.last_dir_fd(self.allow_existing_dirs)
|
||||
.map_err(|err| format_err!("failed to get parent directory file descriptor: {}", err))
|
||||
}
|
||||
|
||||
pub fn extract_symlink(
|
||||
@ -320,10 +323,14 @@ impl Extractor {
|
||||
file_name,
|
||||
OFlag::O_CREAT | OFlag::O_EXCL | OFlag::O_WRONLY | OFlag::O_CLOEXEC,
|
||||
Mode::from_bits(0o600).unwrap(),
|
||||
)?)
|
||||
)
|
||||
.map_err(|err| format_err!("failed to create file {:?}: {}", file_name, err))?)
|
||||
};
|
||||
|
||||
let extracted = io::copy(&mut *contents, &mut file)?;
|
||||
metadata::apply_initial_flags(self.feature_flags, metadata, file.as_raw_fd())?;
|
||||
|
||||
let extracted = io::copy(&mut *contents, &mut file)
|
||||
.map_err(|err| format_err!("failed to copy file contents: {}", err))?;
|
||||
if size != extracted {
|
||||
bail!("extracted {} bytes of a file of {} bytes", extracted, size);
|
||||
}
|
||||
@ -345,10 +352,15 @@ impl Extractor {
|
||||
file_name,
|
||||
OFlag::O_CREAT | OFlag::O_EXCL | OFlag::O_WRONLY | OFlag::O_CLOEXEC,
|
||||
Mode::from_bits(0o600).unwrap(),
|
||||
)?)
|
||||
)
|
||||
.map_err(|err| format_err!("failed to create file {:?}: {}", file_name, err))?)
|
||||
});
|
||||
|
||||
let extracted = tokio::io::copy(&mut *contents, &mut file).await?;
|
||||
metadata::apply_initial_flags(self.feature_flags, metadata, file.as_raw_fd())?;
|
||||
|
||||
let extracted = tokio::io::copy(&mut *contents, &mut file)
|
||||
.await
|
||||
.map_err(|err| format_err!("failed to copy file contents: {}", err))?;
|
||||
if size != extracted {
|
||||
bail!("extracted {} bytes of a file of {} bytes", extracted, size);
|
||||
}
|
||||
|
@ -3,6 +3,8 @@
|
||||
//! Flags for known supported features for a given filesystem can be derived
|
||||
//! from the superblocks magic number.
|
||||
|
||||
use libc::c_long;
|
||||
|
||||
use bitflags::bitflags;
|
||||
|
||||
bitflags! {
|
||||
@ -149,34 +151,54 @@ impl Default for Flags {
|
||||
}
|
||||
}
|
||||
|
||||
// form /usr/include/linux/fs.h
|
||||
const FS_APPEND_FL: c_long = 0x0000_0020;
|
||||
const FS_NOATIME_FL: c_long = 0x0000_0080;
|
||||
const FS_COMPR_FL: c_long = 0x0000_0004;
|
||||
const FS_NOCOW_FL: c_long = 0x0080_0000;
|
||||
const FS_NODUMP_FL: c_long = 0x0000_0040;
|
||||
const FS_DIRSYNC_FL: c_long = 0x0001_0000;
|
||||
const FS_IMMUTABLE_FL: c_long = 0x0000_0010;
|
||||
const FS_SYNC_FL: c_long = 0x0000_0008;
|
||||
const FS_NOCOMP_FL: c_long = 0x0000_0400;
|
||||
const FS_PROJINHERIT_FL: c_long = 0x2000_0000;
|
||||
|
||||
pub(crate) const INITIAL_FS_FLAGS: c_long =
|
||||
FS_NOATIME_FL
|
||||
| FS_COMPR_FL
|
||||
| FS_NOCOW_FL
|
||||
| FS_NOCOMP_FL
|
||||
| FS_PROJINHERIT_FL;
|
||||
|
||||
#[rustfmt::skip]
|
||||
const CHATTR_MAP: [(Flags, c_long); 10] = [
|
||||
( Flags::WITH_FLAG_APPEND, FS_APPEND_FL ),
|
||||
( Flags::WITH_FLAG_NOATIME, FS_NOATIME_FL ),
|
||||
( Flags::WITH_FLAG_COMPR, FS_COMPR_FL ),
|
||||
( Flags::WITH_FLAG_NOCOW, FS_NOCOW_FL ),
|
||||
( Flags::WITH_FLAG_NODUMP, FS_NODUMP_FL ),
|
||||
( Flags::WITH_FLAG_DIRSYNC, FS_DIRSYNC_FL ),
|
||||
( Flags::WITH_FLAG_IMMUTABLE, FS_IMMUTABLE_FL ),
|
||||
( Flags::WITH_FLAG_SYNC, FS_SYNC_FL ),
|
||||
( Flags::WITH_FLAG_NOCOMP, FS_NOCOMP_FL ),
|
||||
( Flags::WITH_FLAG_PROJINHERIT, FS_PROJINHERIT_FL ),
|
||||
];
|
||||
|
||||
// from /usr/include/linux/msdos_fs.h
|
||||
const ATTR_HIDDEN: u32 = 2;
|
||||
const ATTR_SYS: u32 = 4;
|
||||
const ATTR_ARCH: u32 = 32;
|
||||
|
||||
#[rustfmt::skip]
|
||||
const FAT_ATTR_MAP: [(Flags, u32); 3] = [
|
||||
( Flags::WITH_FLAG_HIDDEN, ATTR_HIDDEN ),
|
||||
( Flags::WITH_FLAG_SYSTEM, ATTR_SYS ),
|
||||
( Flags::WITH_FLAG_ARCHIVE, ATTR_ARCH ),
|
||||
];
|
||||
|
||||
impl Flags {
|
||||
/// Get a set of feature flags from file attributes.
|
||||
pub fn from_chattr(attr: u32) -> Flags {
|
||||
// form /usr/include/linux/fs.h
|
||||
const FS_APPEND_FL: u32 = 0x0000_0020;
|
||||
const FS_NOATIME_FL: u32 = 0x0000_0080;
|
||||
const FS_COMPR_FL: u32 = 0x0000_0004;
|
||||
const FS_NOCOW_FL: u32 = 0x0080_0000;
|
||||
const FS_NODUMP_FL: u32 = 0x0000_0040;
|
||||
const FS_DIRSYNC_FL: u32 = 0x0001_0000;
|
||||
const FS_IMMUTABLE_FL: u32 = 0x0000_0010;
|
||||
const FS_SYNC_FL: u32 = 0x0000_0008;
|
||||
const FS_NOCOMP_FL: u32 = 0x0000_0400;
|
||||
const FS_PROJINHERIT_FL: u32 = 0x2000_0000;
|
||||
|
||||
const CHATTR_MAP: [(Flags, u32); 10] = [
|
||||
( Flags::WITH_FLAG_APPEND, FS_APPEND_FL ),
|
||||
( Flags::WITH_FLAG_NOATIME, FS_NOATIME_FL ),
|
||||
( Flags::WITH_FLAG_COMPR, FS_COMPR_FL ),
|
||||
( Flags::WITH_FLAG_NOCOW, FS_NOCOW_FL ),
|
||||
( Flags::WITH_FLAG_NODUMP, FS_NODUMP_FL ),
|
||||
( Flags::WITH_FLAG_DIRSYNC, FS_DIRSYNC_FL ),
|
||||
( Flags::WITH_FLAG_IMMUTABLE, FS_IMMUTABLE_FL ),
|
||||
( Flags::WITH_FLAG_SYNC, FS_SYNC_FL ),
|
||||
( Flags::WITH_FLAG_NOCOMP, FS_NOCOMP_FL ),
|
||||
( Flags::WITH_FLAG_PROJINHERIT, FS_PROJINHERIT_FL ),
|
||||
];
|
||||
|
||||
pub fn from_chattr(attr: c_long) -> Flags {
|
||||
let mut flags = Flags::empty();
|
||||
|
||||
for (fe_flag, fs_flag) in &CHATTR_MAP {
|
||||
@ -188,19 +210,25 @@ impl Flags {
|
||||
flags
|
||||
}
|
||||
|
||||
/// Get the chattr bit representation of these feature flags.
|
||||
pub fn to_chattr(self) -> c_long {
|
||||
let mut flags: c_long = 0;
|
||||
|
||||
for (fe_flag, fs_flag) in &CHATTR_MAP {
|
||||
if self.contains(*fe_flag) {
|
||||
flags |= *fs_flag;
|
||||
}
|
||||
}
|
||||
|
||||
flags
|
||||
}
|
||||
|
||||
pub fn to_initial_chattr(self) -> c_long {
|
||||
self.to_chattr() & INITIAL_FS_FLAGS
|
||||
}
|
||||
|
||||
/// Get a set of feature flags from FAT attributes.
|
||||
pub fn from_fat_attr(attr: u32) -> Flags {
|
||||
// from /usr/include/linux/msdos_fs.h
|
||||
const ATTR_HIDDEN: u32 = 2;
|
||||
const ATTR_SYS: u32 = 4;
|
||||
const ATTR_ARCH: u32 = 32;
|
||||
|
||||
const FAT_ATTR_MAP: [(Flags, u32); 3] = [
|
||||
( Flags::WITH_FLAG_HIDDEN, ATTR_HIDDEN ),
|
||||
( Flags::WITH_FLAG_SYSTEM, ATTR_SYS ),
|
||||
( Flags::WITH_FLAG_ARCHIVE, ATTR_ARCH ),
|
||||
];
|
||||
|
||||
let mut flags = Flags::empty();
|
||||
|
||||
for (fe_flag, fs_flag) in &FAT_ATTR_MAP {
|
||||
@ -212,6 +240,19 @@ impl Flags {
|
||||
flags
|
||||
}
|
||||
|
||||
/// Get the fat attribute bit representation of these feature flags.
|
||||
pub fn to_fat_attr(self) -> u32 {
|
||||
let mut flags = 0u32;
|
||||
|
||||
for (fe_flag, fs_flag) in &FAT_ATTR_MAP {
|
||||
if self.contains(*fe_flag) {
|
||||
flags |= *fs_flag;
|
||||
}
|
||||
}
|
||||
|
||||
flags
|
||||
}
|
||||
|
||||
/// Return the supported *pxar* feature flags based on the magic number of the filesystem.
|
||||
pub fn from_magic(magic: i64) -> Flags {
|
||||
use proxmox::sys::linux::magic::*;
|
||||
|
@ -79,13 +79,19 @@ pub fn apply_at(
|
||||
apply(flags, metadata, fd.as_raw_fd(), file_name)
|
||||
}
|
||||
|
||||
pub fn apply_initial_flags(
|
||||
flags: Flags,
|
||||
metadata: &Metadata,
|
||||
fd: RawFd,
|
||||
) -> Result<(), Error> {
|
||||
let entry_flags = Flags::from_bits_truncate(metadata.stat.flags);
|
||||
apply_chattr(fd, entry_flags.to_initial_chattr(), flags.to_initial_chattr());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn apply(flags: Flags, metadata: &Metadata, fd: RawFd, file_name: &CStr) -> Result<(), Error> {
|
||||
let c_proc_path = CString::new(format!("/proc/self/fd/{}", fd)).unwrap();
|
||||
|
||||
if metadata.stat.flags != 0 {
|
||||
todo!("apply flags!");
|
||||
}
|
||||
|
||||
unsafe {
|
||||
// UID and GID first, as this fails if we lose access anyway.
|
||||
c_result!(libc::chown(
|
||||
@ -94,13 +100,15 @@ pub fn apply(flags: Flags, metadata: &Metadata, fd: RawFd, file_name: &CStr) ->
|
||||
metadata.stat.gid
|
||||
))
|
||||
.map(drop)
|
||||
.or_else(allow_notsupp)?;
|
||||
.or_else(allow_notsupp)
|
||||
.map_err(|err| format_err!("failed to set ownership: {}", err))?;
|
||||
}
|
||||
|
||||
let mut skip_xattrs = false;
|
||||
apply_xattrs(flags, c_proc_path.as_ptr(), metadata, &mut skip_xattrs)?;
|
||||
add_fcaps(flags, c_proc_path.as_ptr(), metadata, &mut skip_xattrs)?;
|
||||
apply_acls(flags, &c_proc_path, metadata)?;
|
||||
apply_acls(flags, &c_proc_path, metadata)
|
||||
.map_err(|err| format_err!("failed to apply acls: {}", err))?;
|
||||
apply_quota_project_id(flags, fd, metadata)?;
|
||||
|
||||
// Finally mode and time. We may lose access with mode, but the changing the mode also
|
||||
@ -110,7 +118,12 @@ pub fn apply(flags: Flags, metadata: &Metadata, fd: RawFd, file_name: &CStr) ->
|
||||
libc::chmod(c_proc_path.as_ptr(), perms_from_metadata(metadata)?.bits())
|
||||
})
|
||||
.map(drop)
|
||||
.or_else(allow_notsupp)?;
|
||||
.or_else(allow_notsupp)
|
||||
.map_err(|err| format_err!("failed to change file mode: {}", err))?;
|
||||
}
|
||||
|
||||
if metadata.stat.flags != 0 {
|
||||
apply_flags(flags, fd, metadata.stat.flags)?;
|
||||
}
|
||||
|
||||
let res = c_result!(unsafe {
|
||||
@ -160,7 +173,8 @@ fn add_fcaps(
|
||||
)
|
||||
})
|
||||
.map(drop)
|
||||
.or_else(|err| allow_notsupp_remember(err, skip_xattrs))?;
|
||||
.or_else(|err| allow_notsupp_remember(err, skip_xattrs))
|
||||
.map_err(|err| format_err!("failed to apply file capabilities: {}", err))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -195,7 +209,8 @@ fn apply_xattrs(
|
||||
)
|
||||
})
|
||||
.map(drop)
|
||||
.or_else(|err| allow_notsupp_remember(err, &mut *skip_xattrs))?;
|
||||
.or_else(|err| allow_notsupp_remember(err, &mut *skip_xattrs))
|
||||
.map_err(|err| format_err!("failed to apply extended attributes: {}", err))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -317,3 +332,49 @@ fn apply_quota_project_id(flags: Flags, fd: RawFd, metadata: &Metadata) -> Resul
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn errno_is_unsupported(errno: Errno) -> bool {
|
||||
match errno {
|
||||
Errno::ENOTTY | Errno::ENOSYS | Errno::EBADF | Errno::EOPNOTSUPP | Errno::EINVAL => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_chattr(fd: RawFd, chattr: libc::c_long, mask: libc::c_long) -> Result<(), Error> {
|
||||
if chattr == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut fattr: libc::c_long = 0;
|
||||
match unsafe { fs::read_attr_fd(fd, &mut fattr) } {
|
||||
Ok(_) => (),
|
||||
Err(nix::Error::Sys(errno)) if errno_is_unsupported(errno) => {
|
||||
return Ok(());
|
||||
}
|
||||
Err(err) => bail!("failed to read file attributes: {}", err),
|
||||
}
|
||||
|
||||
let attr = (chattr & mask) | (fattr & !mask);
|
||||
match unsafe { fs::write_attr_fd(fd, &attr) } {
|
||||
Ok(_) => Ok(()),
|
||||
Err(nix::Error::Sys(errno)) if errno_is_unsupported(errno) => Ok(()),
|
||||
Err(err) => bail!("failed to set file attributes: {}", err),
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_flags(flags: Flags, fd: RawFd, entry_flags: u64) -> Result<(), Error> {
|
||||
let entry_flags = Flags::from_bits_truncate(entry_flags);
|
||||
|
||||
apply_chattr(fd, entry_flags.to_chattr(), flags.to_chattr())?;
|
||||
|
||||
let fatattr = (flags & entry_flags).to_fat_attr();
|
||||
if fatattr != 0 {
|
||||
match unsafe { fs::write_fat_attr_fd(fd, &fatattr) } {
|
||||
Ok(_) => (),
|
||||
Err(nix::Error::Sys(errno)) if errno_is_unsupported(errno) => (),
|
||||
Err(err) => bail!("failed to set file attributes: {}", err),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -222,11 +222,13 @@ where
|
||||
|
||||
// /usr/include/linux/fs.h: #define FS_IOC_GETFLAGS _IOR('f', 1, long)
|
||||
// read Linux file system attributes (see man chattr)
|
||||
nix::ioctl_read!(read_attr_fd, b'f', 1, usize);
|
||||
nix::ioctl_read!(read_attr_fd, b'f', 1, libc::c_long);
|
||||
nix::ioctl_write_ptr!(write_attr_fd, b'f', 2, libc::c_long);
|
||||
|
||||
// /usr/include/linux/msdos_fs.h: #define FAT_IOCTL_GET_ATTRIBUTES _IOR('r', 0x10, __u32)
|
||||
// read FAT file system attributes
|
||||
nix::ioctl_read!(read_fat_attr_fd, b'r', 0x10, u32);
|
||||
nix::ioctl_write_ptr!(write_fat_attr_fd, b'r', 0x11, u32);
|
||||
|
||||
// From /usr/include/linux/fs.h
|
||||
// #define FS_IOC_FSGETXATTR _IOR('X', 31, struct fsxattr)
|
||||
|
Loading…
Reference in New Issue
Block a user