diff --git a/src/meta/access_control.rs b/src/meta/access_control.rs index 493025a7a..75c9bdc8e 100644 --- a/src/meta/access_control.rs +++ b/src/meta/access_control.rs @@ -4,6 +4,7 @@ use std::path::Path; #[derive(Clone, Debug)] pub struct AccessControl { has_acl: bool, + has_xattr: bool, selinux_context: String, smack_context: String, } @@ -11,7 +12,7 @@ pub struct AccessControl { impl AccessControl { #[cfg(not(unix))] pub fn for_path(_: &Path) -> Self { - Self::from_data(false, &[], &[]) + Self::from_data(false, false, &[], &[]) } #[cfg(unix)] @@ -27,14 +28,27 @@ impl AccessControl { .unwrap_or_default() .unwrap_or_default(); - Self::from_data(has_acl, &selinux_context, &smack_context) + let has_xattr = has_acl + || !selinux_context.is_empty() + || !smack_context.is_empty() + || xattr::list(path) + .map(|x| x.into_iter().count() > 0) + .unwrap_or(false); + + Self::from_data(has_acl, has_xattr, &selinux_context, &smack_context) } - fn from_data(has_acl: bool, selinux_context: &[u8], smack_context: &[u8]) -> Self { + fn from_data( + has_acl: bool, + has_xattr: bool, + selinux_context: &[u8], + smack_context: &[u8], + ) -> Self { let selinux_context = String::from_utf8_lossy(selinux_context).to_string(); let smack_context = String::from_utf8_lossy(smack_context).to_string(); Self { has_acl, + has_xattr, selinux_context, smack_context, } @@ -43,6 +57,8 @@ impl AccessControl { pub fn render_method(&self, colors: &Colors) -> ColoredString { if self.has_acl { colors.colorize('+', &Elem::Acl) + } else if self.has_xattr { + colors.colorize('@', &Elem::Acl) } else if !self.selinux_context.is_empty() || !self.smack_context.is_empty() { colors.colorize('.', &Elem::Context) } else { @@ -92,7 +108,7 @@ mod test { #[test] fn test_acl_only_indicator() { // actual file would collide with proper AC data, no permission to scrub those - let access_control = AccessControl::from_data(true, &[], &[]); + let access_control = AccessControl::from_data(true, false, &[], &[]); assert_eq!( String::from("+").with(Color::DarkCyan), @@ -100,9 +116,28 @@ mod test { ); } + #[test] + fn test_extended_attr_indicator() { + let access_control = AccessControl::from_data(false, true, &[], &[]); + + assert_eq!( + String::from("@").with(Color::DarkCyan), + access_control.render_method(&Colors::new(ThemeOption::Default)) + ); + } + + #[test] + fn test_extended_attr_with_acl_indicator() { + let access_control = AccessControl::from_data(true, true, &[], &[]); + + assert_eq!( + String::from("+").with(Color::DarkCyan), + access_control.render_method(&Colors::new(ThemeOption::Default)) + ); + } #[test] fn test_smack_only_indicator() { - let access_control = AccessControl::from_data(false, &[], &[b'a']); + let access_control = AccessControl::from_data(false, false, &[], &[b'a']); assert_eq!( String::from(".").with(Color::Cyan), @@ -112,7 +147,7 @@ mod test { #[test] fn test_acl_and_selinux_indicator() { - let access_control = AccessControl::from_data(true, &[b'a'], &[]); + let access_control = AccessControl::from_data(true, false, &[b'a'], &[]); assert_eq!( String::from("+").with(Color::DarkCyan), @@ -122,7 +157,7 @@ mod test { #[test] fn test_selinux_context() { - let access_control = AccessControl::from_data(false, &[b'a'], &[]); + let access_control = AccessControl::from_data(false, false, &[b'a'], &[]); assert_eq!( String::from("a").with(Color::Cyan), @@ -132,7 +167,7 @@ mod test { #[test] fn test_selinux_and_smack_context() { - let access_control = AccessControl::from_data(false, &[b'a'], &[b'b']); + let access_control = AccessControl::from_data(false, false, &[b'a'], &[b'b']); assert_eq!( String::from("a+b").with(Color::Cyan), @@ -142,7 +177,7 @@ mod test { #[test] fn test_no_context() { - let access_control = AccessControl::from_data(false, &[], &[]); + let access_control = AccessControl::from_data(false, false, &[], &[]); assert_eq!( String::from("?").with(Color::Cyan),