]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
copy: fix possible over allocation for regular files
authorPaul Eggert <eggert@cs.ucla.edu>
Sun, 20 Nov 2022 03:04:36 +0000 (19:04 -0800)
committerPádraig Brady <P@draigBrady.com>
Mon, 2 Jan 2023 23:30:07 +0000 (23:30 +0000)
* bootstrap.conf (gnulib_modules): Add count-leading-zeros,
which was already an indirect dependency, since ioblksize.h
now uses it directly.
* src/ioblksize.h: Include count-leading-zeros.h.
(io_blksize): Treat impossible blocksizes as IO_BUFSIZE.
When growing a blocksize to IO_BUFSIZE, keep it a multiple of the
stated blocksize.  Work around the ZFS performance bug.
* NEWS: Mention the bug fix.
Problem reported by Korn Andras at https://bugs.gnu.org/59382

NEWS
bootstrap.conf
src/ioblksize.h

diff --git a/NEWS b/NEWS
index f43182757fd8718354e4db0baac9830f2f707bcc..78a429274a5c6fcbada5708ba4c5203da054ae06 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -13,6 +13,11 @@ GNU coreutils NEWS                                    -*- outline -*-
   'cp -rx / /mnt' no longer complains "cannot create directory /mnt/".
   [bug introduced in coreutils-9.1]
 
+  cp, mv, and install avoid allocating too much memory, and possibly
+  triggering "memory exhausted" failures, on file systems like ZFS,
+  which can return varied file system I/O block size values for files.
+  [bug introduced in coreutils-6.0]
+
   'mv --backup=simple f d/' no longer mistakenly backs up d/f to f~.
   [bug introduced in coreutils-9.1]
 
index 2477346731306c902ad1cccb63098afba939012f..f04c906242fa60899d87db2444a205e1e5d17bce 100644 (file)
@@ -59,6 +59,7 @@ gnulib_modules="
   config-h
   configmake
   copy-file-range
+  count-leading-zeros
   crypto/md5
   crypto/sha1
   crypto/sha256
index 7a56c1a51459134bb3d2a9b179827c913cf7076b..80d7931ba8d341cab671f3865f83c2802ab68dd1 100644 (file)
@@ -18,6 +18,7 @@
 
 /* sys/stat.h and minmax.h will already have been included by system.h. */
 #include "idx.h"
+#include "count-leading-zeros.h"
 #include "stat-size.h"
 
 
@@ -75,8 +76,33 @@ enum { IO_BUFSIZE = 128 * 1024 };
 static inline idx_t
 io_blksize (struct stat sb)
 {
+  /* Treat impossible blocksizes as if they were IO_BUFSIZE.  */
+  idx_t blocksize = ST_BLKSIZE (sb) <= 0 ? IO_BUFSIZE : ST_BLKSIZE (sb);
+
+  /* Use a blocksize of at least IO_BUFSIZE bytes, keeping it a
+     multiple of the original blocksize.  */
+  blocksize += (IO_BUFSIZE - 1) - (IO_BUFSIZE - 1) % blocksize;
+
+  /* For regular files we can ignore the blocksize if we think we know better.
+     ZFS sometimes understates the blocksize, because it thinks
+     apps stupidly allocate a block that large even for small files.
+     This misinformation can cause coreutils to use wrong-sized blocks.
+     Work around some of the performance bug by substituting the next
+     power of two when the reported blocksize is not a power of two.  */
+  if (S_ISREG (sb.st_mode)
+      && blocksize & (blocksize - 1))
+    {
+      int leading_zeros = count_leading_zeros_ll (blocksize);
+      if (IDX_MAX < ULLONG_MAX || leading_zeros)
+        {
+          unsigned long long power = 1ull << (ULLONG_WIDTH - leading_zeros);
+          if (power <= IDX_MAX)
+            blocksize = power;
+        }
+    }
+
   /* Don’t go above the largest power of two that fits in idx_t and size_t,
      as that is asking for trouble.  */
   return MIN (MIN (IDX_MAX, SIZE_MAX) / 2 + 1,
-              MAX (IO_BUFSIZE, ST_BLKSIZE (sb)));
+              blocksize);
 }