From e0100ac746e6ddefeef09e17716c148edc1f995b Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sun, 3 Aug 2025 08:19:53 -0700 Subject: [PATCH] tail: fix race between read and fstat MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit * 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. --- src/tail.c | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/src/tail.c b/src/tail.c index f23e949b1d..6a769181a9 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; -- 2.47.2