diff options
author | Pádraig Brady <P@draigBrady.com> | 2013-09-13 17:31:24 +0200 |
---|---|---|
committer | Pádraig Brady <P@draigBrady.com> | 2013-11-27 01:40:08 +0000 |
commit | ba6582e95ce2a041423e1ff34c93abe7b4702332 (patch) | |
tree | b9d30c8ff7fc2a00e80b12c5b7f15f318ec02b98 /src | |
parent | 799e10f3619ea7949a4f606b2f29b662daf31e3c (diff) | |
download | coreutils-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.c | 39 |
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; |