Subject: Re: bug#36291: od --skip-bytes reads everything from the very beginning

From: Paul Eggert <[email-masked]>
Date: Wed, 19 Jun 2019 18:52:01 -0700

On 6/19/19 12:42 PM, Pádraig Brady wrote:
> Maybe we should relax the cases we do read() for,
> and try to seek in block/character special files,
> falling back to read() where that fails?

Sure, that's easy enough. I installed the attached patch and am marking 
this bug report as done.

From ea353844d8642b5533cbc713e0cec46addbf3907 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Wed, 19 Jun 2019 18:46:57 -0700
Subject: [PATCH] od: use fseek on non-regular files
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Problem reported by Szőts Ákos (Bug#36291).
* NEWS: Mention this.
* src/od.c (skip): Try fseek even on files that do not have usable
sizes, falling back on fread if fseek fails.
---
 NEWS     | 3 +++
 src/od.c | 8 ++++++--
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/NEWS b/NEWS
index d30711bc6..fd0543351 100644
--- a/NEWS
+++ b/NEWS
@@ -36,6 +36,9 @@ GNU coreutils NEWS                                    -*- outline -*-
 
 ** New Features
 
+  od --skip-bytes now can use lseek even if the input is not a regular
+  file, greatly improving performance in some cases.
+
   stat(1) now uses the statx() system call where available, which can
   operate more efficiently by only retrieving requested attributes.
   stat(1) also supports a new --cached= option to control cache
diff --git a/src/od.c b/src/od.c
index 1a89542ee..75a402004 100644
--- a/src/od.c
+++ b/src/od.c
@@ -1033,6 +1033,8 @@ skip (uintmax_t n_skip)
 
       if (fstat (fileno (in_stream), &file_stats) == 0)
         {
+          bool usable_size = usable_st_size (&file_stats);
+
           /* The st_size field is valid for regular files.
              If the number of bytes left to skip is larger than
              the size of the current file, we can decrement n_skip
@@ -1040,8 +1042,7 @@ skip (uintmax_t n_skip)
              when st_size is no greater than the block size, because
              some kernels report nonsense small file sizes for
              proc-like file systems.  */
-          if (usable_st_size (&file_stats)
-              && ST_BLKSIZE (file_stats) < file_stats.st_size)
+          if (usable_size && ST_BLKSIZE (file_stats) < file_stats.st_size)
             {
               if ((uintmax_t) file_stats.st_size < n_skip)
                 n_skip -= file_stats.st_size;
@@ -1056,6 +1057,9 @@ skip (uintmax_t n_skip)
                 }
             }
 
+          else if (!usable_size && fseeko (in_stream, n_skip, SEEK_CUR) == 0)
+            n_skip = 0;
+
           /* If it's not a regular file with nonnegative size,
              or if it's so small that it might be in a proc-like file system,
              position the file pointer by reading.  */
-- 
2.21.0