Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,8 @@ pub fn parse_params<I: Iterator<Item = OsString>>(
/// `name`: `name` itself, or with --ignore-file-name-case, an entry whose name
/// differs only by case when `name` does not exist.
fn directory_entry(dir: &Path, name: &OsStr, params: &Params) -> OsString {
if params.ignore_file_name_case && !dir.join(name).exists() {
// symlink_metadata(), as exists() says no for a dangling symlink.
if params.ignore_file_name_case && dir.join(name).symlink_metadata().is_err() {
let wanted = fold_file_name_case(name);
let entries = std::fs::read_dir(dir).into_iter().flatten().flatten();
if let Some(entry) = entries
Expand Down
37 changes: 37 additions & 0 deletions tests/by-util/test_diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,43 @@ fn directory_name_matching() {
.no_stdout();
}

// An entry with the exact name is used even if it is a dangling symlink; a
// case variant only stands in for a name that is not there at all.
#[cfg(unix)]
#[test]
fn ignore_file_name_case_keeps_exact_dangling_link() {
let (at, ts) = at_and_ts!();
at.write("foo", "foo\n");
if at.file_exists("FOO") {
// The file system is not case-sensitive: the variants below would
// all be the link itself.
return;
}

// Which entry a directory lists first is up to the file system (oldest
// first, newest first, hashed), so create the link both before and after
// several variants: at least one layout lists a variant first.
for link_first in [true, false] {
let dir = format!("link_first_{link_first}");
at.mkdir(&dir);
if link_first {
at.relative_symlink_file("gone", &format!("{dir}/foo"));
}
for variant in ["FOO", "Foo", "fOO", "foO", "FoO"] {
at.write(&format!("{dir}/{variant}"), "zz\n");
}
if !link_first {
at.relative_symlink_file("gone", &format!("{dir}/foo"));
}

ts.ucmd()
.args(&["--ignore-file-name-case", "foo", &dir])
.fails_with_code(2)
.no_stdout()
.stderr_contains("No such file or directory");
}
}

// Among names differing only by case, the one that is the same on both sides
// is the counterpart, whichever sorts first.
#[test]
Expand Down
Loading