From 2dbba78b9828485423f41a87412675c330fc2dd7 Mon Sep 17 00:00:00 2001 From: Christian Ebner Date: Thu, 23 May 2019 18:09:15 +0200 Subject: [PATCH] src/pxar/sequential_decoder.rs: impl support to dump/restore ACLs from pxar archives Signed-off-by: Christian Ebner --- src/pxar/sequential_decoder.rs | 191 ++++++++++++++++++++++++++------- 1 file changed, 151 insertions(+), 40 deletions(-) diff --git a/src/pxar/sequential_decoder.rs b/src/pxar/sequential_decoder.rs index 309eae7c..dbe99837 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::acl; use crate::tools::xattr; // This one need Read, but works without Seek @@ -166,8 +167,16 @@ impl <'a, R: Read> SequentialDecoder<'a, R> { } fn restore_attributes(&mut self, entry: &CaFormatEntry, fd: RawFd) -> Result { - let mut xattrs: Vec = Vec::new(); - let mut fcaps: Option = None; + let mut xattrs = Vec::new(); + let mut fcaps = None; + + let mut acl_user = Vec::new(); + let mut acl_group = Vec::new(); + let mut acl_group_obj = None; + + let mut acl_default = None; + let mut acl_default_user = Vec::new(); + let mut acl_default_group = Vec::new(); let mut head: CaFormatHeader = self.read_item()?; let mut size = (head.size - HEADER_SIZE) as usize; @@ -187,6 +196,48 @@ impl <'a, R: Read> SequentialDecoder<'a, R> { self.skip_bytes(size)?; } }, + CA_FORMAT_ACL_USER => { + if self.has_features(CA_FORMAT_WITH_ACL) { + acl_user.push(self.read_item::()?); + } else { + self.skip_bytes(size)?; + } + }, + CA_FORMAT_ACL_GROUP => { + if self.has_features(CA_FORMAT_WITH_ACL) { + acl_group.push(self.read_item::()?); + } else { + self.skip_bytes(size)?; + } + }, + CA_FORMAT_ACL_GROUP_OBJ => { + if self.has_features(CA_FORMAT_WITH_ACL) { + acl_group_obj = Some(self.read_item::()?); + } else { + self.skip_bytes(size)?; + } + }, + CA_FORMAT_ACL_DEFAULT => { + if self.has_features(CA_FORMAT_WITH_ACL) { + acl_default = Some(self.read_item::()?); + } else { + self.skip_bytes(size)?; + } + }, + CA_FORMAT_ACL_DEFAULT_USER => { + if self.has_features(CA_FORMAT_WITH_ACL) { + acl_default_user.push(self.read_item::()?); + } else { + self.skip_bytes(size)?; + } + }, + CA_FORMAT_ACL_DEFAULT_GROUP => { + if self.has_features(CA_FORMAT_WITH_ACL) { + acl_default_group.push(self.read_item::()?); + } else { + self.skip_bytes(size)?; + } + }, _ => break, } head = self.read_item()?; @@ -194,6 +245,50 @@ impl <'a, R: Read> SequentialDecoder<'a, R> { } self.restore_xattrs_fcaps_fd(fd, xattrs, fcaps)?; + let mut acl = acl::ACL::init(5)?; + acl.add_entry_full(acl::ACL_USER_OBJ, None, mode_user_to_acl_permissions(entry.mode))?; + acl.add_entry_full(acl::ACL_OTHER, None, mode_other_to_acl_permissions(entry.mode))?; + match acl_group_obj { + Some(group_obj) => { + acl.add_entry_full(acl::ACL_MASK, None, mode_group_to_acl_permissions(entry.mode))?; + acl.add_entry_full(acl::ACL_GROUP_OBJ, None, group_obj.permissions)?; + }, + None => { + acl.add_entry_full(acl::ACL_GROUP_OBJ, None, mode_group_to_acl_permissions(entry.mode))?; + }, + } + for user in acl_user { + acl.add_entry_full(acl::ACL_USER, Some(user.uid), user.permissions)?; + } + for group in acl_group { + acl.add_entry_full(acl::ACL_GROUP, Some(group.gid), group.permissions)?; + } + let proc_path = Path::new("/proc/self/fd/").join(fd.to_string()); + if !acl.is_valid() { + bail!("Error while restoring ACL - ACL invalid"); + } + acl.set_file(&proc_path, acl::ACL_TYPE_ACCESS)?; + + if let Some(default) = acl_default { + let mut acl = acl::ACL::init(5)?; + acl.add_entry_full(acl::ACL_USER_OBJ, None, default.user_obj_permissions)?; + acl.add_entry_full(acl::ACL_GROUP_OBJ, None, default.group_obj_permissions)?; + acl.add_entry_full(acl::ACL_OTHER, None, default.other_permissions)?; + if default.mask_permissions != std::u64::MAX { + acl.add_entry_full(acl::ACL_MASK, None, default.mask_permissions)?; + } + for user in acl_default_user { + acl.add_entry_full(acl::ACL_USER, Some(user.uid), user.permissions)?; + } + for group in acl_default_group { + acl.add_entry_full(acl::ACL_GROUP, Some(group.gid), group.permissions)?; + } + if !acl.is_valid() { + bail!("Error while restoring ACL - ACL invalid"); + } + acl.set_file(&proc_path, acl::ACL_TYPE_DEFAULT)?; + } + Ok(head) } @@ -580,7 +675,6 @@ impl <'a, R: Read> SequentialDecoder<'a, R> { if verbose { println!("Mode: {:08x} {:08x}", entry.mode, (entry.mode as u32) & libc::S_IFDIR); } - // fixme: dump attributes (ACLs, ...) let ifmt = (entry.mode as u32) & libc::S_IFMT; @@ -593,22 +687,16 @@ impl <'a, R: Read> SequentialDecoder<'a, R> { if verbose { print_head(&head); } + + // This call covers all the cases of the match statement + // regarding extended attributes. These calls will never + // break on the loop and can therefore be handled separately. + // If the header was matched, true is returned and we can continue + if self.dump_if_attribute(&head, verbose)? { + continue; + } + match head.htype { - //TODO verify the correct order of occurrence - CA_FORMAT_XATTR => { - let size = (head.size - HEADER_SIZE) as usize; - let xattr: CaFormatXAttr = self.read_xattr(size)?; - if verbose { - println!("XAttr: {:?}: {:?}", String::from_utf8_lossy(&xattr.name), String::from_utf8_lossy(&xattr.value)); - } - } - CA_FORMAT_FCAPS => { - let size = (head.size - HEADER_SIZE) as usize; - let fcaps: CaFormatFCaps = self.read_fcaps(size)?; - if verbose { - println!("FCaps: {:?}", fcaps); - } - } CA_FORMAT_FILENAME => { let name = self.read_filename(head.size)?; if verbose { println!("Name: {:?}", name); } @@ -616,7 +704,7 @@ impl <'a, R: Read> SequentialDecoder<'a, R> { path.push(&name); self.dump_entry(path, verbose, output)?; path.pop(); - } + }, CA_FORMAT_GOODBYE => { let table_size = (head.size - HEADER_SIZE) as usize; if verbose { @@ -626,10 +714,8 @@ impl <'a, R: Read> SequentialDecoder<'a, R> { self.skip_bytes(table_size)?; } break; - } - _ => { - panic!("got unexpected header type inside directory"); - } + }, + _ => panic!("got unexpected header type inside directory"), } } } else if (ifmt == libc::S_IFBLK) || (ifmt == libc::S_IFCHR) || @@ -641,36 +727,29 @@ impl <'a, R: Read> SequentialDecoder<'a, R> { print_head(&head); } - match head.htype { + // This call covers all the cases of the match statement + // regarding extended attributes. These calls will never + // break on the loop and can therefore be handled separately. + // If the header was matched, true is returned and we can continue + if self.dump_if_attribute(&head, verbose)? { + continue; + } + match head.htype { CA_FORMAT_SYMLINK => { let target = self.read_link(head.size)?; if verbose { println!("Symlink: {:?}", target); } break; - } + }, CA_FORMAT_DEVICE => { let device: CaFormatDevice = self.read_item()?; if verbose { println!("Device: {}, {}", device.major, device.minor); } break; - } - CA_FORMAT_XATTR => { - let size = (head.size - HEADER_SIZE) as usize; - let xattr: CaFormatXAttr = self.read_xattr(size)?; - if verbose { - println!("XAttr: {:?}: {:?}", String::from_utf8_lossy(&xattr.name), String::from_utf8_lossy(&xattr.value)); - } - } - CA_FORMAT_FCAPS => { - let size = (head.size - HEADER_SIZE) as usize; - let fcaps: CaFormatFCaps = self.read_fcaps(size)?; - if verbose { - println!("FCaps: {:?}", fcaps); - } - } + }, CA_FORMAT_PAYLOAD => { let payload_size = (head.size - HEADER_SIZE) as usize; if verbose { @@ -698,6 +777,26 @@ impl <'a, R: Read> SequentialDecoder<'a, R> { Ok(()) } + fn dump_if_attribute(&mut self, header: &CaFormatHeader, verbose: bool) -> Result { + let dump_string = match header.htype { + CA_FORMAT_XATTR => format!("XAttr: {:?}", self.read_xattr((header.size - HEADER_SIZE) as usize)?), + CA_FORMAT_FCAPS => format!("FCaps: {:?}", self.read_fcaps((header.size - HEADER_SIZE) as usize)?), + CA_FORMAT_ACL_USER => format!("ACLUser: {:?}", self.read_item::()?), + CA_FORMAT_ACL_GROUP => format!("ACLGroup: {:?}", self.read_item::()?), + CA_FORMAT_ACL_GROUP_OBJ => format!("ACLGroupObj: {:?}", self.read_item::()?), + CA_FORMAT_ACL_DEFAULT => format!("ACLDefault: {:?}", self.read_item::()?), + CA_FORMAT_ACL_DEFAULT_USER => format!("ACLDefaultUser: {:?}", self.read_item::()?), + CA_FORMAT_ACL_DEFAULT_GROUP => format!("ACLDefaultGroup: {:?}", self.read_item::()?), + _ => return Ok(false), + }; + let flags = CA_FORMAT_WITH_XATTRS | CA_FORMAT_WITH_FCAPS | CA_FORMAT_WITH_ACL; + if verbose && self.has_features(flags) { + println!("{}", dump_string); + } + + Ok(true) + } + fn dump_goodby_entries( &mut self, entry_count: usize, @@ -812,3 +911,15 @@ fn nsec_to_update_timespec(mtime_nsec: u64) -> [libc::timespec; 2] { times } + +fn mode_user_to_acl_permissions(mode: u64) -> u64 { + return (mode >> 6) & 7; +} + +fn mode_group_to_acl_permissions(mode: u64) -> u64 { + return (mode >> 3) & 7; +} + +fn mode_other_to_acl_permissions(mode: u64) -> u64 { + return mode & 7; +}