@@ -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