tail: fix -F to properly track symlinks with changing targets (#10158)

Co-authored-by: Sylvestre Ledru <sylvestre@debian.org>
This commit is contained in:
Chris Dryden
2026-02-01 22:26:08 +01:00
committed by GitHub
co-authored by Sylvestre Ledru
parent 507a524c40
commit 8bb31eeab9
2 changed files with 43 additions and 0 deletions
+7
View File
@@ -277,6 +277,10 @@ impl Observer {
// If `path` is not a tailable file, add its parent to `Watcher`.
watcher_rx
.watch(path.parent().unwrap(), RecursiveMode::NonRecursive)?;
// Add symlinks to orphans for retry polling (target may not exist)
if path.is_symlink() {
self.orphans.push(path);
}
} else {
// If there is no parent, add `path` to `orphans`.
self.orphans.push(path);
@@ -374,6 +378,9 @@ impl Observer {
}
}
self.files.update_metadata(event_path, Some(new_md));
} else if event_path.is_symlink() && settings.retry {
self.files.reset_reader(event_path);
self.orphans.push(event_path.clone());
}
}
EventKind::Remove(RemoveKind::File | RemoveKind::Any)
+36
View File
@@ -5149,3 +5149,39 @@ fn test_debug_flag_with_inotify() {
.with_all_output()
.stderr_contains("tail: using notification mode");
}
#[test]
#[cfg(target_os = "linux")]
fn test_follow_dangling_symlink() {
let (at, mut ucmd) = at_and_ucmd!();
at.symlink_file("target", "link");
let mut p = ucmd
.args(&["-s.1", "--max-unchanged-stats=1", "-F", "link"])
.run_no_wait();
p.delay(500);
at.write("target", "X\n");
p.delay(500);
p.kill().make_assertion().with_all_output().stdout_is("X\n");
}
#[test]
#[cfg(target_os = "linux")]
fn test_follow_symlink_target_change() {
let (at, mut ucmd) = at_and_ucmd!();
at.write("t1", "A\n");
at.symlink_file("t1", "link");
let mut p = ucmd
.args(&["-s.1", "--max-unchanged-stats=1", "-F", "link"])
.run_no_wait();
p.delay(500);
at.remove("link");
at.symlink_file("t2", "link");
p.delay(500);
at.write("t2", "B\n");
p.delay(500);
p.kill()
.make_assertion()
.with_all_output()
.stdout_contains("A\n")
.stdout_contains("B\n");
}