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:
		| @ -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) | ||||
|  | ||||
		Reference in New Issue
	
	Block a user