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 <c.ebner@proxmox.com>
This commit is contained in:
Christian Ebner 2019-05-29 14:34:05 +02:00 committed by Dietmar Maurer
parent 7312ab9eef
commit e7b60a16c8
2 changed files with 79 additions and 0 deletions

View File

@ -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<Option<CaFormatQuotaProjID>, 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::<CaFormatEntry>() 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::<CaFormatQuotaProjID>() 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)]

View File

@ -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<CaFormatHeader, Error> {
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::<CaFormatQuotaProjID>()?);
} 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<CaFormatQuotaProjID>) -> 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::<CaFormatQuotaProjID>()?;
if verbose && self.has_features(CA_FORMAT_WITH_QUOTA_PROJID) {
println!("Quota project id: {:?}", quota_projid);
}
},
_ => return Ok(false),
}