1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-27 02:57:44 +00:00

ls: fix the GNU test tests/ls/selinux.sh (#8281)

* ls: fix the GNU test tests/ls/selinux.sh

* Remove old comment

Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com>

* simpli

Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com>

---------

Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com>
This commit is contained in:
Sylvestre Ledru 2025-07-02 11:12:26 +02:00 committed by GitHub
parent 0923b92921
commit d04d0cd987
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 114 additions and 9 deletions

View file

@ -52,4 +52,4 @@ name = "ls"
path = "src/main.rs" path = "src/main.rs"
[features] [features]
feat_selinux = ["selinux"] feat_selinux = ["selinux", "uucore/selinux"]

View file

@ -1900,11 +1900,7 @@ impl PathData {
None => OnceCell::new(), None => OnceCell::new(),
}; };
let security_context = if config.context { let security_context = get_security_context(config, &p_buf, must_dereference);
get_security_context(config, &p_buf, must_dereference)
} else {
String::new()
};
Self { Self {
md: OnceCell::new(), md: OnceCell::new(),
@ -3339,7 +3335,10 @@ fn get_security_context(config: &Config, p_buf: &Path, must_dereference: bool) -
Err(err) => { Err(err) => {
// The Path couldn't be dereferenced, so return early and set exit code 1 // The Path couldn't be dereferenced, so return early and set exit code 1
// to indicate a minor error // to indicate a minor error
show!(LsError::IOErrorContext(p_buf.to_path_buf(), err, false)); // Only show error when context display is requested to avoid duplicate messages
if config.context {
show!(LsError::IOErrorContext(p_buf.to_path_buf(), err, false));
}
return substitute_string; return substitute_string;
} }
Ok(_md) => (), Ok(_md) => (),

View file

@ -4263,8 +4263,7 @@ fn test_ls_context_long() {
let line: Vec<_> = result.stdout_str().split(' ').collect(); let line: Vec<_> = result.stdout_str().split(' ').collect();
assert!(line[0].ends_with('.')); assert!(line[0].ends_with('.'));
assert!(line[4].starts_with("unconfined_u")); assert!(line[4].starts_with("unconfined_u"));
let s: Vec<_> = line[4].split(':').collect(); validate_selinux_context(line[4]);
assert!(s.len() == 4);
} }
} }
@ -4298,6 +4297,113 @@ fn test_ls_context_format() {
} }
} }
/// Helper function to validate `SELinux` context format
#[cfg(feature = "feat_selinux")]
fn validate_selinux_context(context: &str) {
assert!(
context.contains(':'),
"Expected SELinux context format (user:role:type:level), got: {}",
context
);
assert_eq!(
context.split(':').count(),
4,
"SELinux context should have 4 components separated by colons, got: {}",
context
);
}
#[test]
#[cfg(feature = "feat_selinux")]
fn test_ls_selinux_context_format() {
if !uucore::selinux::is_selinux_enabled() {
println!("test skipped: Kernel has no support for SElinux context");
return;
}
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("file");
at.symlink_file("file", "link");
// Test that ls -lnZ properly shows the context
for file in ["file", "link"] {
let result = scene.ucmd().args(&["-lnZ", file]).succeeds();
let output = result.stdout_str();
let lines: Vec<&str> = output.lines().collect();
assert!(!lines.is_empty(), "Output should contain at least one line");
let first_line = lines[0];
let parts: Vec<&str> = first_line.split_whitespace().collect();
assert!(parts.len() >= 6, "Line should have at least 6 fields");
// The 5th field (0-indexed position 4) should contain the SELinux context
// Format: permissions links owner group context size date time name
let context = parts[4];
validate_selinux_context(context);
}
}
#[test]
#[cfg(feature = "feat_selinux")]
fn test_ls_selinux_context_indicator() {
if !uucore::selinux::is_selinux_enabled() {
println!("test skipped: Kernel has no support for SElinux context");
return;
}
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("file");
at.symlink_file("file", "link");
// Test that ls -l shows "." indicator for files with SELinux contexts
for file in ["file", "link"] {
let result = scene.ucmd().args(&["-l", file]).succeeds();
let output = result.stdout_str();
// The 11th character should be "." indicating SELinux context
// -rw-rw-r--. (permissions + context indicator)
let lines: Vec<&str> = output.lines().collect();
assert!(!lines.is_empty(), "Output should contain at least one line");
let first_line = lines[0];
let chars: Vec<char> = first_line.chars().collect();
assert!(
chars.len() >= 11,
"Line should be at least 11 characters long"
);
// The 11th character (0-indexed position 10) should be "." for SELinux context
assert_eq!(
chars[10], '.',
"Expected '.' indicator for SELinux context in position 11, got '{}' in line: {}",
chars[10], first_line
);
}
// Test that ls -lnZ properly shows the context
for file in ["file", "link"] {
let result = scene.ucmd().args(&["-lnZ", file]).succeeds();
let output = result.stdout_str();
let lines: Vec<&str> = output.lines().collect();
assert!(!lines.is_empty(), "Output should contain at least one line");
let first_line = lines[0];
let parts: Vec<&str> = first_line.split_whitespace().collect();
assert!(parts.len() >= 6, "Line should have at least 6 fields");
// The 5th field (0-indexed position 4) should contain the SELinux context
// Format: permissions links owner group context size date time name
validate_selinux_context(parts[4]);
}
}
#[test] #[test]
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn test_ls_a_A() { fn test_ls_a_A() {