From 9a97c18877691f0f17b0fc1b3c0d9b21d2354b14 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sat, 21 Dec 2024 21:18:38 +0100 Subject: [PATCH 1/2] ls: when a file has capabilities (setcap), change the color Should fix tests/ls/capability.sh --- src/uu/ls/src/colors.rs | 16 ++++++++++++++ tests/by-util/test_ls.rs | 47 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/src/uu/ls/src/colors.rs b/src/uu/ls/src/colors.rs index 6c580d18a..0e314e5a4 100644 --- a/src/uu/ls/src/colors.rs +++ b/src/uu/ls/src/colors.rs @@ -156,6 +156,22 @@ pub(crate) fn color_name( target_symlink: Option<&PathData>, wrap: bool, ) -> String { + #[cfg(any(not(unix), target_os = "android", target_os = "macos"))] + let has_capabilities = false; + #[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))] + // Check if the file has capabilities + let has_capabilities = uucore::fsxattr::has_acl(path.p_buf.as_path()); + + // If the file has capabilities, use a specific style for `ca` (capabilities) + if has_capabilities { + if let Some(style) = style_manager + .colors + .style_for_indicator(Indicator::Capabilities) + { + return style_manager.apply_style(Some(style), name, wrap); + } + } + if !path.must_dereference { // If we need to dereference (follow) a symlink, we will need to get the metadata if let Some(de) = &path.de { diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 3b2d46b39..f65078a0d 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -3,6 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. // spell-checker:ignore (words) READMECAREFULLY birthtime doesntexist oneline somebackup lrwx somefile somegroup somehiddenbackup somehiddenfile tabsize aaaaaaaa bbbb cccc dddddddd ncccc neee naaaaa nbcdef nfffff dired subdired tmpfs mdir COLORTERM mexe bcdef mfoo +// spell-checker:ignore (words) fakeroot setcap #![allow( clippy::similar_names, clippy::too_many_lines, @@ -5516,3 +5517,49 @@ fn test_suffix_case_sensitivity() { /* cSpell:enable */ ); } + +#[cfg(all(unix, target_os = "linux"))] +#[test] +fn test_ls_capabilities() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + // Test must be run as root (or with `sudo -E`) + // fakeroot setcap cap_net_bind_service=ep /tmp/file_name + // doesn't trigger an error and fails silently + if scene.cmd("whoami").run().stdout_str() != "root\n" { + return; + } + at.mkdir("test"); + at.mkdir("test/dir"); + at.touch("test/cap_pos"); + at.touch("test/dir/cap_neg"); + at.touch("test/dir/cap_pos"); + + let files = ["test/cap_pos", "test/dir/cap_pos"]; + for file in &files { + scene + .cmd("sudo") + .args(&[ + "-E", + "--non-interactive", + "setcap", + "cap_net_bind_service=ep", + at.plus(file).to_str().unwrap(), + ]) + .succeeds(); + } + + let ls_colors = "di=:ca=30;41"; + + scene + .ucmd() + .env("LS_COLORS", ls_colors) + .arg("--color=always") + .arg("test/cap_pos") + .arg("test/dir") + .succeeds() + .stdout_contains("\x1b[30;41mtest/cap_pos") // spell-checker:disable-line + .stdout_contains("\x1b[30;41mcap_pos") // spell-checker:disable-line + .stdout_does_not_contain("0;41mtest/dir/cap_neg"); // spell-checker:disable-line +} From ffc6eb094a646554fdd2c456a7cfd572e6e4c700 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sat, 21 Dec 2024 22:51:29 +0100 Subject: [PATCH 2/2] ls: Don't call the capabilites features of the system when passed an empty ca= in LS_COLORS In parallel, in the GNU test, adjust the GNU tests as we don't use libcap but xattr instead. --- .../cspell.dictionaries/jargon.wordlist.txt | 3 +++ src/uu/ls/src/colors.rs | 26 +++++++++++-------- util/gnu-patches/tests_ls_no_cap.patch | 22 ++++++++++++++++ 3 files changed, 40 insertions(+), 11 deletions(-) create mode 100644 util/gnu-patches/tests_ls_no_cap.patch diff --git a/.vscode/cspell.dictionaries/jargon.wordlist.txt b/.vscode/cspell.dictionaries/jargon.wordlist.txt index 6dd5483c6..4109630e5 100644 --- a/.vscode/cspell.dictionaries/jargon.wordlist.txt +++ b/.vscode/cspell.dictionaries/jargon.wordlist.txt @@ -10,6 +10,7 @@ bytewise canonicalization canonicalize canonicalizing +capget codepoint codepoints codegen @@ -65,6 +66,7 @@ kibi kibibytes libacl lcase +llistxattr lossily lstat mebi @@ -108,6 +110,7 @@ seedable semver semiprime semiprimes +setcap setfacl shortcode shortcodes diff --git a/src/uu/ls/src/colors.rs b/src/uu/ls/src/colors.rs index 0e314e5a4..4f97e42e2 100644 --- a/src/uu/ls/src/colors.rs +++ b/src/uu/ls/src/colors.rs @@ -156,19 +156,23 @@ pub(crate) fn color_name( target_symlink: Option<&PathData>, wrap: bool, ) -> String { - #[cfg(any(not(unix), target_os = "android", target_os = "macos"))] - let has_capabilities = false; - #[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))] // Check if the file has capabilities - let has_capabilities = uucore::fsxattr::has_acl(path.p_buf.as_path()); - - // If the file has capabilities, use a specific style for `ca` (capabilities) - if has_capabilities { - if let Some(style) = style_manager + #[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))] + { + // Skip checking capabilities if LS_COLORS=ca=: + let capabilities = style_manager .colors - .style_for_indicator(Indicator::Capabilities) - { - return style_manager.apply_style(Some(style), name, wrap); + .style_for_indicator(Indicator::Capabilities); + + let has_capabilities = if capabilities.is_none() { + false + } else { + uucore::fsxattr::has_acl(path.p_buf.as_path()) + }; + + // If the file has capabilities, use a specific style for `ca` (capabilities) + if has_capabilities { + return style_manager.apply_style(capabilities, name, wrap); } } diff --git a/util/gnu-patches/tests_ls_no_cap.patch b/util/gnu-patches/tests_ls_no_cap.patch new file mode 100644 index 000000000..5944e3f56 --- /dev/null +++ b/util/gnu-patches/tests_ls_no_cap.patch @@ -0,0 +1,22 @@ +diff --git a/tests/ls/no-cap.sh b/tests/ls/no-cap.sh +index 3d84c74ff..d1f60e70a 100755 +--- a/tests/ls/no-cap.sh ++++ b/tests/ls/no-cap.sh +@@ -21,13 +21,13 @@ print_ver_ ls + require_strace_ capget + + LS_COLORS=ca=1; export LS_COLORS +-strace -e capget ls --color=always > /dev/null 2> out || fail=1 +-$EGREP 'capget\(' out || skip_ "your ls doesn't call capget" ++strace -e llistxattr ls --color=always > /dev/null 2> out || fail=1 ++$EGREP 'llistxattr\(' out || skip_ "your ls doesn't call llistxattr" + + rm -f out + + LS_COLORS=ca=:; export LS_COLORS +-strace -e capget ls --color=always > /dev/null 2> out || fail=1 +-$EGREP 'capget\(' out && fail=1 ++strace -e llistxattr ls --color=always > /dev/null 2> out || fail=1 ++$EGREP 'llistxattr\(' out && fail=1 + + Exit $fail