Subject: bug#61386: [PATCH] cp,mv,install: Disable sparse copy on macOS

From: Paul Eggert <[email-masked]>
Date: Thu, 9 Feb 2023 19:57:44 -0800

On 2/9/23 01:20, George Valkov wrote:
> -#ifdef SEEK_HOLE
> +#if defined(SEEK_HOLE) && !defined(__APPLE__)

Instead of always disabling the SEEK_HOLE optimization, how about doing 
it only on APFS files? Something like the attached, perhaps (this is 
against Savannah master).

If APFS is pretty much universal now on Apple platforms, this patch is 
overkill and we should use your much-simpler patch.

From bd8c931fade7fcf96c02e4492912f27a2d1c795b Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Thu, 9 Feb 2023 19:53:59 -0800
Subject: [PATCH] cp: work around APFS bug

See <https://bugs.gnu.org/61386>.
* src/copy.c (SEEK_HOLE && __APPLE__):
Include <sys/mount.h>, for statfs.
(infer_scantype) [SEEK_HOLE && __APPLE__):
Don't use SEEK_HOLE on APFS, as APFS is buggy.
---
 src/copy.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/src/copy.c b/src/copy.c
index dfbb557de..95151fac0 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -65,6 +65,10 @@
 #include "yesno.h"
 #include "selinux.h"
 
+#if defined SEEK_HOLE && defined __APPLE__
+# include <sys/mount.h>
+#endif
+
 #ifndef USE_XATTR
 # define USE_XATTR false
 #endif
@@ -1063,6 +1067,26 @@ infer_scantype (int fd, struct stat const *sb,
     return PLAIN_SCANTYPE;
 
 #ifdef SEEK_HOLE
+# ifdef __APPLE__
+  /* Work around APFS bug <https://debbugs.gnu.org/61386> by not using
+     SEEK_DATA on APFS files.  To avoid fstatfs calls in common cases,
+     APFS_DEV[0] caches the most recent non-APFS st_dev, and APFS_DEV[1]
+     caches the most recent APFS st_dev.  Zero caches are invalid.  */
+  static dev_t apfs_dev[2];
+  if (! (sb->st_dev && sb->st_dev == apfs_dev[0]))
+    {
+      if (sb->st_dev && sb->st_dev == apfs_dev[1])
+        return ZERO_SCANTYPE;
+      struct statfs fs;
+      if (fstatfs (fd, &fs) != 0)
+        return ERROR_SCANTYPE;
+      bool apfs = strcmp (fs.f_fstypename, "apfs") == 0;
+      if (sb->st_dev)
+        apfs_dev[apfs] = sb->st_dev;
+      if (apfs)
+        return ZERO_SCANTYPE;
+    }
+# endif
   off_t ext_start = lseek (fd, 0, SEEK_DATA);
   if (0 <= ext_start || errno == ENXIO)
     {
-- 
2.39.1