Skip to content

Commit 89bc8c6

Browse files
committed
find: Fix -printf interactions with -L
- %l should only trigger for broken symlinks - %Y should follow symlinks
1 parent f870d27 commit 89bc8c6

File tree

2 files changed

+52
-31
lines changed

2 files changed

+52
-31
lines changed

src/find/matchers/mod.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ pub enum Follow {
8484
Roots,
8585
/// Always follow symlinks (-L).
8686
Always,
87+
/// Follow all links, treating broken links as errors (-printf %Y).
88+
Force,
8789
}
8890

8991
impl Follow {
@@ -93,14 +95,20 @@ impl Follow {
9395
Self::Never => false,
9496
Self::Roots => depth == 0,
9597
Self::Always => true,
98+
Self::Force => true,
9699
}
97100
}
98101

99102
/// Get metadata for a [WalkEntry].
100103
pub fn metadata(self, entry: &WalkEntry) -> Result<Metadata, WalkError> {
101104
if self.follow_at_depth(entry.depth()) == entry.follow() {
102-
// Same follow flag, re-use cached metadata
103-
entry.metadata().cloned()
105+
if self == Self::Force && entry.file_type().is_symlink() {
106+
// Broken link, return an error
107+
self.metadata_at_depth(entry.path(), entry.depth())
108+
} else {
109+
// Same follow flag, re-use cached metadata
110+
entry.metadata().cloned()
111+
}
104112
} else if !entry.follow() && !entry.file_type().is_symlink() {
105113
// Not a symlink, re-use cached metadata
106114
entry.metadata().cloned()
@@ -126,7 +134,11 @@ impl Follow {
126134
let path = path.as_ref();
127135

128136
if self.follow_at_depth(depth) {
129-
match path.metadata().map_err(WalkError::from) {
137+
let meta = path.metadata().map_err(WalkError::from);
138+
if self == Self::Force {
139+
return meta;
140+
}
141+
match meta {
130142
Ok(meta) => return Ok(meta),
131143
Err(e) if !e.is_not_found() => return Err(e),
132144
_ => {}

src/find/matchers/printf.rs

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use std::{borrow::Cow, io::Write};
1212

1313
use chrono::{format::StrftimeItems, DateTime, Local};
1414

15-
use super::{FileType, Matcher, MatcherIO, WalkEntry, WalkError};
15+
use super::{FileType, Follow, Matcher, MatcherIO, WalkEntry};
1616

1717
#[cfg(unix)]
1818
use std::os::unix::prelude::MetadataExt;
@@ -359,18 +359,6 @@ fn get_starting_point(file_info: &WalkEntry) -> &Path {
359359
.unwrap()
360360
}
361361

362-
fn format_non_link_file_type(file_type: FileType) -> char {
363-
match file_type {
364-
FileType::Regular => 'f',
365-
FileType::Directory => 'd',
366-
FileType::BlockDevice => 'b',
367-
FileType::CharDevice => 'c',
368-
FileType::Fifo => 'p',
369-
FileType::Socket => 's',
370-
_ => 'U',
371-
}
372-
}
373-
374362
fn format_directive<'entry>(
375363
file_info: &'entry WalkEntry,
376364
directive: &FormatDirective,
@@ -524,7 +512,7 @@ fn format_directive<'entry>(
524512
FormatDirective::StartingPoint => get_starting_point(file_info).to_string_lossy(),
525513

526514
FormatDirective::SymlinkTarget => {
527-
if file_info.path_is_symlink() {
515+
if file_info.file_type().is_symlink() {
528516
fs::read_link(file_info.path())?
529517
.to_string_lossy()
530518
.into_owned()
@@ -534,22 +522,43 @@ fn format_directive<'entry>(
534522
}
535523
}
536524

537-
FormatDirective::Type { follow_links } => if file_info.path_is_symlink() {
538-
if *follow_links {
539-
match file_info.path().metadata().map_err(WalkError::from) {
540-
Ok(meta) => format_non_link_file_type(meta.file_type().into()),
541-
Err(e) if e.is_not_found() => 'N',
542-
Err(e) if e.is_loop() => 'L',
543-
Err(_) => '?',
544-
}
525+
FormatDirective::Type { follow_links } => {
526+
let follow = if *follow_links {
527+
Follow::Force
528+
} else if file_info.follow() {
529+
Follow::Always
545530
} else {
546-
'l'
547-
}
548-
} else {
549-
format_non_link_file_type(file_info.file_type())
531+
Follow::Never
532+
};
533+
let meta = follow.metadata(file_info);
534+
let ftype = meta
535+
.as_ref()
536+
.map(|m| m.file_type().into())
537+
.unwrap_or(FileType::Unknown);
538+
539+
let ret = match ftype {
540+
FileType::Regular => "f",
541+
FileType::Directory => "d",
542+
FileType::BlockDevice => "b",
543+
FileType::CharDevice => "c",
544+
FileType::Fifo => "p",
545+
FileType::Socket => "s",
546+
FileType::Symlink => "l",
547+
FileType::Unknown if *follow_links => {
548+
match meta {
549+
Err(e) if e.is_not_found() => "N",
550+
Err(e) if e.is_loop() => "L",
551+
Err(_) => {
552+
// TODO: matcher_io.set_exit_code(1);
553+
"?"
554+
}
555+
_ => "U",
556+
}
557+
}
558+
FileType::Unknown => "U",
559+
};
560+
ret.into()
550561
}
551-
.to_string()
552-
.into(),
553562

554563
#[cfg(not(unix))]
555564
FormatDirective::User { .. } => "0".into(),

0 commit comments

Comments
 (0)