From 31dd7a0de272affa1120ba1fbc7db3445c548aa5 Mon Sep 17 00:00:00 2001 From: =?utf8?q?P=C3=A1draig=20Brady?=
Date: Sun, 12 Nov 2017 23:06:08 -0800
Subject: [PATCH] tail: seek to the end of block devices
* src/tail.c (tail_bytes): Try lseek(..., SEEK_END) when
we can't determine the file size.
* tests/tail-2/end-of-device.sh: Add a new root only test.
* tests/local.mk: Reference the new test.
* NEWS: Mention the improvement.
Paul Eggert suggested using lseek() (rather than ioctl(BLKGETSIZE64)).
Fixes https://bugs.gnu.org/29259
---
NEWS | 5 ++++
src/tail.c | 27 ++++++++++++--------
tests/local.mk | 1 +
tests/tail-2/end-of-device.sh | 48 +++++++++++++++++++++++++++++++++++
4 files changed, 71 insertions(+), 10 deletions(-)
create mode 100755 tests/tail-2/end-of-device.sh
diff --git a/NEWS b/NEWS
index 3e6704d5c7..bc5d5beeb7 100644
--- a/NEWS
+++ b/NEWS
@@ -31,6 +31,11 @@ GNU coreutils NEWS -*- outline -*-
timeout would have then waited for the time limit to expire.
[bug introduced in coreutils-8.27]
+** Improvements
+
+ tail --bytes=NUM will efficiently seek to the end of block devices,
+ rather than reading from the start.
+
** Build-related
Default man pages are now distributed which are used if perl is
diff --git a/src/tail.c b/src/tail.c
index a195443d86..536d0346d1 100644
--- a/src/tail.c
+++ b/src/tail.c
@@ -1830,12 +1830,11 @@ tail_bytes (const char *pretty_filename, int fd, uintmax_t n_bytes,
if (from_start)
{
- if ( ! presume_input_pipe
- && S_ISREG (stats.st_mode) && n_bytes <= OFF_T_MAX)
- {
- xlseek (fd, n_bytes, SEEK_CUR, pretty_filename);
- *read_pos += n_bytes;
- }
+ if (! presume_input_pipe && n_bytes <= OFF_T_MAX
+ && ((S_ISREG (stats.st_mode)
+ && xlseek (fd, n_bytes, SEEK_CUR, pretty_filename) >= 0)
+ || lseek (fd, n_bytes, SEEK_CUR) != -1))
+ *read_pos += n_bytes;
else
{
int t = start_bytes (pretty_filename, fd, n_bytes, read_pos);
@@ -1846,12 +1845,20 @@ tail_bytes (const char *pretty_filename, int fd, uintmax_t n_bytes,
}
else
{
- off_t end_pos = ((! presume_input_pipe && usable_st_size (&stats)
- && n_bytes <= OFF_T_MAX)
- ? stats.st_size : -1);
+ off_t end_pos = -1;
+ off_t current_pos = -1;
+
+ if (! presume_input_pipe && n_bytes <= OFF_T_MAX)
+ {
+ if (usable_st_size (&stats))
+ end_pos = stats.st_size;
+ else if ((current_pos = lseek (fd, -n_bytes, SEEK_END)) != -1)
+ end_pos = current_pos + n_bytes;
+ }
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 == -1)
+ current_pos = xlseek (fd, 0, SEEK_CUR, pretty_filename);
if (current_pos < end_pos)
{
off_t bytes_remaining = end_pos - current_pos;
diff --git a/tests/local.mk b/tests/local.mk
index 74426f6085..8ee7c50399 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -135,6 +135,7 @@ all_root_tests = \
tests/rm/one-file-system.sh \
tests/rm/read-only.sh \
tests/tail-2/append-only.sh \
+ tests/tail-2/end-of-device.sh \
tests/touch/now-owned-by-other.sh
ALL_RECURSIVE_TARGETS += check-root
diff --git a/tests/tail-2/end-of-device.sh b/tests/tail-2/end-of-device.sh
new file mode 100755
index 0000000000..9123c80ccf
--- /dev/null
+++ b/tests/tail-2/end-of-device.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+# Ensure that tail seeks to the end of a device
+
+# Copyright (C) 2017 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see