Subject: Re: bug#51857: cross-filesystem copying broken on macOS with coreutils >= 9.0

From: Paul Eggert <[email-masked]>
Date: Mon, 15 Nov 2021 15:41:00 -0800

On 11/15/21 10:37, Sudhip Nashi wrote:
> Turns out lseek is broken (or at least works differently) on macOS as well (https://lists.gnu.org/archive/html/bug-gnulib/2018-09/msg00054.html). Funny coincidence! I’ll take a better look later this week if I can and try to see what the exact problem is.

Thanks, I think I see the problem now.

Eventually macOS will likely get fixed to work around this 
lseek+SEEK_DATA incompatibility (as FreeBSD, Solaris, etc. all do things 
the Linux way and that's what I think will appear in the next POSIX), 
but in the meantime I attempted to work around the portability issue by 
installing the attached patch into Gnulib, and by syncing coreutils to 
the latest Gnulib.

I don't use macOS so have not tested this. Please give it a try, either 
by building from bleeding-edge coreutils on Savannah, or by building 
from the tarball temporarily here:

https://www.cs.ucla.edu/~eggert/coreutils-9.0.26-0f4d9.tar.gz

Thanks.

From 4db8db34112b86ddf8bac48f16b5acff732b5fa9 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Mon, 15 Nov 2021 15:08:25 -0800
Subject: [PATCH] lseek: port around macOS SEEK_DATA glitch

Problem reported by Sudhip Nashi (Bug#51857).
* doc/posix-functions/lseek.texi (lseek): Mention macOS SEEK_DATA
issue.
* lib/lseek.c (rpl_lseek): Work around macOS portability glitch.
* m4/lseek.m4 (gl_FUNC_LSEEK): Replace lseek on Darwin.
* modules/lseek (Depends-on): Depend on msvc-nothrow
and fstat only if needed.
---
 ChangeLog                      | 11 +++++++++++
 doc/posix-functions/lseek.texi |  4 ++++
 lib/lseek.c                    | 16 ++++++++++++++++
 m4/lseek.m4                    | 10 ++++++++--
 modules/lseek                  |  4 ++--
 5 files changed, 41 insertions(+), 4 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index f47071a72..71a226570 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2021-11-15  Paul Eggert  <[email-masked]>
+
+	lseek: port around macOS SEEK_DATA glitch
+	Problem reported by Sudhip Nashi (Bug#51857).
+	* doc/posix-functions/lseek.texi (lseek): Mention macOS SEEK_DATA
+	issue.
+	* lib/lseek.c (rpl_lseek): Work around macOS portability glitch.
+	* m4/lseek.m4 (gl_FUNC_LSEEK): Replace lseek on Darwin.
+	* modules/lseek (Depends-on): Depend on msvc-nothrow
+	and fstat only if needed.
+
 2021-11-11  Fabrice Fontaine  <[email-masked]>  (tiny change)
 
 	sigsegv: fix builds on microblazeel, or1k
diff --git a/doc/posix-functions/lseek.texi b/doc/posix-functions/lseek.texi
index 4a9d55dcf..2f8e2b587 100644
--- a/doc/posix-functions/lseek.texi
+++ b/doc/posix-functions/lseek.texi
@@ -9,6 +9,10 @@ Gnulib module: lseek
 Portability problems fixed by Gnulib:
 @itemize
 @item
+On some platforms, @code{lseek (fd, offset, SEEK_DATA)} returns a value
+greater than @code{offset} even when @code{offset} addresses data:
+macOS 12
+@item
 This function is declared in a different header file (namely, @code{<io.h>})
 on some platforms:
 MSVC 14.
diff --git a/lib/lseek.c b/lib/lseek.c
index 0042546a8..7dcd6c9da 100644
--- a/lib/lseek.c
+++ b/lib/lseek.c
@@ -52,6 +52,22 @@ rpl_lseek (int fd, off_t offset, int whence)
       errno = ESPIPE;
       return -1;
     }
+#elif defined __APPLE__ && defined __MACH__ && defined SEEK_DATA
+  if (whence == SEEK_DATA)
+    {
+      /* If OFFSET points to data, macOS lseek+SEEK_DATA returns the
+         start S of the first data region that begins *after* OFFSET,
+         where the region from OFFSET to S consists of possibly-empty
+         data followed by a possibly-empty hole.  To work around this
+         portability glitch, check whether OFFSET is within data by
+         using lseek+SEEK_HOLE, and if so return to OFFSET by using
+         lseek+SEEK_SET.  */
+      off_t next_hole = lseek (fd, offset, SEEK_HOLE);
+      if (next_hole < 0)
+        return next_hole;
+      if (next_hole != offset)
+        whence = SEEK_SET;
+    }
 #else
   /* BeOS lseek mistakenly succeeds on pipes...  */
   struct stat statbuf;
diff --git a/m4/lseek.m4 b/m4/lseek.m4
index 0af63780a..faab09b73 100644
--- a/m4/lseek.m4
+++ b/m4/lseek.m4
@@ -1,4 +1,4 @@
-# lseek.m4 serial 11
+# lseek.m4 serial 12
 dnl Copyright (C) 2007, 2009-2021 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -59,7 +59,7 @@ AC_DEFUN([gl_FUNC_LSEEK],
          ;;
      esac
     ])
-  if test $gl_cv_func_lseek_pipe = no; then
+  if test "$gl_cv_func_lseek_pipe" = no; then
     REPLACE_LSEEK=1
     AC_DEFINE([LSEEK_PIPE_BROKEN], [1],
       [Define to 1 if lseek does not detect pipes.])
@@ -69,4 +69,10 @@ AC_DEFUN([gl_FUNC_LSEEK],
   if test $WINDOWS_64_BIT_OFF_T = 1; then
     REPLACE_LSEEK=1
   fi
+
+  dnl macOS SEEK_DATA is incompatible with other platforms.
+  case $host_os in
+    darwin*)
+      REPLACE_LSEEK=1;;
+  esac
 ])
diff --git a/modules/lseek b/modules/lseek
index ced443123..f60809319 100644
--- a/modules/lseek
+++ b/modules/lseek
@@ -9,8 +9,8 @@ Depends-on:
 unistd
 sys_types
 largefile
-msvc-nothrow    [test $REPLACE_LSEEK = 1]
-fstat           [test $REPLACE_LSEEK = 1]
+msvc-nothrow    [test $WINDOWS_64_BIT_OFF_T = 1]
+fstat           [test "$gl_cv_func_lseek_pipe" = no]
 
 configure.ac:
 gl_FUNC_LSEEK
-- 
2.33.1