From e7b60a16c8e8c4e24bd2f27d8d1f0094437bcf32 Mon Sep 17 00:00:00 2001 From: Christian Ebner Date: Wed, 29 May 2019 14:34:05 +0200 Subject: [PATCH] pxar: impl storing/dumping/restoring of quota project ids Allows to store/dump/restore the quota project id associated with an inode in order to correctly restore project quotas. The project id is obtained/set via ioctl calls getting/setting the fsxattr associated with the given file descriptor. Signed-off-by: Christian Ebner --- src/pxar/encoder.rs | 46 ++++++++++++++++++++++++++++++++++ src/pxar/sequential_decoder.rs | 33 ++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/src/pxar/encoder.rs b/src/pxar/encoder.rs index 9ed4f2c7..1428f053 100644 --- a/src/pxar/encoder.rs +++ b/src/pxar/encoder.rs @@ -9,6 +9,7 @@ use std::collections::HashMap; use super::format_definition::*; use super::binary_search_tree::*; use super::helper::*; +use crate::tools::fs; use crate::tools::acl; use crate::tools::xattr; @@ -396,6 +397,34 @@ impl <'a, W: Write> Encoder<'a, W> { }) } + /// Read the project quota id for an inode, supported on ext4/XFS/FUSE/(ZFS TODO impl) filesystems + fn read_quota_project_id(&self, fd: RawFd, magic: i64, stat: &FileStat) -> Result, Error> { + if !(is_directory(&stat) || is_reg_file(&stat)) { + return Ok(None); + } + if !self.has_features(CA_FORMAT_WITH_QUOTA_PROJID) { + return Ok(None); + } + let projid = match magic { + //TODO ZFS quota + EXT4_SUPER_MAGIC | XFS_SUPER_MAGIC | FUSE_SUPER_MAGIC => { + let mut fsxattr = fs::FSXAttr::default(); + unsafe { + fs::fs_ioc_fsgetxattr(fd, &mut fsxattr) + .map_err(|err| format_err!("error while reading quota project id for {:#?} - {}", self.full_path(), err))?; + } + fsxattr.fsx_projid as u64 + }, + _ => return Ok(None), + }; + println!("Encountered projid: {} for {:#?}", projid, self.full_path()); + if projid == 0 { + return Ok(None); + } + + Ok(Some(CaFormatQuotaProjID { projid })) + } + fn write_entry(&mut self, entry: CaFormatEntry) -> Result<(), Error> { self.write_header(CA_FORMAT_ENTRY, std::mem::size_of::() as u64)?; @@ -466,6 +495,13 @@ impl <'a, W: Write> Encoder<'a, W> { Ok(()) } + fn write_quota_project_id(&mut self, projid: CaFormatQuotaProjID) -> Result<(), Error> { + self.write_header(CA_FORMAT_QUOTA_PROJID, std::mem::size_of::() as u64)?; + self.write_item(projid)?; + + Ok(()) + } + fn write_goodbye_table(&mut self, goodbye_offset: usize, goodbye_items: &mut [CaFormatGoodbyeItem]) -> Result<(), Error> { goodbye_items.sort_unstable_by(|a, b| a.hash.cmp(&b.hash)); @@ -522,6 +558,7 @@ impl <'a, W: Write> Encoder<'a, W> { let (xattrs, fcaps) = self.read_xattrs(rawfd, &dir_stat)?; let acl_access = self.read_acl(rawfd, &dir_stat, acl::ACL_TYPE_ACCESS)?; let acl_default = self.read_acl(rawfd, &dir_stat, acl::ACL_TYPE_DEFAULT)?; + let projid = self.read_quota_project_id(rawfd, magic, &dir_stat)?; self.write_entry(dir_entry)?; for xattr in xattrs { @@ -548,6 +585,9 @@ impl <'a, W: Write> Encoder<'a, W> { if let Some(default) = acl_default.default { self.write_acl_default(default)?; } + if let Some(projid) = projid { + self.write_quota_project_id(projid)?; + } let mut dir_count = 0; @@ -733,6 +773,7 @@ impl <'a, W: Write> Encoder<'a, W> { self.read_fat_attr(filefd, magic, &mut entry)?; let (xattrs, fcaps) = self.read_xattrs(filefd, &stat)?; let acl_access = self.read_acl(filefd, &stat, acl::ACL_TYPE_ACCESS)?; + let projid = self.read_quota_project_id(filefd, magic, &stat)?; self.write_entry(entry)?; for xattr in xattrs { @@ -748,6 +789,9 @@ impl <'a, W: Write> Encoder<'a, W> { if let Some(group_obj) = acl_access.group_obj { self.write_acl_group_obj(group_obj)?; } + if let Some(projid) = projid { + self.write_quota_project_id(projid)?; + } let include_payload; if is_virtual_file_system(magic) { @@ -912,6 +956,8 @@ pub const TMPFS_MAGIC: i64 = 0x01021994; pub const SYSFS_MAGIC: i64 = 0x62656572; pub const MSDOS_SUPER_MAGIC: i64 = 0x00004d44; pub const FUSE_SUPER_MAGIC: i64 = 0x65735546; +pub const EXT4_SUPER_MAGIC: i64 = 0xEF53; +pub const XFS_SUPER_MAGIC: i64 = 0x58465342; #[inline(always)] diff --git a/src/pxar/sequential_decoder.rs b/src/pxar/sequential_decoder.rs index 49e210b4..8f01031d 100644 --- a/src/pxar/sequential_decoder.rs +++ b/src/pxar/sequential_decoder.rs @@ -23,6 +23,7 @@ use nix::NixPath; use crate::tools::io::ops::*; use crate::tools::vec; +use crate::tools::fs; use crate::tools::acl; use crate::tools::xattr; @@ -169,6 +170,7 @@ impl <'a, R: Read> SequentialDecoder<'a, R> { fn restore_attributes(&mut self, entry: &CaFormatEntry, fd: RawFd) -> Result { let mut xattrs = Vec::new(); let mut fcaps = None; + let mut quota_projid = None; let mut acl_user = Vec::new(); let mut acl_group = Vec::new(); @@ -238,6 +240,13 @@ impl <'a, R: Read> SequentialDecoder<'a, R> { self.skip_bytes(size)?; } }, + CA_FORMAT_QUOTA_PROJID => { + if self.has_features(CA_FORMAT_WITH_QUOTA_PROJID) { + quota_projid = Some(self.read_item::()?); + } else { + self.skip_bytes(size)?; + } + }, _ => break, } head = self.read_item()?; @@ -288,6 +297,7 @@ impl <'a, R: Read> SequentialDecoder<'a, R> { } acl.set_file(&proc_path, acl::ACL_TYPE_DEFAULT)?; } + self.restore_quota_projid(fd, quota_projid)?; Ok(head) } @@ -307,6 +317,23 @@ impl <'a, R: Read> SequentialDecoder<'a, R> { Ok(()) } + fn restore_quota_projid(&mut self, fd: RawFd, projid: Option) -> Result<(), Error> { + if let Some(projid) = projid { + let mut fsxattr = fs::FSXAttr::default(); + unsafe { + fs::fs_ioc_fsgetxattr(fd, &mut fsxattr) + .map_err(|err| format_err!("error while getting fsxattr to restore quota project id - {}", err))?; + } + fsxattr.fsx_projid = projid.projid as u32; + unsafe { + fs::fs_ioc_fssetxattr(fd, &fsxattr) + .map_err(|err| format_err!("error while setting fsxattr to restore quota project id - {}", err))?; + } + } + + Ok(()) + } + fn restore_mode(&mut self, entry: &CaFormatEntry, fd: RawFd) -> Result<(), Error> { let mode = Mode::from_bits_truncate((entry.mode as u32) & 0o7777); @@ -827,6 +854,12 @@ impl <'a, R: Read> SequentialDecoder<'a, R> { println!("ACLDefaultGroup: {:?}", default_group); } }, + CA_FORMAT_QUOTA_PROJID => { + let quota_projid = self.read_item::()?; + if verbose && self.has_features(CA_FORMAT_WITH_QUOTA_PROJID) { + println!("Quota project id: {:?}", quota_projid); + } + }, _ => return Ok(false), }