Skip to content

Commit d3db8dc

Browse files
authored
Merge pull request #6987 from sylvestre/setcap
ls: when a file has capabilities (setcap), change the color
2 parents e736c2a + ffc6eb0 commit d3db8dc

File tree

4 files changed

+92
-0
lines changed

4 files changed

+92
-0
lines changed

.vscode/cspell.dictionaries/jargon.wordlist.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ bytewise
1010
canonicalization
1111
canonicalize
1212
canonicalizing
13+
capget
1314
codepoint
1415
codepoints
1516
codegen
@@ -65,6 +66,7 @@ kibi
6566
kibibytes
6667
libacl
6768
lcase
69+
llistxattr
6870
lossily
6971
lstat
7072
mebi
@@ -108,6 +110,7 @@ seedable
108110
semver
109111
semiprime
110112
semiprimes
113+
setcap
111114
setfacl
112115
shortcode
113116
shortcodes

src/uu/ls/src/colors.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,26 @@ pub(crate) fn color_name(
156156
target_symlink: Option<&PathData>,
157157
wrap: bool,
158158
) -> String {
159+
// Check if the file has capabilities
160+
#[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))]
161+
{
162+
// Skip checking capabilities if LS_COLORS=ca=:
163+
let capabilities = style_manager
164+
.colors
165+
.style_for_indicator(Indicator::Capabilities);
166+
167+
let has_capabilities = if capabilities.is_none() {
168+
false
169+
} else {
170+
uucore::fsxattr::has_acl(path.p_buf.as_path())
171+
};
172+
173+
// If the file has capabilities, use a specific style for `ca` (capabilities)
174+
if has_capabilities {
175+
return style_manager.apply_style(capabilities, name, wrap);
176+
}
177+
}
178+
159179
if !path.must_dereference {
160180
// If we need to dereference (follow) a symlink, we will need to get the metadata
161181
if let Some(de) = &path.de {

tests/by-util/test_ls.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// For the full copyright and license information, please view the LICENSE
44
// file that was distributed with this source code.
55
// 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
6+
// spell-checker:ignore (words) fakeroot setcap
67
#![allow(
78
clippy::similar_names,
89
clippy::too_many_lines,
@@ -5516,3 +5517,49 @@ fn test_suffix_case_sensitivity() {
55165517
/* cSpell:enable */
55175518
);
55185519
}
5520+
5521+
#[cfg(all(unix, target_os = "linux"))]
5522+
#[test]
5523+
fn test_ls_capabilities() {
5524+
let scene = TestScenario::new(util_name!());
5525+
let at = &scene.fixtures;
5526+
5527+
// Test must be run as root (or with `sudo -E`)
5528+
// fakeroot setcap cap_net_bind_service=ep /tmp/file_name
5529+
// doesn't trigger an error and fails silently
5530+
if scene.cmd("whoami").run().stdout_str() != "root\n" {
5531+
return;
5532+
}
5533+
at.mkdir("test");
5534+
at.mkdir("test/dir");
5535+
at.touch("test/cap_pos");
5536+
at.touch("test/dir/cap_neg");
5537+
at.touch("test/dir/cap_pos");
5538+
5539+
let files = ["test/cap_pos", "test/dir/cap_pos"];
5540+
for file in &files {
5541+
scene
5542+
.cmd("sudo")
5543+
.args(&[
5544+
"-E",
5545+
"--non-interactive",
5546+
"setcap",
5547+
"cap_net_bind_service=ep",
5548+
at.plus(file).to_str().unwrap(),
5549+
])
5550+
.succeeds();
5551+
}
5552+
5553+
let ls_colors = "di=:ca=30;41";
5554+
5555+
scene
5556+
.ucmd()
5557+
.env("LS_COLORS", ls_colors)
5558+
.arg("--color=always")
5559+
.arg("test/cap_pos")
5560+
.arg("test/dir")
5561+
.succeeds()
5562+
.stdout_contains("\x1b[30;41mtest/cap_pos") // spell-checker:disable-line
5563+
.stdout_contains("\x1b[30;41mcap_pos") // spell-checker:disable-line
5564+
.stdout_does_not_contain("0;41mtest/dir/cap_neg"); // spell-checker:disable-line
5565+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
diff --git a/tests/ls/no-cap.sh b/tests/ls/no-cap.sh
2+
index 3d84c74ff..d1f60e70a 100755
3+
--- a/tests/ls/no-cap.sh
4+
+++ b/tests/ls/no-cap.sh
5+
@@ -21,13 +21,13 @@ print_ver_ ls
6+
require_strace_ capget
7+
8+
LS_COLORS=ca=1; export LS_COLORS
9+
-strace -e capget ls --color=always > /dev/null 2> out || fail=1
10+
-$EGREP 'capget\(' out || skip_ "your ls doesn't call capget"
11+
+strace -e llistxattr ls --color=always > /dev/null 2> out || fail=1
12+
+$EGREP 'llistxattr\(' out || skip_ "your ls doesn't call llistxattr"
13+
14+
rm -f out
15+
16+
LS_COLORS=ca=:; export LS_COLORS
17+
-strace -e capget ls --color=always > /dev/null 2> out || fail=1
18+
-$EGREP 'capget\(' out && fail=1
19+
+strace -e llistxattr ls --color=always > /dev/null 2> out || fail=1
20+
+$EGREP 'llistxattr\(' out && fail=1
21+
22+
Exit $fail

0 commit comments

Comments
 (0)