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
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);
}
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;
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
--- /dev/null
+#!/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 <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ tail
+
+# need write access to local device
+# (even though we don't actually write anything)
+require_root_
+require_local_dir_
+
+get_device_size() {
+ BLOCKDEV=blockdev
+ $BLOCKDEV -V >/dev/null 2>&1 || BLOCKDEV=/sbin/blockdev
+ $BLOCKDEV --getsize64 "$1"
+}
+
+# Get path to device the current dir is on.
+# Note df can only get fs size, not device size.
+device=$(df --output=source . | tail -n1) || framework_failure_
+
+dev_size=$(get_device_size "$device") ||
+ skip_ "failed to determine size of $device"
+
+tail_offset=$(expr $dev_size - 1023) ||
+ skip_ "failed to determine tail offset"
+
+timeout 10 tail -c 1024 "$device" > end1 || fail=1
+timeout 10 tail -c +"$tail_offset" "$device" > end2 || fail=1
+test $(wc -c < end1) = 1024 || fail=1
+cmp end1 end2 || fail=1
+
+Exit $fail