Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/uu/ls/src/colors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ pub(crate) fn color_name(
let has_capabilities = style_manager
.colors
.has_explicit_style_for(Indicator::Capabilities)
&& uucore::fsxattr::has_security_cap_acl(&path.p_buf);
&& uucore::fsxattr::has_security_cap_acl_with_deref(&path.p_buf, path.must_dereference);

// If the file has capabilities, use a specific style for `ca` (capabilities)
if has_capabilities {
Expand Down
6 changes: 3 additions & 3 deletions src/uu/ls/src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use term_grid::{DEFAULT_SEPARATOR_SIZE, Direction, Filling, Grid, GridOptions};
#[cfg(unix)]
use uucore::entries;
#[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))]
use uucore::fsxattr::has_acl;
use uucore::fsxattr::has_acl_with_deref;
#[cfg(unix)]
use uucore::libc::{dev_t, major, minor};
use uucore::{
Expand Down Expand Up @@ -934,7 +934,7 @@ fn display_item_long(
// TODO: See how Mac should work here
let is_acl_set = false;
#[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))]
let is_acl_set = has_acl(item.path());
let is_acl_set = has_acl_with_deref(item.path(), item.must_dereference);
state
.display_buf
.extend(display_permissions(md, true).as_bytes());
Expand Down Expand Up @@ -1339,7 +1339,7 @@ fn calculate_padding_collection(
// TODO: See how Mac should work here
let is_acl_set = false;
#[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))]
let is_acl_set = has_acl(item.path());
let is_acl_set = has_acl_with_deref(item.path(), item.must_dereference);
if context_len > 1 || is_acl_set {
padding_collections.permissions = PERMISSIONS_WIDTH + 1;
}
Expand Down
32 changes: 28 additions & 4 deletions src/uucore/src/lib/features/fsxattr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,30 +156,49 @@ pub fn apply_xattrs<P: AsRef<Path>>(
/// # Arguments
///
/// * `file` - A reference to the path of the file.
/// * `dereference` - Whether to inspect the target of a symlink instead of the link itself.
///
/// # Returns
///
/// `true` if the file has extended attributes (indicating an ACL), `false` otherwise.
pub fn has_acl<P: AsRef<Path>>(file: P) -> bool {
pub fn has_acl_with_deref<P: AsRef<Path>>(file: P, dereference: bool) -> bool {
// don't use exacl here, it is doing more getxattr call then needed
xattr::list_deref(file).is_ok_and(|acl| {
let attrs = if dereference {
xattr::list_deref(file)
} else {
xattr::list(file)
};

attrs.is_ok_and(|acl| {
// if we have extra attributes, we have an acl
acl.count() > 0
})
}

/// Checks if a file has an Access Control List (ACL) based on its extended attributes.
pub fn has_acl<P: AsRef<Path>>(file: P) -> bool {
has_acl_with_deref(file, true)
}

/// Checks if a file has an Access Control List (ACL) named "security.capability" based on its extended attributes.
///
/// # Arguments
///
/// * `file` - A reference to the path of the file.
/// * `dereference` - Whether to inspect the target of a symlink instead of the link itself.
///
/// # Returns
///
/// `true` if the file has an extended attribute named "security.capability", `false` otherwise.
pub fn has_security_cap_acl<P: AsRef<Path>>(file: P) -> bool {
pub fn has_security_cap_acl_with_deref<P: AsRef<Path>>(file: P, dereference: bool) -> bool {
// don't use exacl here, it is doing more getxattr call then needed
xattr::list_deref(file).is_ok_and(|mut acl| {
let attrs = if dereference {
xattr::list_deref(file)
} else {
xattr::list(file)
};

attrs.is_ok_and(|mut acl| {
#[cfg(unix)]
return acl.contains(OsStr::from_bytes(b"security.capability"));

Expand All @@ -188,6 +207,11 @@ pub fn has_security_cap_acl<P: AsRef<Path>>(file: P) -> bool {
})
}

/// Checks if a file has an Access Control List (ACL) named "security.capability" based on its extended attributes.
pub fn has_security_cap_acl<P: AsRef<Path>>(file: P) -> bool {
has_security_cap_acl_with_deref(file, true)
}

/// Returns the permissions bits of a file or directory which has Access Control List (ACL) entries based on its
/// extended attributes (Only works for linux)
///
Expand Down
38 changes: 38 additions & 0 deletions tests/by-util/test_ls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6985,6 +6985,44 @@ fn test_acl_display_symlink() {
assert!(iter.all(|i| i == first));
}

#[cfg(all(unix, not(target_os = "macos")))]
#[test]
fn test_acl_display_symlink_without_dereference() {
use std::process::Command;

let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;

let dir_name = "dir";
let link_name = "link";
at.mkdir(dir_name);

match Command::new("setfacl")
.args(["-d", "-m", "u:bin:rwx", &at.plus_as_string(dir_name)])
.status()
.map(|status| status.code())
{
Ok(Some(0)) => {}
Ok(_) => {
println!("test skipped: setfacl failed");
return;
}
Err(e) => {
println!("test skipped: setfacl failed with {e}");
return;
}
}

at.symlink_dir(dir_name, link_name);

scene
.ucmd()
.arg("-ld")
.arg(link_name)
.succeeds()
.stdout_does_not_contain("+");
}

#[test]
fn ls_emoji_alignment() {
let scene = TestScenario::new(util_name!());
Expand Down
Loading