Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions src/uu/tail/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ tail-status-has-been-replaced-following-new-file = { $file } has been replaced;
tail-status-file-truncated = { $file }: file truncated
tail-status-replaced-with-untailable-file = { $file } has been replaced with an untailable file
tail-status-replaced-with-untailable-file-giving-up = { $file } has been replaced with an untailable file; giving up on this name
tail-status-replaced-with-untailable-symlink = { $file } has been replaced with an untailable symbolic link
tail-status-file-became-inaccessible = { $file } { $become_inaccessible }: { $no_such_file }
tail-status-directory-containing-watched-file-removed = directory containing watched file was removed
tail-status-backend-cannot-be-used-reverting-to-polling = { $backend } cannot be used, reverting to polling
Expand Down
1 change: 1 addition & 0 deletions src/uu/tail/locales/fr-FR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ tail-status-has-been-replaced-following-new-file = { $file } a été remplacé ;
tail-status-file-truncated = { $file } : fichier tronqué
tail-status-replaced-with-untailable-file = { $file } a été remplacé par un fichier non suivable
tail-status-replaced-with-untailable-file-giving-up = { $file } a été remplacé par un fichier non suivable ; abandon de ce nom
tail-status-replaced-with-untailable-symlink = { $file } a été remplacé par un lien symbolique non suivable
tail-status-file-became-inaccessible = { $file } { $become_inaccessible } : { $no_such_file }
tail-status-directory-containing-watched-file-removed = le répertoire contenant le fichier surveillé a été supprimé
tail-status-backend-cannot-be-used-reverting-to-polling = { $backend } ne peut pas être utilisé, retour au sondage
Expand Down
17 changes: 16 additions & 1 deletion src/uu/tail/src/follow/files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ impl FileHandling {
};
}

pub fn update_symlink(&mut self, path: &Path, is_symlink: bool) {
self.get_mut(path).is_symlink = is_symlink;
}

/// Read new data from `path` and print it to stdout
pub fn tail_file(&mut self, path: &Path, verbose: bool) -> UResult<bool> {
let mut chunks = BytesChunkBuffer::new(u64::MAX);
Expand Down Expand Up @@ -178,18 +182,21 @@ pub struct PathData {
pub reader: Option<Box<dyn BufRead>>,
pub metadata: Option<Metadata>,
pub display_name: String,
pub is_symlink: bool,
}

impl PathData {
pub fn new(
reader: Option<Box<dyn BufRead>>,
metadata: Option<Metadata>,
display_name: &str,
is_symlink: bool,
) -> Self {
Self {
reader,
metadata,
display_name: display_name.to_owned(),
is_symlink,
}
}
pub fn from_other_with_path(data: Self, path: &Path) -> Self {
Expand All @@ -205,7 +212,15 @@ impl PathData {
// Probably file was renamed/moved or removed again
None
};
let is_symlink = path
.symlink_metadata()
.is_ok_and(|meta| meta.file_type().is_symlink());

Self::new(reader, path.metadata().ok(), data.display_name.as_str())
Self::new(
reader,
path.metadata().ok(),
data.display_name.as_str(),
is_symlink,
)
}
}
45 changes: 44 additions & 1 deletion src/uu/tail/src/follow/watch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,12 @@ impl Observer {
path.to_owned()
};
let metadata = path.metadata().ok();
let is_symlink = path
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please dedup the declaration (see files:.rs:215)

.symlink_metadata()
.is_ok_and(|meta| meta.file_type().is_symlink());
self.files.insert(
&path,
PathData::new(reader, metadata, display_name),
PathData::new(reader, metadata, display_name, is_symlink),
update_last,
);
}
Expand Down Expand Up @@ -304,8 +307,29 @@ impl Observer {
EventKind::Modify(ModifyKind::Metadata(MetadataKind::Any | MetadataKind::WriteTime) | ModifyKind::Data(DataChange::Any) | ModifyKind::Name(RenameMode::To)) |
EventKind::Create(CreateKind::File | CreateKind::Folder | CreateKind::Any) => {
if let Ok(new_md) = event_path.metadata() {
let new_is_symlink = event_path
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

.symlink_metadata()
.is_ok_and(|meta| meta.file_type().is_symlink());
let is_tailable = new_md.is_tailable();
let pd = self.files.get(event_path);

if self.follow_name() && !pd.is_symlink && new_is_symlink {
if pd.reader.is_some() {
Comment on lines +312 to +315
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add comments to describe why we could be here

self.files.reset_reader(event_path);
}
show_error!(
"{}",
translate!(
"tail-status-replaced-with-untailable-symlink",
"file" => display_name.quote()
)
);
self.files
.update_metadata(event_path, event_path.symlink_metadata().ok());
self.files.update_symlink(event_path, true);
return Ok(paths);
}

if let Some(old_md) = &pd.metadata {
if is_tailable {
// We resume tracking from the start of the file,
Expand Down Expand Up @@ -374,6 +398,7 @@ impl Observer {
}
}
self.files.update_metadata(event_path, Some(new_md));
self.files.update_symlink(event_path, new_is_symlink);
}
}
EventKind::Remove(RemoveKind::File | RemoveKind::Any)
Expand Down Expand Up @@ -497,12 +522,30 @@ pub fn follow(mut observer: Observer, settings: &Settings) -> UResult<()> {
if new_path.exists() {
let pd = observer.files.get(new_path);
let md = new_path.metadata().unwrap();
let new_is_symlink = new_path
.symlink_metadata()
.is_ok_and(|meta| meta.file_type().is_symlink());
if !pd.is_symlink && new_is_symlink {
show_error!(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see that it is duplicated from above

"{}",
translate!(
"tail-status-replaced-with-untailable-symlink",
"file" => pd.display_name.quote()
)
);
observer
.files
.update_metadata(new_path, new_path.symlink_metadata().ok());
observer.files.update_symlink(new_path, true);
continue;
}
if md.is_tailable() && pd.reader.is_none() {
show_error!(
"{}",
translate!("tail-status-has-appeared-following-new-file", "file" => pd.display_name.quote())
);
observer.files.update_metadata(new_path, Some(md));
observer.files.update_symlink(new_path, new_is_symlink);
observer.files.update_reader(new_path)?;
_read_some = observer.files.tail_file(new_path, settings.verbose)?;
observer
Expand Down
39 changes: 37 additions & 2 deletions tests/by-util/test_tail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3684,17 +3684,52 @@ fn test_when_argument_file_is_a_symlink() {
fn test_when_argument_file_is_a_symlink_to_directory_then_error() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;

at.mkdir("dir");
at.symlink_file("dir", "dir_link");

let expected = "tail: error reading 'dir_link': Is a directory\n";
ts.ucmd()
.arg("dir_link")
.fails_with_code(1)
.stderr_only(expected);
}

// TODO: make this work on windows
#[test]
#[cfg(unix)]
fn test_follow_name_replaced_with_symlink() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
let file = "testfile";
let target = "target";

at.write(file, "original\n");
at.write(target, "target\n");

let mut child = ts
.ucmd()
.args(&[
"--follow=name",
"-n",
"0",
"--sleep-interval=0.1",
"--use-polling",
file,
])
.run_no_wait();

child.delay(500);
at.remove(file);
at.symlink_file(target, file);
child.delay(500);

child
.kill()
.make_assertion()
.with_all_output()
.stderr_contains("tail: 'testfile' has been replaced with an untailable symbolic link")
.stdout_is("");
}

// TODO: make this work on windows
#[test]
#[cfg(unix)]
Expand Down
Loading