Skip to content

Commit 5565ef0

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

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 == Follow::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 == Follow::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,
@@ -529,7 +517,7 @@ fn format_directive<'entry>(
529517
FormatDirective::StartingPoint => get_starting_point(file_info).to_string_lossy(),
530518

531519
FormatDirective::SymlinkTarget => {
532-
if file_info.path_is_symlink() {
520+
if file_info.file_type().is_symlink() {
533521
fs::read_link(file_info.path())?
534522
.to_string_lossy()
535523
.into_owned()
@@ -539,22 +527,43 @@ fn format_directive<'entry>(
539527
}
540528
}
541529

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

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

0 commit comments

Comments
 (0)