Skip to content

Commit 8bd40c8

Browse files
committed
Lazily obtain security context
1 parent 97d7d47 commit 8bd40c8

File tree

1 file changed

+33
-22
lines changed

1 file changed

+33
-22
lines changed

src/uu/ls/src/ls.rs

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1780,7 +1780,7 @@ struct PathData {
17801780
// PathBuf that all above data corresponds to
17811781
p_buf: PathBuf,
17821782
must_dereference: bool,
1783-
security_context: String,
1783+
security_context: OnceCell<String>,
17841784
command_line: bool,
17851785
}
17861786

@@ -1844,16 +1844,14 @@ impl PathData {
18441844
None => OnceCell::new(),
18451845
};
18461846

1847-
let security_context = get_security_context(config, &p_buf, must_dereference);
1848-
18491847
Self {
18501848
md: OnceCell::new(),
18511849
ft,
18521850
de,
18531851
display_name,
18541852
p_buf,
18551853
must_dereference,
1856-
security_context,
1854+
security_context: OnceCell::new(),
18571855
command_line,
18581856
}
18591857
}
@@ -1917,6 +1915,12 @@ impl PathData {
19171915
self.file_type().is_some_and(|f| f.is_file())
19181916
&& self.metadata().is_some_and(file_is_executable)
19191917
}
1918+
1919+
fn security_context(&self, config: &Config) -> &String {
1920+
self.security_context.get_or_init(|| {
1921+
get_security_context(config, &self.p_buf, self.metadata(), self.must_dereference)
1922+
})
1923+
}
19201924
}
19211925

19221926
/// Show the directory name in the case where several arguments are given to ls
@@ -2460,7 +2464,7 @@ fn display_items(
24602464
let mut longest_context_len = 1;
24612465
let prefix_context = if config.context {
24622466
for item in items {
2463-
let context_len = item.security_context.len();
2467+
let context_len = item.security_context(config).len();
24642468
longest_context_len = context_len.max(longest_context_len);
24652469
}
24662470
Some(longest_context_len)
@@ -2708,7 +2712,7 @@ fn display_item_long(
27082712
#[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))]
27092713
let is_acl_set = has_acl(item.display_name.as_os_str());
27102714
output_display.extend(display_permissions(md, true).as_bytes());
2711-
if item.security_context.len() > 1 {
2715+
if item.security_context(config).len() > 1 {
27122716
// GNU `ls` uses a "." character to indicate a file with a security context,
27132717
// but not other alternate access method.
27142718
output_display.extend(b".");
@@ -2730,7 +2734,7 @@ fn display_item_long(
27302734

27312735
if config.context {
27322736
output_display.extend(b" ");
2733-
output_display.extend_pad_right(&item.security_context, padding.context);
2737+
output_display.extend_pad_right(&item.security_context(config), padding.context);
27342738
}
27352739

27362740
// Author is only different from owner on GNU/Hurd, so we reuse
@@ -2842,7 +2846,7 @@ fn display_item_long(
28422846

28432847
output_display.extend(leading_char.as_bytes());
28442848
output_display.extend(b"?????????");
2845-
if item.security_context.len() > 1 {
2849+
if item.security_context(config).len() > 1 {
28462850
// GNU `ls` uses a "." character to indicate a file with a security context,
28472851
// but not other alternate access method.
28482852
output_display.extend(b".");
@@ -2862,7 +2866,7 @@ fn display_item_long(
28622866

28632867
if config.context {
28642868
output_display.extend(b" ");
2865-
output_display.extend_pad_right(&item.security_context, padding.context);
2869+
output_display.extend_pad_right(item.security_context(config), padding.context);
28662870
}
28672871

28682872
// Author is only different from owner on GNU/Hurd, so we reuse
@@ -3182,9 +3186,9 @@ fn display_item_name(
31823186
if config.context {
31833187
if let Some(pad_count) = prefix_context {
31843188
let security_context = if matches!(config.format, Format::Commas) {
3185-
path.security_context.clone()
3189+
path.security_context(config).to_owned()
31863190
} else {
3187-
pad_left(&path.security_context, pad_count)
3191+
pad_left(path.security_context(config), pad_count).to_owned()
31883192
};
31893193
let old_name = name;
31903194
name = format!("{security_context} ").into();
@@ -3245,23 +3249,30 @@ fn display_inode(metadata: &Metadata) -> String {
32453249

32463250
/// This returns the `SELinux` security context as UTF8 `String`.
32473251
/// In the long term this should be changed to [`OsStr`], see discussions at #2621/#2656
3248-
fn get_security_context(config: &Config, p_buf: &Path, must_dereference: bool) -> String {
3252+
fn get_security_context(
3253+
config: &Config,
3254+
p_buf: &Path,
3255+
opt_metadata: Option<&Metadata>,
3256+
must_dereference: bool,
3257+
) -> String {
32493258
let substitute_string = "?".to_string();
32503259
// If we must dereference, ensure that the symlink is actually valid even if the system
32513260
// does not support SELinux.
32523261
// Conforms to the GNU coreutils where a dangling symlink results in exit code 1.
32533262
if must_dereference {
3254-
match get_metadata_with_deref_opt(p_buf, must_dereference) {
3255-
Err(err) => {
3256-
// The Path couldn't be dereferenced, so return early and set exit code 1
3257-
// to indicate a minor error
3258-
// Only show error when context display is requested to avoid duplicate messages
3259-
if config.context {
3260-
show!(LsError::IOErrorContext(p_buf.to_path_buf(), err, false));
3263+
if opt_metadata.is_none() {
3264+
match get_metadata_with_deref_opt(p_buf, must_dereference) {
3265+
Err(err) => {
3266+
// The Path couldn't be dereferenced, so return early and set exit code 1
3267+
// to indicate a minor error
3268+
// Only show error when context display is requested to avoid duplicate messages
3269+
if config.context {
3270+
show!(LsError::IOErrorContext(p_buf.to_path_buf(), err, false));
3271+
}
3272+
return substitute_string;
32613273
}
3262-
return substitute_string;
3274+
Ok(_md) => (),
32633275
}
3264-
Ok(_md) => (),
32653276
}
32663277
}
32673278
if config.selinux_supported {
@@ -3335,7 +3346,7 @@ fn calculate_padding_collection(
33353346
}
33363347

33373348
if config.format == Format::Long {
3338-
let context_len = item.security_context.len();
3349+
let context_len = item.security_context(config).len();
33393350
let (link_count_len, uname_len, group_len, size_len, major_len, minor_len) =
33403351
display_dir_entry_size(item, config, state);
33413352
padding_collections.link_count = link_count_len.max(padding_collections.link_count);

0 commit comments

Comments
 (0)