summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPádraig Brady <P@draigBrady.com>2013-09-13 17:31:24 +0200
committerPádraig Brady <P@draigBrady.com>2013-11-27 01:40:08 +0000
commitba6582e95ce2a041423e1ff34c93abe7b4702332 (patch)
treeb9d30c8ff7fc2a00e80b12c5b7f15f318ec02b98 /src
parent799e10f3619ea7949a4f606b2f29b662daf31e3c (diff)
downloadcoreutils-ba6582e95ce2a041423e1ff34c93abe7b4702332.tar.xz
tail: improve inotify handling of symlinks
Previous behavior failed to read contents of a (re)appearing file, when symlinked by tail's watched file. Also we now diagnose other edge cases when running in inotify mode, where an initially missing or regular file changes to a symlink. * src/tail.c (main): If any arg is a symlink, use polling mode. (recheck): Diagnose the edge case where a symlink appears during inotify processing. * tests/tail-2/symlink.sh: Test the fix. Mention the edge cases. * tests/local.mk: Reference the new test. * NEWS: Mention the fix. Reported by: Ondrej Oprala
Diffstat (limited to 'src')
-rw-r--r--src/tail.c39
1 files changed, 38 insertions, 1 deletions
diff --git a/src/tail.c b/src/tail.c
index e7fefda71..c781dc2fa 100644
--- a/src/tail.c
+++ b/src/tail.c
@@ -945,7 +945,20 @@ recheck (struct File_spec *f, bool blocking)
then mark the file as not tailable. */
f->tailable = !(reopen_inaccessible_files && fd == -1);
- if (fd == -1 || fstat (fd, &new_stats) < 0)
+ if (! disable_inotify && ! lstat (f->name, &new_stats)
+ && S_ISLNK (new_stats.st_mode))
+ {
+ /* Diagnose the edge case where a regular file is changed
+ to a symlink. We avoid inotify with symlinks since
+ it's awkward to match between symlink name and target. */
+ ok = false;
+ f->errnum = -1;
+ f->ignore = true;
+
+ error (0, 0, _("%s has been replaced with a symbolic link. "
+ "giving up on this name"), quote (pretty_name (f)));
+ }
+ else if (fd == -1 || fstat (fd, &new_stats) < 0)
{
ok = false;
f->errnum = errno;
@@ -1251,6 +1264,23 @@ any_remote_file (const struct File_spec *f, size_t n_files)
return false;
}
+/* Return true if any of the N_FILES files in F is a symlink.
+ Note we don't worry about the edge case where "-" exists,
+ since that will have the same consequences for inotify,
+ which is the only context this function is currently used. */
+
+static bool
+any_symlinks (const struct File_spec *f, size_t n_files)
+{
+ size_t i;
+
+ struct stat st;
+ for (i = 0; i < n_files; i++)
+ if (lstat (f[i].name, &st) == 0 && S_ISLNK (st.st_mode))
+ return true;
+ return false;
+}
+
/* Return true if any of the N_FILES files in F represents
stdin and is tailable. */
@@ -1531,6 +1561,7 @@ tail_forever_inotify (int wd, struct File_spec *f, size_t n_files,
int new_wd = inotify_add_watch (wd, f[j].name, inotify_wd_mask);
if (new_wd < 0)
{
+ /* Can get ENOENT for a dangling symlink for example. */
error (0, errno, _("cannot watch %s"), quote (f[j].name));
continue;
}
@@ -2206,6 +2237,11 @@ main (int argc, char **argv)
in this case because it would miss any updates to the file
that were not initiated from the local system.
+ any_symlinks() checks if the user has specified any symbolic links.
+ inotify is not used in this case because it returns updated _targets_
+ which would not match the specified names. If we tried to always
+ use the target names, then we would miss changes to the symlink itself.
+
ok is false when one of the files specified could not be opened for
reading. In this case and when following by descriptor,
tail_forever_inotify() cannot be used (in its current implementation).
@@ -2222,6 +2258,7 @@ main (int argc, char **argv)
follow_mode == Follow_name */
if (!disable_inotify && (tailable_stdin (F, n_files)
|| any_remote_file (F, n_files)
+ || any_symlinks (F, n_files)
|| (!ok && follow_mode == Follow_descriptor)))
disable_inotify = true;