From 551f3f55180669ab0bfd6c5d9e3e0f38cb035172 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sat, 19 Nov 2022 19:04:36 -0800 Subject: [PATCH] cp: work around ZFS misinformation Problem reported by Korn Andras (Bug#59382). * 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 | 3 +++ bootstrap.conf | 1 + src/ioblksize.h | 28 +++++++++++++++++++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index b6b5201e7..9282352c8 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,9 @@ GNU coreutils NEWS -*- outline -*- 'cp -rx / /mnt' no longer complains "cannot create directory /mnt/". [bug introduced in coreutils-9.1] + cp, mv, and install no longer use overly large I/O buffers when ZFS + misinforms them about IO block sizes. + 'mv --backup=simple f d/' no longer mistakenly backs up d/f to f~. [bug introduced in coreutils-9.1] diff --git a/bootstrap.conf b/bootstrap.conf index 8e257a254..f8715068e 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -59,6 +59,7 @@ gnulib_modules=" config-h configmake copy-file-range + count-leading-zeros crypto/md5 crypto/sha1 crypto/sha256 diff --git a/src/ioblksize.h b/src/ioblksize.h index 8bd18ba05..aa367aa4e 100644 --- a/src/ioblksize.h +++ b/src/ioblksize.h @@ -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); } -- 2.37.2