From 07a6c45787b5f364c71ac3d4d603740659d8bedc Mon Sep 17 00:00:00 2001 From: ChanTsune <41658782+ChanTsune@users.noreply.github.com> Date: Wed, 6 Nov 2024 16:57:59 +0900 Subject: [PATCH] :boom: Changed ACL-related chunk specifications The current specification for `faCe` chunks does not distinguish between no ACL information and an empty ACL. To address this, we will introduce a new `faCl` chunk. The absence of an `faCe` chunk while an `faCl` chunk exists will indicate an empty ACL. To improve data size efficiency, we will remove the platform specifier from the `faCe` chunk and have the `faCl` chunk hold it instead. The new specifications for the body part of each chunk are as follows: `faCl` chunk `` `faCe` chunk `::::` --- cli/src/chunk/acl.rs | 319 +++++++++++++++++++++++--------- cli/src/command/acl.rs | 28 ++- cli/src/command/commons.rs | 5 +- cli/src/command/extract.rs | 18 +- cli/src/command/list.rs | 4 +- cli/src/command/strip.rs | 1 + cli/src/ext.rs | 46 ++++- cli/src/utils/os/unix/acl.rs | 21 ++- cli/src/utils/os/windows/acl.rs | 86 +++++---- 9 files changed, 365 insertions(+), 163 deletions(-) diff --git a/cli/src/chunk/acl.rs b/cli/src/chunk/acl.rs index 5d9a3642..0ad4b70a 100644 --- a/cli/src/chunk/acl.rs +++ b/cli/src/chunk/acl.rs @@ -11,6 +11,10 @@ use std::{ #[allow(non_upper_case_globals)] pub const faCe: ChunkType = unsafe { ChunkType::from_unchecked(*b"faCe") }; +/// [ChunkType] File Access Control List +#[allow(non_upper_case_globals)] +pub const faCl: ChunkType = unsafe { ChunkType::from_unchecked(*b"faCl") }; + #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] pub enum AcePlatform { General, @@ -53,6 +57,13 @@ impl Display for AcePlatform { } } +impl AcePlatform { + #[inline] + pub(crate) fn to_bytes(&self) -> Vec { + self.to_string().into_bytes() + } +} + impl FromStr for AcePlatform { type Err = core::convert::Infallible; @@ -69,6 +80,14 @@ impl FromStr for AcePlatform { } } +impl TryFrom<&[u8]> for AcePlatform { + type Error = Utf8Error; + + fn try_from(value: &[u8]) -> Result { + Ok(Self::from_str(from_utf8(value)?).expect("Infallible error occurred")) + } +} + #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] pub struct Identifier(pub(crate) String); @@ -131,8 +150,7 @@ impl From for ParseAceError { /// Access Control Entry #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] -pub struct Ace { - pub(crate) platform: AcePlatform, +pub(crate) struct Ace { pub(crate) flags: Flag, pub(crate) owner_type: OwnerType, pub(crate) allow: bool, @@ -146,45 +164,12 @@ impl Ace { } } -impl Flag { - pub(crate) const FLAG_NAME_MAP: &'static [(Flag, &'static [&'static str])] = &[ - (Flag::DEFAULT, &["d", "default"]), - (Flag::FILE_INHERIT, &["file_inherit"]), - (Flag::DIRECTORY_INHERIT, &["directory_inherit"]), - (Flag::ONLY_INHERIT, &["only_inherit"]), - (Flag::LIMIT_INHERIT, &["limit_inherit"]), - (Flag::INHERITED, &["inherited"]), - ]; -} - -impl Permission { - pub(crate) const PERMISSION_NAME_MAP: &'static [(Permission, &'static [&'static str])] = &[ - (Permission::READ, &["r", "read"]), - (Permission::WRITE, &["w", "write"]), - (Permission::EXECUTE, &["x", "execute"]), - (Permission::DELETE, &["delete"]), - (Permission::APPEND, &["append"]), - (Permission::DELETE_CHILD, &["delete_child"]), - (Permission::READATTR, &["readattr"]), - (Permission::WRITEATTR, &["writeattr"]), - (Permission::READEXTATTR, &["readextattr"]), - (Permission::WRITEEXTATTR, &["writeextattr"]), - (Permission::READSECURITY, &["readsecurity"]), - (Permission::WRITESECURITY, &["writesecurity"]), - (Permission::CHOWN, &["chown"]), - (Permission::SYNC, &["sync"]), - (Permission::READ_DATA, &["read_data"]), - (Permission::WRITE_DATA, &["write_data"]), - ]; -} - impl Display for Ace { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!( f, - "{}:{}:{}:{}:{}", - self.platform, + "{}:{}:{}:{}", Flag::FLAG_NAME_MAP .iter() .filter(|(f, _)| self.flags.contains(*f)) @@ -207,8 +192,6 @@ impl FromStr for Ace { #[inline] fn from_str(s: &str) -> Result { let mut it = s.split(':'); - let platform = AcePlatform::from_str(it.next().ok_or(ParseAceError::NotEnoughElement)?) - .expect("Infallible error occurred"); let mut flag_list = it.next().ok_or(ParseAceError::NotEnoughElement)?.split(','); let mut flags = Flag::empty(); for (f, names) in Flag::FLAG_NAME_MAP { @@ -248,7 +231,6 @@ impl FromStr for Ace { return Err(Self::Err::TooManyElement); } Ok(Self { - platform, flags, owner_type: owner, allow, @@ -276,13 +258,108 @@ impl TryFrom<&[u8]> for Ace { } } +/// Access Control Entry with a platform +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +pub(crate) struct AceWithPlatform { + pub(crate) platform: AcePlatform, + pub(crate) ace: Ace, +} + +impl Flag { + pub(crate) const FLAG_NAME_MAP: &'static [(Flag, &'static [&'static str])] = &[ + (Flag::DEFAULT, &["d", "default"]), + (Flag::FILE_INHERIT, &["file_inherit"]), + (Flag::DIRECTORY_INHERIT, &["directory_inherit"]), + (Flag::ONLY_INHERIT, &["only_inherit"]), + (Flag::LIMIT_INHERIT, &["limit_inherit"]), + (Flag::INHERITED, &["inherited"]), + ]; +} + +impl Permission { + pub(crate) const PERMISSION_NAME_MAP: &'static [(Permission, &'static [&'static str])] = &[ + (Permission::READ, &["r", "read"]), + (Permission::WRITE, &["w", "write"]), + (Permission::EXECUTE, &["x", "execute"]), + (Permission::DELETE, &["delete"]), + (Permission::APPEND, &["append"]), + (Permission::DELETE_CHILD, &["delete_child"]), + (Permission::READATTR, &["readattr"]), + (Permission::WRITEATTR, &["writeattr"]), + (Permission::READEXTATTR, &["readextattr"]), + (Permission::WRITEEXTATTR, &["writeextattr"]), + (Permission::READSECURITY, &["readsecurity"]), + (Permission::WRITESECURITY, &["writesecurity"]), + (Permission::CHOWN, &["chown"]), + (Permission::SYNC, &["sync"]), + (Permission::READ_DATA, &["read_data"]), + (Permission::WRITE_DATA, &["write_data"]), + ]; +} + +impl Display for AceWithPlatform { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}:{}", self.platform, self.ace) + } +} + +impl FromStr for AceWithPlatform { + type Err = ParseAceError; + + #[inline] + fn from_str(s: &str) -> Result { + let num_sep = s.chars().filter(|c| c.eq(&':')).count(); + if num_sep == 5 { + let (p, r) = s.split_once(':').ok_or(ParseAceError::NotEnoughElement)?; + let platform = AcePlatform::from_str(p).expect("Infallible error occurred"); + Ok(Self { + platform, + ace: r.parse()?, + }) + } else { + Ok(Self { + platform: AcePlatform::General, + ace: s.parse()?, + }) + } + } +} + +impl TryFrom<&str> for AceWithPlatform { + type Error = ParseAceError; + + #[inline] + fn try_from(value: &str) -> Result { + Self::from_str(value) + } +} + +impl TryFrom<&[u8]> for AceWithPlatform { + type Error = ParseAceError; + + #[inline] + fn try_from(value: &[u8]) -> Result { + let body = from_utf8(value)?; + Self::from_str(body) + } +} + +/// Access Control List +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +pub struct Acl { + pub(crate) platform: AcePlatform, + pub(crate) entries: Vec, +} + #[allow(dead_code)] -pub fn ace_convert_current_platform(src: Ace) -> Ace { - ace_convert_platform(src, AcePlatform::CURRENT) +pub fn acl_convert_current_platform(src: Acl) -> Acl { + let platform = AcePlatform::CURRENT; + ace_convert_platform(src, &platform) } -pub fn ace_convert_platform(src: Ace, to: AcePlatform) -> Ace { - match &to { +pub fn ace_convert_platform(src: Acl, to: &AcePlatform) -> Acl { + match to { AcePlatform::General | AcePlatform::Unknown(_) => ace_to_generic(src), AcePlatform::Windows => ace_to_windows(src), AcePlatform::MacOs => ace_to_macos(src), @@ -331,47 +408,77 @@ fn to_general_permission(src_permission: Permission) -> Permission { permission } -fn ace_to_generic(src: Ace) -> Ace { +fn ace_to_generic(src: Acl) -> Acl { match src.platform { AcePlatform::General => src, - AcePlatform::Windows => Ace { + AcePlatform::Windows => Acl { platform: AcePlatform::General, - flags: Flag::empty(), - owner_type: src.owner_type, - allow: src.allow, - permission: to_general_permission(src.permission), + entries: src + .entries + .into_iter() + .map(|ace| Ace { + flags: Flag::empty(), + owner_type: ace.owner_type, + allow: ace.allow, + permission: to_general_permission(ace.permission), + }) + .collect(), }, - AcePlatform::MacOs => Ace { + AcePlatform::MacOs => Acl { platform: AcePlatform::General, - flags: src.flags & { - let mut macos_flags = Flag::all(); - macos_flags.remove(Flag::DEFAULT); - macos_flags - }, - owner_type: src.owner_type, - allow: src.allow, - permission: to_general_permission(src.permission), + entries: src + .entries + .into_iter() + .map(|ace| Ace { + flags: ace.flags & { + let mut macos_flags = Flag::all(); + macos_flags.remove(Flag::DEFAULT); + macos_flags + }, + owner_type: ace.owner_type, + allow: ace.allow, + permission: to_general_permission(ace.permission), + }) + .collect(), }, - AcePlatform::Linux => Ace { - platform: AcePlatform::Linux, - flags: src.flags & Flag::DEFAULT, - owner_type: src.owner_type, - allow: src.allow, - permission: to_general_permission(src.permission), + AcePlatform::Linux => Acl { + platform: AcePlatform::General, + entries: src + .entries + .into_iter() + .map(|ace| Ace { + flags: ace.flags & Flag::DEFAULT, + owner_type: ace.owner_type, + allow: ace.allow, + permission: to_general_permission(ace.permission), + }) + .collect(), }, - AcePlatform::FreeBSD => Ace { + AcePlatform::FreeBSD => Acl { platform: AcePlatform::General, - flags: src.flags, - owner_type: src.owner_type, - allow: src.allow, - permission: to_general_permission(src.permission), + entries: src + .entries + .into_iter() + .map(|ace| Ace { + flags: ace.flags, + owner_type: ace.owner_type, + allow: ace.allow, + permission: to_general_permission(ace.permission), + }) + .collect(), }, - AcePlatform::Unknown(_) => Ace { + AcePlatform::Unknown(_) => Acl { platform: AcePlatform::General, - flags: Flag::empty(), - owner_type: src.owner_type, - allow: src.allow, - permission: to_general_permission(src.permission), + entries: src + .entries + .into_iter() + .map(|ace| Ace { + flags: Flag::empty(), + owner_type: ace.owner_type, + allow: ace.allow, + permission: to_general_permission(ace.permission), + }) + .collect(), }, } } @@ -425,7 +532,7 @@ const GENERIC_TO_WINDOWS_PERMISSION_TABLE: [(&[Permission], Permission); 3] = [ ), ]; -fn ace_to_windows(src: Ace) -> Ace { +fn ace_to_windows(src: Acl) -> Acl { match src.platform { AcePlatform::Windows => src, AcePlatform::General @@ -434,21 +541,27 @@ fn ace_to_windows(src: Ace) -> Ace { | AcePlatform::FreeBSD | AcePlatform::Unknown(_) => { let src = ace_to_generic(src); - Ace { + Acl { platform: AcePlatform::Windows, - flags: src.flags, - owner_type: src.owner_type, - allow: src.allow, - permission: mapping_permission( - src.permission, - &GENERIC_TO_WINDOWS_PERMISSION_TABLE, - ), + entries: src + .entries + .into_iter() + .map(|ace| Ace { + flags: ace.flags, + owner_type: ace.owner_type, + allow: ace.allow, + permission: mapping_permission( + ace.permission, + &GENERIC_TO_WINDOWS_PERMISSION_TABLE, + ), + }) + .collect(), } } } } -fn ace_to_linux(src: Ace) -> Ace { +fn ace_to_linux(src: Acl) -> Acl { match src.platform { AcePlatform::Linux => src, AcePlatform::General @@ -489,7 +602,7 @@ const GENERIC_TO_MACOS_PERMISSION_TABLE: [(&[Permission], Permission); 3] = [ (&[Permission::EXECUTE], Permission::EXECUTE), ]; -fn ace_to_macos(src: Ace) -> Ace { +fn ace_to_macos(src: Acl) -> Acl { match src.platform { AcePlatform::MacOs => src, AcePlatform::General @@ -498,18 +611,27 @@ fn ace_to_macos(src: Ace) -> Ace { | AcePlatform::FreeBSD | AcePlatform::Unknown(_) => { let src = ace_to_generic(src); - Ace { + Acl { platform: AcePlatform::MacOs, - flags: src.flags, - owner_type: src.owner_type, - allow: src.allow, - permission: mapping_permission(src.permission, &GENERIC_TO_MACOS_PERMISSION_TABLE), + entries: src + .entries + .into_iter() + .map(|ace| Ace { + flags: ace.flags, + owner_type: ace.owner_type, + allow: ace.allow, + permission: mapping_permission( + ace.permission, + &GENERIC_TO_MACOS_PERMISSION_TABLE, + ), + }) + .collect(), } } } } -fn ace_to_freebsd(src: Ace) -> Ace { +fn ace_to_freebsd(src: Acl) -> Acl { match src.platform { AcePlatform::FreeBSD => src, AcePlatform::General @@ -596,10 +718,23 @@ bitflags! { #[cfg(test)] mod tests { use super::*; + #[test] + fn ace_with_platform_to_string_from_str() { + let ace = AceWithPlatform { + platform: AcePlatform::CURRENT, + ace: Ace { + flags: Flag::all(), + owner_type: OwnerType::Owner, + allow: true, + permission: Permission::all(), + }, + }; + assert_eq!(AceWithPlatform::from_str(&ace.to_string()), Ok(ace)); + } + #[test] fn ace_to_string_from_str() { let ace = Ace { - platform: AcePlatform::CURRENT, flags: Flag::all(), owner_type: OwnerType::Owner, allow: true, diff --git a/cli/src/command/acl.rs b/cli/src/command/acl.rs index 2e801efe..c28862b2 100644 --- a/cli/src/command/acl.rs +++ b/cli/src/command/acl.rs @@ -109,7 +109,6 @@ impl AclEntries { fn to_ace(&self) -> Ace { Ace { - platform: AcePlatform::General, flags: if self.default { Flag::DEFAULT } else { @@ -233,8 +232,11 @@ fn archive_get_acl(args: GetAclCommand) -> io::Result<()> { "# group: {}", permission.map(|it| it.gname()).unwrap_or("-") ); - for ace in entry.acl()? { - println!("{}", ace); + for (platform, acl) in entry.acl()? { + println!("# platform: {}", platform); + for ace in acl { + println!("{}", ace); + } } } Ok(()) @@ -302,7 +304,15 @@ where RawChunk: Chunk, RawChunk: From, { - let mut acl = entry.acl().unwrap_or_default(); + let platform = AcePlatform::General; + let platform = &platform; + let mut acls = entry.acl().unwrap_or_default(); + let acl = if let Some(acl) = acls.get_mut(platform) { + acl + } else { + return entry; + }; + let extra_without_known = entry .extra_chunks() .iter() @@ -323,9 +333,15 @@ where log::debug!("Removing ace {}", remove.to_ace()); acl.retain(|it| !remove.is_match(it)); } - let extra_chunks = acl + let mut acl_chunks = Vec::new(); + for (platform, aces) in acls { + acl_chunks.push(RawChunk::from_data(crate::chunk::faCl, platform.to_bytes()).into()); + for ace in aces { + acl_chunks.push(RawChunk::from_data(crate::chunk::faCe, ace.to_bytes()).into()); + } + } + let extra_chunks = acl_chunks .into_iter() - .map(|it| RawChunk::from_data(crate::chunk::faCe, it.to_bytes()).into()) .chain(extra_without_known) .collect::>(); entry.with_extra_chunks(&extra_chunks) diff --git a/cli/src/command/commons.rs b/cli/src/command/commons.rs index 0ce92ea0..d9cfe227 100644 --- a/cli/src/command/commons.rs +++ b/cli/src/command/commons.rs @@ -250,8 +250,9 @@ pub(crate) fn apply_metadata( if keep_options.keep_acl { use crate::chunk; use pna::RawChunk; - let ace_list = utils::acl::get_facl(path)?; - for ace in ace_list { + let acl = utils::acl::get_facl(path)?; + entry.add_extra_chunk(RawChunk::from_data(chunk::faCl, acl.platform.to_bytes())); + for ace in acl.entries { entry.add_extra_chunk(RawChunk::from_data(chunk::faCe, ace.to_bytes())); } } diff --git a/cli/src/command/extract.rs b/cli/src/command/extract.rs index e1d35398..1e537c24 100644 --- a/cli/src/command/extract.rs +++ b/cli/src/command/extract.rs @@ -351,11 +351,23 @@ where windows ))] if keep_options.keep_acl { + use crate::chunk::{acl_convert_current_platform, AcePlatform, Acl}; use crate::ext::*; + use itertools::Itertools; - let acl = item.acl()?; - if !acl.is_empty() { - utils::acl::set_facl(&path, acl)?; + let platform = AcePlatform::CURRENT; + let acls = item.acl()?; + if let Some((platform, acl)) = acls.into_iter().find_or_first(|(p, _)| p.eq(&platform)) + { + if !acl.is_empty() { + utils::acl::set_facl( + &path, + acl_convert_current_platform(Acl { + platform, + entries: acl, + }), + )?; + } } } #[cfg(not(any( diff --git a/cli/src/command/list.rs b/cli/src/command/list.rs index a352f3fc..2ec41447 100644 --- a/cli/src/command/list.rs +++ b/cli/src/command/list.rs @@ -93,7 +93,7 @@ struct TableRow { modified: String, name: String, xattrs: Vec, - acl: Vec, + acl: Vec, privates: Vec, } @@ -184,7 +184,7 @@ where header.path().to_string() }, xattrs: entry.xattrs().to_vec(), - acl: entry.acl()?, + acl: entry.acl_with_platform()?, privates: entry .extra_chunks() .iter() diff --git a/cli/src/command/strip.rs b/cli/src/command/strip.rs index 0f72ee74..4e3791c5 100644 --- a/cli/src/command/strip.rs +++ b/cli/src/command/strip.rs @@ -96,6 +96,7 @@ where .is_some_and(|it| it.is_empty()); let mut keep_private_chunks = Vec::new(); if options.keep_acl { + keep_private_chunks.push(crate::chunk::faCl); keep_private_chunks.push(crate::chunk::faCe); } if let Some(chunks) = &options.keep_private { diff --git a/cli/src/ext.rs b/cli/src/ext.rs index cacf56d1..8ca98af8 100644 --- a/cli/src/ext.rs +++ b/cli/src/ext.rs @@ -1,9 +1,23 @@ -use crate::chunk::{self, Ace}; +use crate::chunk::{self, Ace, AcePlatform, AceWithPlatform}; use pna::{prelude::*, NormalEntry, RawChunk}; +use std::collections::HashMap; use std::io; pub(crate) trait NormalEntryExt { - fn acl(&self) -> io::Result>; + fn acl(&self) -> io::Result>>; + fn acl_with_platform(&self) -> io::Result> { + let acl = self.acl()?; + Ok(acl + .into_iter() + .map(|(platform, ace)| { + ace.into_iter().map(move |it| AceWithPlatform { + platform: platform.clone(), + ace: it, + }) + }) + .flatten() + .collect()) + } } impl NormalEntryExt for NormalEntry @@ -11,11 +25,27 @@ where RawChunk: Chunk, { #[inline] - fn acl(&self) -> io::Result> { - self.extra_chunks() - .iter() - .filter(|c| c.ty() == chunk::faCe) - .map(|c| Ace::try_from(c.data()).map_err(io::Error::other)) - .collect() + fn acl(&self) -> io::Result>> { + let mut acls = HashMap::new(); + let mut platform = AcePlatform::General; + for c in self.extra_chunks().iter() { + match c.ty() { + chunk::faCl => { + platform = AcePlatform::try_from(c.data()).map_err(io::Error::other)? + } + chunk::faCe => { + let ace = AceWithPlatform::try_from(c.data()).map_err(io::Error::other)?; + if ace.platform != platform { + acls.entry(ace.platform) + } else { + acls.entry(platform.clone()) + } + .or_insert_with(Vec::new) + .push(ace.ace); + } + _ => continue, + } + } + Ok(acls) } } diff --git a/cli/src/utils/os/unix/acl.rs b/cli/src/utils/os/unix/acl.rs index ac0f1cc0..c71380a7 100644 --- a/cli/src/utils/os/unix/acl.rs +++ b/cli/src/utils/os/unix/acl.rs @@ -1,12 +1,11 @@ -use crate::chunk::{ - ace_convert_current_platform, Ace, AcePlatform, Flag, Identifier, OwnerType, Permission, -}; +use crate::chunk::{Ace, AcePlatform, Acl, Flag, Identifier, OwnerType, Permission}; use std::io; use std::path::Path; -pub fn set_facl>(path: P, acl: Vec) -> io::Result<()> { +pub fn set_facl>(path: P, acl: Acl) -> io::Result<()> { let path = path.as_ref(); - let mut acl_entries: Vec = acl.into_iter().map(Into::into).collect::>(); + let mut acl_entries: Vec = + acl.entries.into_iter().map(Into::into).collect::>(); #[cfg(target_os = "macos")] { use std::os::unix::fs::MetadataExt; @@ -86,9 +85,12 @@ pub fn set_facl>(path: P, acl: Vec) -> io::Result<()> { exacl::setfacl(&[path], &acl_entries, None) } -pub fn get_facl>(path: P) -> io::Result> { +pub fn get_facl>(path: P) -> io::Result { let ace_list = exacl::getfacl(path.as_ref(), None)?; - Ok(ace_list.into_iter().map(Into::into).collect()) + Ok(Acl { + platform: AcePlatform::CURRENT, + entries: ace_list.into_iter().map(Into::into).collect(), + }) } #[allow(clippy::from_over_into)] @@ -183,7 +185,6 @@ impl Into for exacl::AclEntry { } Ace { - platform: AcePlatform::CURRENT, flags, owner_type: match self.kind { exacl::AclEntryKind::User if self.name.is_empty() => OwnerType::Owner, @@ -207,7 +208,7 @@ impl Into for exacl::AclEntry { #[allow(clippy::from_over_into)] impl Into for Ace { fn into(self) -> exacl::AclEntry { - let slf = ace_convert_current_platform(self); + let slf = self; let (kind, name) = match slf.owner_type { OwnerType::Owner => (exacl::AclEntryKind::User, String::new()), OwnerType::User(u) => (exacl::AclEntryKind::User, u.0), @@ -337,7 +338,7 @@ mod tests { fn ace_mutual_convert() { let acl_entry = exacl::AclEntry { kind: exacl::AclEntryKind::User, - name: "name".to_string(), + name: "name".into(), perms: exacl::Perm::all(), flags: exacl::Flag::all(), allow: false, diff --git a/cli/src/utils/os/windows/acl.rs b/cli/src/utils/os/windows/acl.rs index f73927ae..267b0b63 100644 --- a/cli/src/utils/os/windows/acl.rs +++ b/cli/src/utils/os/windows/acl.rs @@ -1,10 +1,9 @@ -use crate::chunk; -use crate::chunk::{ace_convert_current_platform, AcePlatform, Identifier, OwnerType}; -use crate::utils::os::windows::security::{SecurityDescriptor, Sid, SidType}; +use crate::{ + chunk::{self, AcePlatform, Identifier, OwnerType}, + utils::os::windows::security::{SecurityDescriptor, Sid, SidType}, +}; use field_offset::offset_of; -use std::path::Path; -use std::ptr::null_mut; -use std::{io, mem}; +use std::{io, mem, path::Path, ptr::null_mut}; use windows::Win32::Security::{ AddAccessAllowedAceEx, AddAccessDeniedAceEx, GetAce, InitializeAcl, ACCESS_ALLOWED_ACE, ACCESS_DENIED_ACE, ACE_FLAGS, ACE_HEADER, ACL as Win32ACL, ACL_REVISION_DS, @@ -19,21 +18,25 @@ use windows::Win32::Storage::FileSystem::{ }; use windows::Win32::System::SystemServices::{ACCESS_ALLOWED_ACE_TYPE, ACCESS_DENIED_ACE_TYPE}; -pub fn set_facl>(path: P, ace_list: Vec) -> io::Result<()> { +pub fn set_facl>(path: P, ace_list: chunk::Acl) -> io::Result<()> { let acl = ACL::try_from(path.as_ref())?; let group_sid = acl.security_descriptor.group_sid()?; let owner_sid = acl.security_descriptor.owner_sid()?; let acl_entries = ace_list + .entries .into_iter() .map(|it| it.into_acl_entry_with(&owner_sid, &group_sid)) .collect::>(); acl.set_d_acl(&acl_entries) } -pub fn get_facl>(path: P) -> io::Result> { +pub fn get_facl>(path: P) -> io::Result { let acl = ACL::try_from(path.as_ref())?; let ace_list = acl.get_d_acl()?; - Ok(ace_list.into_iter().map(Into::into).collect()) + Ok(chunk::Acl { + platform: AcePlatform::Windows, + entries: ace_list.into_iter().map(Into::into).collect(), + }) } #[allow(non_camel_case_types)] @@ -186,7 +189,7 @@ const FLAGS_MAPPING_TABLE: [(chunk::Flag, ACE_FLAGS); 6] = [ impl chunk::Ace { fn into_acl_entry_with(self, owner_sid: &Sid, group_sid: &Sid) -> ACLEntry { - let slf = ace_convert_current_platform(self); + let slf = self; let sid = match slf.owner_type { OwnerType::Owner => owner_sid.clone(), OwnerType::User(i) => Sid::try_from_name(&i.0, None).unwrap(), @@ -235,7 +238,6 @@ impl Into for ACLEntry { t => panic!("Unsupported ace type {:?}", t), }; chunk::Ace { - platform: AcePlatform::Windows, flags: { let mut flags = chunk::Flag::empty(); for (f, g) in FLAGS_MAPPING_TABLE { @@ -276,7 +278,7 @@ impl Into for ACLEntry { #[cfg(test)] mod tests { use super::*; - use crate::chunk::Ace; + use crate::chunk::{Ace, Acl}; #[test] fn acl_for_everyone() { let path = "everyone.txt"; @@ -285,41 +287,45 @@ mod tests { set_facl( &path, - vec![Ace { + Acl { platform: AcePlatform::General, - flags: chunk::Flag::empty(), - owner_type: OwnerType::Group(Identifier(sid.name.clone())), - allow: true, - permission: chunk::Permission::READ - | chunk::Permission::WRITE - | chunk::Permission::EXECUTE, - }], + entries: vec![Ace { + flags: chunk::Flag::empty(), + owner_type: OwnerType::Group(Identifier(sid.name.clone())), + allow: true, + permission: chunk::Permission::READ + | chunk::Permission::WRITE + | chunk::Permission::EXECUTE, + }], + }, ) .unwrap(); let acl = get_facl(&path).unwrap(); - assert_eq!(acl.len(), 1); + assert_eq!(acl.entries.len(), 1); assert_eq!( - &acl[0], - &Ace { + &acl.entries[0], + &Acl { platform: AcePlatform::Windows, - flags: chunk::Flag::empty(), - owner_type: OwnerType::Group(Identifier(sid.name)), - allow: true, - permission: chunk::Permission::READ - | chunk::Permission::WRITE - | chunk::Permission::EXECUTE - | chunk::Permission::DELETE - | chunk::Permission::APPEND - | chunk::Permission::READATTR - | chunk::Permission::WRITEATTR - | chunk::Permission::READEXTATTR - | chunk::Permission::WRITEEXTATTR - | chunk::Permission::READSECURITY - | chunk::Permission::WRITESECURITY - | chunk::Permission::SYNC - | chunk::Permission::READ_DATA - | chunk::Permission::WRITE_DATA, + entries: vec![Ace { + flags: chunk::Flag::empty(), + owner_type: OwnerType::Group(Identifier(sid.name)), + allow: true, + permission: chunk::Permission::READ + | chunk::Permission::WRITE + | chunk::Permission::EXECUTE + | chunk::Permission::DELETE + | chunk::Permission::APPEND + | chunk::Permission::READATTR + | chunk::Permission::WRITEATTR + | chunk::Permission::READEXTATTR + | chunk::Permission::WRITEEXTATTR + | chunk::Permission::READSECURITY + | chunk::Permission::WRITESECURITY + | chunk::Permission::SYNC + | chunk::Permission::READ_DATA + | chunk::Permission::WRITE_DATA, + }] } ); }