@@ -1812,6 +1812,8 @@ struct PathData {
18121812 // Result<MetaData> got from symlink_metadata() or metadata() based on config
18131813 md : OnceCell < Option < Metadata > > ,
18141814 ft : OnceCell < Option < FileType > > ,
1815+ // can be used to avoid reading the metadata. Can be also called d_type:
1816+ // https://www.gnu.org/software/libc/manual/html_node/Directory-Entries.html
18151817 de : Option < DirEntry > ,
18161818 // Name of the file - will be empty for . or ..
18171819 display_name : OsString ,
@@ -1907,18 +1909,19 @@ impl PathData {
19071909 }
19081910 }
19091911
1910- fn md ( & self , out : & mut BufWriter < Stdout > ) -> Option < & Metadata > {
1912+ fn get_metadata ( & self , out : & mut BufWriter < Stdout > ) -> Option < & Metadata > {
19111913 self . md
19121914 . get_or_init ( || {
19131915 // check if we can use DirEntry metadata
1916+ // it will avoid a call to stat()
19141917 if !self . must_dereference {
19151918 if let Some ( dir_entry) = & self . de {
19161919 return dir_entry. metadata ( ) . ok ( ) ;
19171920 }
19181921 }
19191922
19201923 // if not, check if we can use Path metadata
1921- match get_metadata ( self . p_buf . as_path ( ) , self . must_dereference ) {
1924+ match get_metadata_with_deref_opt ( self . p_buf . as_path ( ) , self . must_dereference ) {
19221925 Err ( err) => {
19231926 // FIXME: A bit tricky to propagate the result here
19241927 out. flush ( ) . unwrap ( ) ;
@@ -1947,7 +1950,7 @@ impl PathData {
19471950
19481951 fn file_type ( & self , out : & mut BufWriter < Stdout > ) -> Option < & FileType > {
19491952 self . ft
1950- . get_or_init ( || self . md ( out) . map ( |md| md. file_type ( ) ) )
1953+ . get_or_init ( || self . get_metadata ( out) . map ( |md| md. file_type ( ) ) )
19511954 . as_ref ( )
19521955 }
19531956}
@@ -1980,7 +1983,7 @@ pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> {
19801983 // Proper GNU handling is don't show if dereferenced symlink DNE
19811984 // but only for the base dir, for a child dir show, and print ?s
19821985 // in long format
1983- if path_data. md ( & mut out) . is_none ( ) {
1986+ if path_data. get_metadata ( & mut out) . is_none ( ) {
19841987 continue ;
19851988 }
19861989
@@ -2068,12 +2071,14 @@ fn sort_entries(entries: &mut [PathData], config: &Config, out: &mut BufWriter<S
20682071 match config. sort {
20692072 Sort :: Time => entries. sort_by_key ( |k| {
20702073 Reverse (
2071- k. md ( out)
2074+ k. get_metadata ( out)
20722075 . and_then ( |md| get_system_time ( md, config) )
20732076 . unwrap_or ( UNIX_EPOCH ) ,
20742077 )
20752078 } ) ,
2076- Sort :: Size => entries. sort_by_key ( |k| Reverse ( k. md ( out) . map ( |md| md. len ( ) ) . unwrap_or ( 0 ) ) ) ,
2079+ Sort :: Size => {
2080+ entries. sort_by_key ( |k| Reverse ( k. get_metadata ( out) . map ( |md| md. len ( ) ) . unwrap_or ( 0 ) ) ) ;
2081+ }
20772082 // The default sort in GNU ls is case insensitive
20782083 Sort :: Name => entries. sort_by ( |a, b| a. display_name . cmp ( & b. display_name ) ) ,
20792084 Sort :: Version => entries. sort_by ( |a, b| {
@@ -2114,7 +2119,8 @@ fn sort_entries(entries: &mut [PathData], config: &Config, out: &mut BufWriter<S
21142119 !match md {
21152120 None | Some ( None ) => {
21162121 // If it metadata cannot be determined, treat as a file.
2117- get_metadata ( p. p_buf . as_path ( ) , true ) . map_or_else ( |_| false , |m| m. is_dir ( ) )
2122+ get_metadata_with_deref_opt ( p. p_buf . as_path ( ) , true )
2123+ . map_or_else ( |_| false , |m| m. is_dir ( ) )
21182124 }
21192125 Some ( Some ( m) ) => m. is_dir ( ) ,
21202126 }
@@ -2289,7 +2295,7 @@ fn enter_directory(
22892295 Ok ( ( ) )
22902296}
22912297
2292- fn get_metadata ( p_buf : & Path , dereference : bool ) -> std:: io:: Result < Metadata > {
2298+ fn get_metadata_with_deref_opt ( p_buf : & Path , dereference : bool ) -> std:: io:: Result < Metadata > {
22932299 if dereference {
22942300 p_buf. metadata ( )
22952301 } else {
@@ -2303,7 +2309,7 @@ fn display_dir_entry_size(
23032309 out : & mut BufWriter < std:: io:: Stdout > ,
23042310) -> ( usize , usize , usize , usize , usize , usize ) {
23052311 // TODO: Cache/memorize the display_* results so we don't have to recalculate them.
2306- if let Some ( md) = entry. md ( out) {
2312+ if let Some ( md) = entry. get_metadata ( out) {
23072313 let ( size_len, major_len, minor_len) = match display_len_or_rdev ( md, config) {
23082314 SizeOrDeviceId :: Device ( major, minor) => (
23092315 ( major. len ( ) + minor. len ( ) + 2usize ) ,
@@ -2341,7 +2347,7 @@ fn return_total(
23412347 let mut total_size = 0 ;
23422348 for item in items {
23432349 total_size += item
2344- . md ( out)
2350+ . get_metadata ( out)
23452351 . as_ref ( )
23462352 . map_or ( 0 , |md| get_block_size ( md, config) ) ;
23472353 }
@@ -2365,7 +2371,7 @@ fn display_additional_leading_info(
23652371 #[ cfg( unix) ]
23662372 {
23672373 if config. inode {
2368- let i = if let Some ( md) = item. md ( out) {
2374+ let i = if let Some ( md) = item. get_metadata ( out) {
23692375 get_inode ( md)
23702376 } else {
23712377 "?" . to_owned ( )
@@ -2375,7 +2381,7 @@ fn display_additional_leading_info(
23752381 }
23762382
23772383 if config. alloc_size {
2378- let s = if let Some ( md) = item. md ( out) {
2384+ let s = if let Some ( md) = item. get_metadata ( out) {
23792385 display_size ( get_block_size ( md, config) , config)
23802386 } else {
23812387 "?" . to_owned ( )
@@ -2590,7 +2596,7 @@ fn display_item_long(
25902596 if config. dired {
25912597 output_display += " " ;
25922598 }
2593- if let Some ( md) = item. md ( out) {
2599+ if let Some ( md) = item. get_metadata ( out) {
25942600 write ! (
25952601 output_display,
25962602 "{}{} {}" ,
@@ -3017,7 +3023,7 @@ fn classify_file(path: &PathData, out: &mut BufWriter<Stdout>) -> Option<char> {
30173023 } else if file_type. is_file ( )
30183024 // Safe unwrapping if the file was removed between listing and display
30193025 // See https://github.com/uutils/coreutils/issues/5371
3020- && path. md ( out) . map ( file_is_executable) . unwrap_or_default ( )
3026+ && path. get_metadata ( out) . map ( file_is_executable) . unwrap_or_default ( )
30213027 {
30223028 Some ( '*' )
30233029 } else {
@@ -3064,18 +3070,7 @@ fn display_item_name(
30643070 }
30653071
30663072 if let Some ( ls_colors) = & config. color {
3067- let md = path. md ( out) ;
3068- name = if md. is_some ( ) {
3069- color_name ( name, & path. p_buf , md, ls_colors, style_manager)
3070- } else {
3071- color_name (
3072- name,
3073- & path. p_buf ,
3074- path. p_buf . symlink_metadata ( ) . ok ( ) . as_ref ( ) ,
3075- ls_colors,
3076- style_manager,
3077- )
3078- } ;
3073+ name = color_name ( name, path, ls_colors, style_manager, out, None ) ;
30793074 }
30803075
30813076 if config. format != Format :: Long && !more_info. is_empty ( ) {
@@ -3141,28 +3136,22 @@ fn display_item_name(
31413136 // Because we use an absolute path, we can assume this is guaranteed to exist.
31423137 // Otherwise, we use path.md(), which will guarantee we color to the same
31433138 // color of non-existent symlinks according to style_for_path_with_metadata.
3144- if path. md ( out) . is_none ( )
3145- && get_metadata ( target_data. p_buf . as_path ( ) , target_data. must_dereference )
3146- . is_err ( )
3139+ if path. get_metadata ( out) . is_none ( )
3140+ && get_metadata_with_deref_opt (
3141+ target_data. p_buf . as_path ( ) ,
3142+ target_data. must_dereference ,
3143+ )
3144+ . is_err ( )
31473145 {
31483146 name. push_str ( & path. p_buf . read_link ( ) . unwrap ( ) . to_string_lossy ( ) ) ;
31493147 } else {
3150- // Use fn get_metadata instead of md() here and above because ls
3151- // should not exit with an err, if we are unable to obtain the target_metadata
3152- let target_metadata = match get_metadata (
3153- target_data. p_buf . as_path ( ) ,
3154- target_data. must_dereference ,
3155- ) {
3156- Ok ( md) => md,
3157- Err ( _) => path. md ( out) . unwrap ( ) . clone ( ) ,
3158- } ;
3159-
31603148 name. push_str ( & color_name (
31613149 escape_name ( target. as_os_str ( ) , & config. quoting_style ) ,
3162- & target_data. p_buf ,
3163- Some ( & target_metadata) ,
3150+ path,
31643151 ls_colors,
31653152 style_manager,
3153+ out,
3154+ Some ( & target_data) ,
31663155 ) ) ;
31673156 }
31683157 } else {
@@ -3259,17 +3248,56 @@ impl StyleManager {
32593248 }
32603249}
32613250
3262- /// Colors the provided name based on the style determined for the given path.
3251+ fn apply_style_based_on_metadata (
3252+ path : & PathData ,
3253+ md_option : Option < & Metadata > ,
3254+ ls_colors : & LsColors ,
3255+ style_manager : & mut StyleManager ,
3256+ name : & str ,
3257+ ) -> String {
3258+ match ls_colors. style_for_path_with_metadata ( & path. p_buf , md_option) {
3259+ Some ( style) => style_manager. apply_style ( style, name) ,
3260+ None => name. to_owned ( ) ,
3261+ }
3262+ }
3263+
3264+ /// Colors the provided name based on the style determined for the given path
3265+ /// This function is quite long because it tries to leverage DirEntry to avoid
3266+ /// unnecessary calls to stat()
3267+ /// and manages the symlink errors
32633268fn color_name (
32643269 name : String ,
3265- path : & Path ,
3266- md : Option < & Metadata > ,
3270+ path : & PathData ,
32673271 ls_colors : & LsColors ,
32683272 style_manager : & mut StyleManager ,
3273+ out : & mut BufWriter < Stdout > ,
3274+ target_symlink : Option < & PathData > ,
32693275) -> String {
3270- match ls_colors. style_for_path_with_metadata ( path, md) {
3271- Some ( style) => style_manager. apply_style ( style, & name) ,
3272- None => name,
3276+ if !path. must_dereference {
3277+ // If we need to dereference (follow) a symlink, we will need to get the metadata
3278+ if let Some ( de) = & path. de {
3279+ // There is a DirEntry, we don't need to get the metadata for the color
3280+ return match ls_colors. style_for ( de) {
3281+ Some ( style) => style_manager. apply_style ( style, & name) ,
3282+ None => name,
3283+ } ;
3284+ }
3285+ }
3286+
3287+ if let Some ( target) = target_symlink {
3288+ // use the optional target_symlink
3289+ // Use fn get_metadata_with_deref_opt instead of get_metadata() here because ls
3290+ // should not exit with an err, if we are unable to obtain the target_metadata
3291+ let md = get_metadata_with_deref_opt ( target. p_buf . as_path ( ) , path. must_dereference )
3292+ . unwrap_or_else ( |_| target. get_metadata ( out) . unwrap ( ) . clone ( ) ) ;
3293+
3294+ apply_style_based_on_metadata ( path, Some ( & md) , ls_colors, style_manager, & name)
3295+ } else {
3296+ let md_option = path. get_metadata ( out) ;
3297+ let symlink_metadata = path. p_buf . symlink_metadata ( ) . ok ( ) ;
3298+ let md = md_option. or ( symlink_metadata. as_ref ( ) ) ;
3299+
3300+ apply_style_based_on_metadata ( path, md, ls_colors, style_manager, & name)
32733301 }
32743302}
32753303
@@ -3299,7 +3327,7 @@ fn get_security_context(config: &Config, p_buf: &Path, must_dereference: bool) -
32993327 // does not support SELinux.
33003328 // Conforms to the GNU coreutils where a dangling symlink results in exit code 1.
33013329 if must_dereference {
3302- match get_metadata ( p_buf, must_dereference) {
3330+ match get_metadata_with_deref_opt ( p_buf, must_dereference) {
33033331 Err ( err) => {
33043332 // The Path couldn't be dereferenced, so return early and set exit code 1
33053333 // to indicate a minor error
@@ -3364,7 +3392,7 @@ fn calculate_padding_collection(
33643392 for item in items {
33653393 #[ cfg( unix) ]
33663394 if config. inode {
3367- let inode_len = if let Some ( md) = item. md ( out) {
3395+ let inode_len = if let Some ( md) = item. get_metadata ( out) {
33683396 display_inode ( md) . len ( )
33693397 } else {
33703398 continue ;
@@ -3373,7 +3401,7 @@ fn calculate_padding_collection(
33733401 }
33743402
33753403 if config. alloc_size {
3376- if let Some ( md) = item. md ( out) {
3404+ if let Some ( md) = item. get_metadata ( out) {
33773405 let block_size_len = display_size ( get_block_size ( md, config) , config) . len ( ) ;
33783406 padding_collections. block_size = block_size_len. max ( padding_collections. block_size ) ;
33793407 }
@@ -3423,7 +3451,7 @@ fn calculate_padding_collection(
34233451
34243452 for item in items {
34253453 if config. alloc_size {
3426- if let Some ( md) = item. md ( out) {
3454+ if let Some ( md) = item. get_metadata ( out) {
34273455 let block_size_len = display_size ( get_block_size ( md, config) , config) . len ( ) ;
34283456 padding_collections. block_size = block_size_len. max ( padding_collections. block_size ) ;
34293457 }
0 commit comments