Subject: bug#51433: cp 9.0 sometimes fails with SEEK_DATA/SEEK_HOLE

From: Paul Eggert <[email-masked]>
Date: Fri, 29 Oct 2021 18:24:38 -0700

On 10/28/21 12:11, Paul Eggert wrote:
> Wait - lseek returns a number less than -1?! We could easily check for 
> that FreeBSD bug, perhaps as an independent patch; this shouldn't 
> require any extra syscalls.

I installed the attached patch to do this. This doesn't fix coreutils 
bug#51433; it merely makes 'cp' and similar programs more likely to 
detect and report the FreeBSD 9.1 bug you mentioned.

From 674912c6f7c45b5f0b1f77da89663cac02c118f7 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Fri, 29 Oct 2021 18:01:34 -0700
Subject: [PATCH] cp: defend better against FreeBSD 9.1 zfs bug
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Problem reported by Pádraig Brady (Bug#51433#14).
* src/copy.c (lseek_copy, infer_scantype): Report an error if
lseek with SEEK_DATA or SEEK_HOLE returns less than -1,
as this is an lseek bug.
---
 src/copy.c | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/src/copy.c b/src/copy.c
index cb9018f93..1cbc9480c 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -530,7 +530,7 @@ lseek_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
       off_t ext_end = lseek (src_fd, ext_start, SEEK_HOLE);
       if (ext_end < 0)
         {
-          if (errno != ENXIO)
+          if (! (ext_end == -1 && errno == ENXIO))
             goto cannot_lseek;
           ext_end = src_total_size;
           if (ext_end <= ext_start)
@@ -607,12 +607,8 @@ lseek_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
         }
 
       ext_start = lseek (src_fd, dest_pos, SEEK_DATA);
-      if (ext_start < 0)
-        {
-          if (errno != ENXIO)
-            goto cannot_lseek;
-          break;
-        }
+      if (ext_start < 0 && ! (ext_start == -1 && errno == ENXIO))
+        goto cannot_lseek;
     }
 
   /* When the source file ends with a hole, we have to do a little more work,
@@ -1097,10 +1093,11 @@ infer_scantype (int fd, struct stat const *sb,
 
 #ifdef SEEK_HOLE
   scan_inference->ext_start = lseek (fd, 0, SEEK_DATA);
-  if (0 <= scan_inference->ext_start)
+  if (0 <= scan_inference->ext_start
+      || (scan_inference->ext_start == -1 && errno == ENXIO))
     return LSEEK_SCANTYPE;
   else if (errno != EINVAL && !is_ENOTSUP (errno))
-    return errno == ENXIO ? LSEEK_SCANTYPE : ERROR_SCANTYPE;
+    return ERROR_SCANTYPE;
 #endif
 
   return ZERO_SCANTYPE;
-- 
2.31.1