From 2662702b9e8643f62c670bbf2fa94b1be1ccf9af Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Tue, 7 Oct 2014 16:46:08 -0700 Subject: wc: don't miscount /sys and similar file systems Fix similar problems in head, od, split, tac, and tail. Reported by George Shuklin in: http://bugs.gnu.org/18621 * NEWS: Document this. * src/head.c (elseek): Move up. (elide_tail_bytes_pipe, elide_tail_lines_pipe): New arg CURRENT_POS. All uses changed. (elide_tail_bytes_file, elide_tail_lines_file): New arg ST and remove arg SIZE. All uses changed. * src/head.c (elide_tail_bytes_file): * src/od.c (skip): Avoid optimization for /sys files, where st_size is bogus and st_size == st_blksize. Don't report error at EOF when not optimizing. * src/head.c, src/od.c, src/tail.c: Include "stat-size.h". * src/split.c (input_file_size): New function. (bytes_split, lines_chunk_split, bytes_chunk_extract): New arg INITIAL_READ. All uses changed. Use it to double-check st_size. * src/tac.c (tac_seekable): New arg FILE_POS. All uses changed. (copy_to_temp): Return size of temp file. All uses changed. * src/tac.c (tac_seekable): * src/tail.c (tail_bytes): * src/wc.c (wc): Don't trust st_size; double-check by reading. * src/wc.c (wc): New arg CURRENT_POS. All uses changed. * tests/local.mk (all_tests): Add tests/misc/wc-proc.sh, tests/misc/od-j.sh, tests/tail-2/tail-c.sh. * tests/misc/head-c.sh: * tests/misc/tac-2-nonseekable.sh: * tests/split/b-chunk.sh: Add tests for problems with /proc and /sys files. * tests/misc/od-j.sh, tests/misc/wc-proc.sh, tests/tail-2/tail-c.sh: New files. --- src/tail.c | 43 +++++++++++++++++-------------------------- 1 file changed, 17 insertions(+), 26 deletions(-) (limited to 'src/tail.c') diff --git a/src/tail.c b/src/tail.c index f5d258517..4c5f943c7 100644 --- a/src/tail.c +++ b/src/tail.c @@ -40,6 +40,7 @@ #include "posixver.h" #include "quote.h" #include "safe-read.h" +#include "stat-size.h" #include "stat-time.h" #include "xfreopen.h" #include "xnanosleep.h" @@ -1665,40 +1666,30 @@ tail_bytes (const char *pretty_filename, int fd, uintmax_t n_bytes, if (t) return t < 0; } - *read_pos += dump_remainder (pretty_filename, fd, COPY_TO_EOF); + n_bytes = COPY_TO_EOF; } else { - if ( ! presume_input_pipe - && S_ISREG (stats.st_mode) && n_bytes <= OFF_T_MAX) + off_t end_pos = ((! presume_input_pipe && usable_st_size (&stats) + && n_bytes <= OFF_T_MAX) + ? stats.st_size : -1); + if (end_pos <= ST_BLKSIZE (stats)) + return pipe_bytes (pretty_filename, fd, n_bytes, read_pos); + off_t current_pos = xlseek (fd, 0, SEEK_CUR, pretty_filename); + if (current_pos < end_pos) { - off_t current_pos = xlseek (fd, 0, SEEK_CUR, pretty_filename); - off_t end_pos = xlseek (fd, 0, SEEK_END, pretty_filename); - off_t diff = end_pos - current_pos; - /* Be careful here. The current position may actually be - beyond the end of the file. */ - off_t bytes_remaining = diff < 0 ? 0 : diff; - off_t nb = n_bytes; - - if (bytes_remaining <= nb) - { - /* From the current position to end of file, there are no - more bytes than have been requested. So reposition the - file pointer to the incoming current position and print - everything after that. */ - *read_pos = xlseek (fd, current_pos, SEEK_SET, pretty_filename); - } - else + off_t bytes_remaining = end_pos - current_pos; + + if (n_bytes < bytes_remaining) { - /* There are more bytes remaining than were requested. - Back up. */ - *read_pos = xlseek (fd, -nb, SEEK_END, pretty_filename); + current_pos = end_pos - n_bytes; + xlseek (fd, current_pos, SEEK_SET, pretty_filename); } - *read_pos += dump_remainder (pretty_filename, fd, n_bytes); } - else - return pipe_bytes (pretty_filename, fd, n_bytes, read_pos); + *read_pos = current_pos; } + + *read_pos += dump_remainder (pretty_filename, fd, n_bytes); return true; } -- cgit v1.2.3-54-g00ecf