Skip to content

Commit 8bb31ee

Browse files
tail: fix -F to properly track symlinks with changing targets (#10158)
Co-authored-by: Sylvestre Ledru <sylvestre@debian.org>
1 parent 507a524 commit 8bb31ee

2 files changed

Lines changed: 43 additions & 0 deletions

File tree

src/uu/tail/src/follow/watch.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,10 @@ impl Observer {
277277
// If `path` is not a tailable file, add its parent to `Watcher`.
278278
watcher_rx
279279
.watch(path.parent().unwrap(), RecursiveMode::NonRecursive)?;
280+
// Add symlinks to orphans for retry polling (target may not exist)
281+
if path.is_symlink() {
282+
self.orphans.push(path);
283+
}
280284
} else {
281285
// If there is no parent, add `path` to `orphans`.
282286
self.orphans.push(path);
@@ -374,6 +378,9 @@ impl Observer {
374378
}
375379
}
376380
self.files.update_metadata(event_path, Some(new_md));
381+
} else if event_path.is_symlink() && settings.retry {
382+
self.files.reset_reader(event_path);
383+
self.orphans.push(event_path.clone());
377384
}
378385
}
379386
EventKind::Remove(RemoveKind::File | RemoveKind::Any)

tests/by-util/test_tail.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5149,3 +5149,39 @@ fn test_debug_flag_with_inotify() {
51495149
.with_all_output()
51505150
.stderr_contains("tail: using notification mode");
51515151
}
5152+
5153+
#[test]
5154+
#[cfg(target_os = "linux")]
5155+
fn test_follow_dangling_symlink() {
5156+
let (at, mut ucmd) = at_and_ucmd!();
5157+
at.symlink_file("target", "link");
5158+
let mut p = ucmd
5159+
.args(&["-s.1", "--max-unchanged-stats=1", "-F", "link"])
5160+
.run_no_wait();
5161+
p.delay(500);
5162+
at.write("target", "X\n");
5163+
p.delay(500);
5164+
p.kill().make_assertion().with_all_output().stdout_is("X\n");
5165+
}
5166+
5167+
#[test]
5168+
#[cfg(target_os = "linux")]
5169+
fn test_follow_symlink_target_change() {
5170+
let (at, mut ucmd) = at_and_ucmd!();
5171+
at.write("t1", "A\n");
5172+
at.symlink_file("t1", "link");
5173+
let mut p = ucmd
5174+
.args(&["-s.1", "--max-unchanged-stats=1", "-F", "link"])
5175+
.run_no_wait();
5176+
p.delay(500);
5177+
at.remove("link");
5178+
at.symlink_file("t2", "link");
5179+
p.delay(500);
5180+
at.write("t2", "B\n");
5181+
p.delay(500);
5182+
p.kill()
5183+
.make_assertion()
5184+
.with_all_output()
5185+
.stdout_contains("A\n")
5186+
.stdout_contains("B\n");
5187+
}

0 commit comments

Comments
 (0)