aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2025-08-03 08:19:53 -0700
committerPaul Eggert <eggert@cs.ucla.edu>2025-08-03 19:48:06 -0700
commite0100ac746e6ddefeef09e17716c148edc1f995b (patch)
tree6017ca72fd3ec4f48bb5cf8bd58874c51e930556
parenttail: track errno more accurately (diff)
downloadcoreutils-e0100ac746e6ddefeef09e17716c148edc1f995b.tar.gz
coreutils-e0100ac746e6ddefeef09e17716c148edc1f995b.zip
tail: fix race between read and fstat
* src/tail.c (get_file_status): Remove, since after the changes described below it would be called in just one place and it’s a bit clearer to inline by hand. (tail_file): Don’t call fstat after reading the file, as that misses changes arriving between read and fstat. Instead, reuse the fstat done before reading the file.
-rw-r--r--src/tail.c34
1 files changed, 10 insertions, 24 deletions
diff --git a/src/tail.c b/src/tail.c
index f23e949b1..6a769181a 100644
--- a/src/tail.c
+++ b/src/tail.c
@@ -1824,22 +1824,6 @@ tail_forever_inotify (int wd, struct File_spec *f, int n_files,
}
#endif
-/* Get the status for file F, which has descriptor FD, into *ST.
- Return true on success, false (diagnosing the failure) otherwise. */
-
-static bool
-get_file_status (struct File_spec *f, int fd, struct stat *st)
-{
- if (fstat (fd, st) < 0)
- {
- f->errnum = errno;
- error (0, f->errnum, _("cannot fstat %s"), quoteaf (f->prettyname));
- return false;
- }
- f->errnum = 0;
- return true;
-}
-
/* Output the last bytes of the file PRETTYNAME open for reading
in FD and with status ST. Output the last N_BYTES bytes.
Return (-1 - errno) on failure, otherwise the resulting file offset
@@ -2023,16 +2007,21 @@ tail_file (struct File_spec *f, count_t n_files, count_t n_units)
if (print_headers)
write_header (f->prettyname);
+ off_t read_pos;
struct stat stats;
- bool stat_ok = get_file_status (f, fd, &stats);
- off_t read_pos = stat_ok ? tail (f->prettyname, fd, &stats, n_units) : -1;
- if (read_pos < -1)
+ bool stat_ok = 0 <= fstat (fd, &stats);
+ if (!stat_ok)
{
- f->errnum = -1 - read_pos;
+ f->errnum = errno;
+ error (0, f->errnum, _("cannot fstat %s"), quoteaf (f->prettyname));
ok = false;
}
else
- ok = stat_ok;
+ {
+ read_pos = tail (f->prettyname, fd, &stats, n_units);
+ ok = -1 <= read_pos;
+ f->errnum = ok ? 0 : -1 - read_pos;
+ }
if (forever)
{
@@ -2047,9 +2036,6 @@ tail_file (struct File_spec *f, count_t n_files, count_t n_units)
f->ignore ? _("; giving up on this name") : "");
}
- if (ok && !get_file_status (f, fd, &stats))
- ok = false;
-
if (!ok)
{
f->ignore = ! reopen_inaccessible_files;