import Oracle_OSS nbdkit-1.44.1-6.el10_1

This commit is contained in:
AlmaLinux RelEng Bot 2026-04-06 13:24:09 -04:00
parent 8ef764c947
commit e7309e9d45
13 changed files with 1051 additions and 1 deletions

View File

@ -0,0 +1,56 @@
From 34ee72b180f2a35e35df7d763881e7cfc205c665 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Wed, 1 Apr 2026 14:34:39 +0100
Subject: [PATCH] cache, cow: Prefix ftruncate error with filter name
We had a peculiar qemu error reported:
server reported: ftruncate: File too large
It was unclear at first where this message came from. (Originally it
was thought to come from qemu). Prefix the filter name on the error
message to hopefully make this a little easier to debug in future.
(cherry picked from commit 63416a8347f9865cc69e162a1c1e42015d394b24)
---
filters/cache/blk.c | 2 +-
filters/cow/blk.c | 3 +--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/filters/cache/blk.c b/filters/cache/blk.c
index ba2c41b4..9a200dde 100644
--- a/filters/cache/blk.c
+++ b/filters/cache/blk.c
@@ -195,7 +195,7 @@ blk_set_size (uint64_t new_size)
return -1;
if (ftruncate (fd, ROUND_UP (size, blksize)) == -1) {
- nbdkit_error ("ftruncate: %m");
+ nbdkit_error ("cache: ftruncate: %m");
return -1;
}
diff --git a/filters/cow/blk.c b/filters/cow/blk.c
index ec51eea1..cc2f536b 100644
--- a/filters/cow/blk.c
+++ b/filters/cow/blk.c
@@ -225,7 +225,6 @@ blk_free (struct blk_overlay *blk)
}
}
-
/* Allocate or resize the overlay file and bitmap. */
int
blk_set_size (struct blk_overlay *blk, uint64_t new_size)
@@ -238,7 +237,7 @@ blk_set_size (struct blk_overlay *blk, uint64_t new_size)
return -1;
if (ftruncate (blk->fd, ROUND_UP (blk->size, blksize)) == -1) {
- nbdkit_error ("ftruncate: %m");
+ nbdkit_error ("cow: ftruncate: %m");
return -1;
}
--
2.47.3

View File

@ -0,0 +1,40 @@
From e60280f405be972d6ca585494825642817d37629 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Wed, 1 Apr 2026 15:30:15 +0100
Subject: [PATCH] cow: Don't leak blk->lock and blk->bm on error paths
Theoretically these were leaked on error paths, although in reality
nbdkit immediately exits on failure here so there wasn't a real issue.
Still it makes the code slightly cleaner.
(cherry picked from commit 04d2b3e30e980f7da787d4cd2a4cde839be756fb)
---
filters/cow/blk.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/filters/cow/blk.c b/filters/cow/blk.c
index cc2f536b..c3173fdd 100644
--- a/filters/cow/blk.c
+++ b/filters/cow/blk.c
@@ -174,8 +174,6 @@ blk_create (void)
}
blk->fd = -1;
- pthread_mutex_init (&blk->lock, NULL);
- bitmap_init (&blk->bm, blksize, 2 /* bits per block */);
filename = strdup (template);
if (filename == NULL) {
@@ -210,6 +208,9 @@ blk_create (void)
unlink (filename);
free (filename);
+ pthread_mutex_init (&blk->lock, NULL);
+ bitmap_init (&blk->bm, blksize, 2 /* bits per block */);
+
return blk;
}
--
2.47.3

View File

@ -0,0 +1,191 @@
From d0f211ae730f5cea4610b1e5455f2ca649c9ae69 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Wed, 1 Apr 2026 15:33:30 +0100
Subject: [PATCH] cow: Use a vector to store the overlay file descriptor
Instead of storing a single blk->fd field, replace this with a vector
of file descriptors. In this change it is only used to store a single
file descriptor, so there is no functional change so far.
(cherry picked from commit bd01870d6e29a83a95dee6c3ab36afff9d96b651)
---
filters/cow/blk.c | 48 ++++++++++++++++++++++++++++++-----------------
1 file changed, 31 insertions(+), 17 deletions(-)
diff --git a/filters/cow/blk.c b/filters/cow/blk.c
index c3173fdd..4957c871 100644
--- a/filters/cow/blk.c
+++ b/filters/cow/blk.c
@@ -98,6 +98,7 @@
#include "pread.h"
#include "pwrite.h"
#include "utils.h"
+#include "vector.h"
#include "cow.h"
#include "blk.h"
@@ -145,9 +146,11 @@ blk_unload (void)
free (template);
}
+DEFINE_VECTOR_TYPE (fd_vector, int);
+
struct blk_overlay {
- /* The temporary overlay. */
- int fd;
+ /* The temporary overlays. Each covers 8T virtual size. */
+ fd_vector fds;
/* This lock protects the bitmap from parallel access. */
pthread_mutex_t lock;
@@ -165,6 +168,7 @@ struct blk_overlay *
blk_create (void)
{
struct blk_overlay *blk;
+ int fd;
char *filename;
blk = calloc (1, sizeof *blk);
@@ -173,8 +177,9 @@ blk_create (void)
return NULL;
}
- blk->fd = -1;
+ blk->fds = (fd_vector)empty_vector;
+ /* We always create the first file, even if the disk size is 0. */
filename = strdup (template);
if (filename == NULL) {
nbdkit_error ("strdup: %m");
@@ -183,22 +188,22 @@ blk_create (void)
}
#ifdef HAVE_MKOSTEMP
- blk->fd = mkostemp (filename, O_CLOEXEC);
+ fd = mkostemp (filename, O_CLOEXEC);
#else
/* Not atomic, but this is only invoked during .load, so the race
* won't affect any plugin actions trying to fork
*/
- blk->fd = mkstemp (filename);
- if (blk->fd >= 0) {
- blk->fd = set_cloexec (blk->fd);
- if (blk->fd < 0) {
+ fd = mkstemp (filename);
+ if (fd >= 0) {
+ fd = set_cloexec (fd);
+ if (fd < 0) {
int e = errno;
unlink (template);
errno = e;
}
}
#endif
- if (blk->fd == -1) {
+ if (fd == -1) {
nbdkit_error ("mkostemp: %s: %m", template);
free (blk);
free (filename);
@@ -208,6 +213,13 @@ blk_create (void)
unlink (filename);
free (filename);
+ if (fd_vector_append (&blk->fds, fd) == -1) {
+ nbdkit_error ("realloc: %m");
+ close (fd);
+ free (blk);
+ return NULL;
+ }
+
pthread_mutex_init (&blk->lock, NULL);
bitmap_init (&blk->bm, blksize, 2 /* bits per block */);
@@ -218,8 +230,7 @@ void
blk_free (struct blk_overlay *blk)
{
if (blk) {
- if (blk->fd >= 0)
- close (blk->fd);
+ fd_vector_iter (&blk->fds, (void*)close);
bitmap_free (&blk->bm);
pthread_mutex_destroy (&blk->lock);
free (blk);
@@ -237,7 +248,7 @@ blk_set_size (struct blk_overlay *blk, uint64_t new_size)
if (bitmap_resize (&blk->bm, blk->size) == -1)
return -1;
- if (ftruncate (blk->fd, ROUND_UP (blk->size, blksize)) == -1) {
+ if (ftruncate (blk->fds.ptr[0], ROUND_UP (blk->size, blksize)) == -1) {
nbdkit_error ("cow: ftruncate: %m");
return -1;
}
@@ -271,6 +282,7 @@ blk_read_multiple (struct blk_overlay *blk,
off_t offset = blknum * blksize;
enum bm_entry state;
uint64_t b, runblocks;
+ const int fd = blk->fds.ptr[0];
/* Find out how many of the following blocks form a "run" with the
* same state. We can process that many blocks in one go.
@@ -329,7 +341,7 @@ blk_read_multiple (struct blk_overlay *blk,
"at offset %" PRIu64 " into the cache",
runblocks, offset);
- if (full_pwrite (blk->fd, block, blksize * runblocks, offset) == -1) {
+ if (full_pwrite (fd, block, blksize * runblocks, offset) == -1) {
*err = errno;
nbdkit_error ("pwrite: %m");
return -1;
@@ -339,7 +351,7 @@ blk_read_multiple (struct blk_overlay *blk,
}
}
else if (state == BLOCK_ALLOCATED) { /* Read overlay. */
- if (full_pread (blk->fd, block, blksize * runblocks, offset) == -1) {
+ if (full_pread (fd, block, blksize * runblocks, offset) == -1) {
*err = errno;
nbdkit_error ("pread: %m");
return -1;
@@ -379,6 +391,7 @@ blk_cache (struct blk_overlay *blk,
off_t offset = blknum * blksize;
enum bm_entry state = bitmap_get_blk (&blk->bm, blknum, BLOCK_NOT_ALLOCATED);
unsigned n = blksize, tail = 0;
+ const int fd = blk->fds.ptr[0];
if (offset + n > blk->size) {
tail = offset + n - blk->size;
@@ -391,7 +404,7 @@ blk_cache (struct blk_overlay *blk,
if (state == BLOCK_ALLOCATED) {
#if HAVE_POSIX_FADVISE
- int r = posix_fadvise (blk->fd, offset, blksize, POSIX_FADV_WILLNEED);
+ int r = posix_fadvise (fd, offset, blksize, POSIX_FADV_WILLNEED);
if (r) {
errno = r;
nbdkit_error ("posix_fadvise: %m");
@@ -416,7 +429,7 @@ blk_cache (struct blk_overlay *blk,
memset (block + n, 0, tail);
if (mode == BLK_CACHE_COW) {
- if (full_pwrite (blk->fd, block, blksize, offset) == -1) {
+ if (full_pwrite (fd, block, blksize, offset) == -1) {
*err = errno;
nbdkit_error ("pwrite: %m");
return -1;
@@ -431,12 +444,13 @@ blk_write (struct blk_overlay *blk,
uint64_t blknum, const uint8_t *block, int *err)
{
off_t offset = blknum * blksize;
+ const int fd = blk->fds.ptr[0];
if (cow_debug_verbose)
nbdkit_debug ("cow: blk_write block %" PRIu64 " (offset %" PRIu64 ")",
blknum, (uint64_t) offset);
- if (full_pwrite (blk->fd, block, blksize, offset) == -1) {
+ if (full_pwrite (fd, block, blksize, offset) == -1) {
*err = errno;
nbdkit_error ("pwrite: %m");
return -1;
--
2.47.3

View File

@ -0,0 +1,97 @@
From 0fc4138b253d3803ea2239df2f268c005ee3b3f4 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Wed, 1 Apr 2026 16:03:21 +0100
Subject: [PATCH] cow: Split out function to create temporary files
Just code refactoring.
(cherry picked from commit e8e409311b2c28b89da78b538196b9be018dd47d)
---
filters/cow/blk.c | 45 ++++++++++++++++++++++++++++-----------------
1 file changed, 28 insertions(+), 17 deletions(-)
diff --git a/filters/cow/blk.c b/filters/cow/blk.c
index 4957c871..96d613e2 100644
--- a/filters/cow/blk.c
+++ b/filters/cow/blk.c
@@ -164,27 +164,16 @@ struct blk_overlay {
uint64_t size;
};
-struct blk_overlay *
-blk_create (void)
+static int
+create_temporary_file (void)
{
- struct blk_overlay *blk;
int fd;
char *filename;
- blk = calloc (1, sizeof *blk);
- if (blk == NULL) {
- nbdkit_error ("calloc: %m");
- return NULL;
- }
-
- blk->fds = (fd_vector)empty_vector;
-
- /* We always create the first file, even if the disk size is 0. */
filename = strdup (template);
if (filename == NULL) {
nbdkit_error ("strdup: %m");
- free (blk);
- return NULL;
+ return -1;
}
#ifdef HAVE_MKOSTEMP
@@ -198,21 +187,43 @@ blk_create (void)
fd = set_cloexec (fd);
if (fd < 0) {
int e = errno;
- unlink (template);
+ unlink (filename);
errno = e;
}
}
#endif
if (fd == -1) {
nbdkit_error ("mkostemp: %s: %m", template);
- free (blk);
free (filename);
- return NULL;
+ return -1;
}
unlink (filename);
free (filename);
+ return fd;
+}
+
+struct blk_overlay *
+blk_create (void)
+{
+ struct blk_overlay *blk;
+ int fd;
+
+ blk = calloc (1, sizeof *blk);
+ if (blk == NULL) {
+ nbdkit_error ("calloc: %m");
+ return NULL;
+ }
+
+ blk->fds = (fd_vector)empty_vector;
+
+ /* We always create the first file, even if the disk size is 0. */
+ fd = create_temporary_file ();
+ if (fd == -1) {
+ free (blk);
+ return NULL;
+ }
if (fd_vector_append (&blk->fds, fd) == -1) {
nbdkit_error ("realloc: %m");
close (fd);
--
2.47.3

View File

@ -0,0 +1,30 @@
From 021b9fab2ba58dde338658052b45618fcbb8ebc7 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Wed, 1 Apr 2026 16:43:45 +0100
Subject: [PATCH] cow: Add name of the temporary file to debug output
If -D cow.verbose=1 display the name of the temporary file. The file
is deleted so it does not appear in the filesystem.
(cherry picked from commit d415227d30ce7056ded227bb9490e937de230ae1)
---
filters/cow/blk.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/filters/cow/blk.c b/filters/cow/blk.c
index 96d613e2..1012fb5e 100644
--- a/filters/cow/blk.c
+++ b/filters/cow/blk.c
@@ -198,6 +198,9 @@ create_temporary_file (void)
return -1;
}
+ if (cow_debug_verbose)
+ nbdkit_debug ("cow: created temporary file for overlay: %s", filename);
+
unlink (filename);
free (filename);
--
2.47.3

View File

@ -0,0 +1,292 @@
From c02b8506469203e537fdf6fe37d4447cc4d358a3 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Wed, 1 Apr 2026 16:37:01 +0100
Subject: [PATCH] cow: Support overlays larger than 16T on ext4
nbdkit-cow-filter writes the copy-on-write overlay into a temporary
file under /var/tmp which has the same size as the underlying virtual
disk. If the temporary directory being used is on ext4, this
effectively limits the size of the overlay to just under 16T, since
that is the maximum file size supported. (Note that the file is
sparse so this does not mean all the space is used.). For example:
$ nbdkit --filter=cow null 17T --run 'nbdinfo "$uri"'
nbdkit: null[1]: error: ftruncate: File too large
nbdkit: null[1]: error: ftruncate: File too large
nbdkit: null[1]: error: ftruncate: File too large
nbdinfo: nbd_opt_go: the server has no export named '': ftruncate: File too large: No such file or directory for the default export
nbdinfo: suggestion: to list all exports on the server, use --list
protocol: newstyle-fixed without TLS, using structured packets
This change splits the overlay into as many 8T chunks as needed, so
the ext4 maximum file size is no longer a limit.
Fixes: https://issues.redhat.com/browse/RHEL-163983
(cherry picked from commit eb79a0e0c63ba1884bde02dc884a26aea2fc4324)
---
filters/cow/blk.c | 69 ++++++++++++++++++++++++++++++++--
tests/Makefile.am | 6 ++-
tests/test-cow-huge.sh | 85 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 155 insertions(+), 5 deletions(-)
create mode 100755 tests/test-cow-huge.sh
diff --git a/filters/cow/blk.c b/filters/cow/blk.c
index 1012fb5e..defd0d94 100644
--- a/filters/cow/blk.c
+++ b/filters/cow/blk.c
@@ -67,6 +67,10 @@
*
* Since the overlay is a deleted temporary file, we can ignore FUA
* and flush commands.
+ *
+ * For disk sizes larger than 8T, the temporary file is split into
+ * multiple files, at most 8T in size. This ensures we can work on
+ * filesystems with file size limits like ext4 (max file size < 16T).
*/
#include <config.h>
@@ -81,6 +85,7 @@
#include <fcntl.h>
#include <limits.h>
#include <errno.h>
+#include <assert.h>
#include <sys/types.h>
#ifdef HAVE_ALLOCA_H
@@ -109,6 +114,8 @@ enum bm_entry {
BLOCK_TRIMMED = 3,
};
+#define MAX_FILE_SIZE (UINT64_C (8) * 1024 * 1024 * 1024 * 1024) /* 8T */
+
static const char *
state_to_string (enum bm_entry state)
{
@@ -256,13 +263,56 @@ int
blk_set_size (struct blk_overlay *blk, uint64_t new_size)
{
ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&blk->lock);
+ const uint64_t new_size_rounded_up = ROUND_UP (new_size, blksize);
+ uint64_t remainder;
+ size_t i, n;
+ int fd;
blk->size = new_size;
if (bitmap_resize (&blk->bm, blk->size) == -1)
return -1;
- if (ftruncate (blk->fds.ptr[0], ROUND_UP (blk->size, blksize)) == -1) {
+ /* How many temporary files do we need? Each temp file is limited to 8T. */
+ n = DIV_ROUND_UP (new_size_rounded_up, MAX_FILE_SIZE);
+ if (n == 0)
+ n = 1;
+ nbdkit_debug ("cow: using %zu temporary file(s)", n);
+
+ /* Create new temporary files in case it has expanded. */
+ while (blk->fds.len < n) {
+ fd = create_temporary_file ();
+ if (fd == -1)
+ return -1;
+ if (fd_vector_append (&blk->fds, fd) == -1) {
+ nbdkit_error ("realloc: %m");
+ /* The other fds are stored in the handle and will be closed by
+ * blk_free, so we only need to close this one.
+ */
+ close (fd);
+ return -1;
+ }
+ }
+
+ /* Remove any extra temporary files in case it has shrunk. */
+ while (blk->fds.len > n) {
+ close (blk->fds.ptr[n]);
+ fd_vector_remove (&blk->fds, n);
+ }
+
+ /* The first 0..n-1 temp files have to be truncated to the full
+ * size, with the last temp file being truncated to the remainder.
+ */
+ remainder = new_size_rounded_up;
+ for (i = 0; i < n-1; ++i) {
+ if (ftruncate (blk->fds.ptr[i], MAX_FILE_SIZE) == -1) {
+ nbdkit_error ("cow: ftruncate: %m");
+ return -1;
+ }
+ assert (remainder >= MAX_FILE_SIZE);
+ remainder -= MAX_FILE_SIZE;
+ }
+ if (ftruncate (blk->fds.ptr[n-1], remainder) == -1) {
nbdkit_error ("cow: ftruncate: %m");
return -1;
}
@@ -270,6 +320,15 @@ blk_set_size (struct blk_overlay *blk, uint64_t new_size)
return 0;
}
+/* Get the temporary file corresponding to a block. */
+static int
+get_fd_for_blknum (struct blk_overlay *blk, uint64_t blknum)
+{
+ size_t i = blknum / MAX_FILE_SIZE / blksize;
+ assert (i < blk->fds.len);
+ return blk->fds.ptr[i];
+}
+
/* This is a bit of a hack since usually this information is hidden in
* the blk module. However it is needed when calculating extents.
*/
@@ -296,7 +355,7 @@ blk_read_multiple (struct blk_overlay *blk,
off_t offset = blknum * blksize;
enum bm_entry state;
uint64_t b, runblocks;
- const int fd = blk->fds.ptr[0];
+ const int fd = get_fd_for_blknum (blk, blknum);
/* Find out how many of the following blocks form a "run" with the
* same state. We can process that many blocks in one go.
@@ -316,6 +375,8 @@ blk_read_multiple (struct blk_overlay *blk,
bitmap_get_blk (&blk->bm, blknum + b, BLOCK_NOT_ALLOCATED);
if (state != s)
break;
+ if (get_fd_for_blknum (blk, blknum + b) != fd)
+ break;
}
}
@@ -405,7 +466,7 @@ blk_cache (struct blk_overlay *blk,
off_t offset = blknum * blksize;
enum bm_entry state = bitmap_get_blk (&blk->bm, blknum, BLOCK_NOT_ALLOCATED);
unsigned n = blksize, tail = 0;
- const int fd = blk->fds.ptr[0];
+ const int fd = get_fd_for_blknum (blk, blknum);
if (offset + n > blk->size) {
tail = offset + n - blk->size;
@@ -458,7 +519,7 @@ blk_write (struct blk_overlay *blk,
uint64_t blknum, const uint8_t *block, int *err)
{
off_t offset = blknum * blksize;
- const int fd = blk->fds.ptr[0];
+ const int fd = get_fd_for_blknum (blk, blknum);
if (cow_debug_verbose)
nbdkit_debug ("cow: blk_write block %" PRIu64 " (offset %" PRIu64 ")",
diff --git a/tests/Makefile.am b/tests/Makefile.am
index d7053ba2..bc66b61c 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1724,7 +1724,10 @@ TESTS += \
test-cow-unaligned.sh \
$(NULL)
endif
-TESTS += test-cow-null.sh
+TESTS += \
+ test-cow-huge.sh \
+ test-cow-null.sh \
+ $(NULL)
EXTRA_DIST += \
test-cow.sh \
test-cow-block-size.sh \
@@ -1732,6 +1735,7 @@ EXTRA_DIST += \
test-cow-extents1.sh \
test-cow-extents2.sh \
test-cow-extents-large.sh \
+ test-cow-huge.sh \
test-cow-null.sh \
test-cow-on-read.sh \
test-cow-on-read-caches.sh \
diff --git a/tests/test-cow-huge.sh b/tests/test-cow-huge.sh
new file mode 100755
index 00000000..cad1f6cc
--- /dev/null
+++ b/tests/test-cow-huge.sh
@@ -0,0 +1,85 @@
+#!/usr/bin/env bash
+# nbdkit
+# Copyright Red Hat
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the name of Red Hat nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+# Test cow filter with huge overlays. This used to break because of
+# file size limits on /var/tmp.
+
+source ./functions.sh
+set -e
+set -x
+set -u
+
+requires_run
+requires_nbdsh_uri
+
+# ext4 has a 16T file size limit.
+size=17T
+
+define script <<'EOF'
+
+import random
+
+random.seed(None)
+
+M = 1024*1024
+T = 1024*1024*1024*1024
+
+# Test a few megabytes either side of the 8T boundary.
+overlay = bytearray(10 * M)
+overlay_offset = int(8 * T - len(overlay)/2)
+assert h.get_size() > overlay_offset + len(overlay)
+
+for iter in range(1, 200):
+ offset = random.randint(0, len(overlay) - 10)
+ count = min(len(overlay) - offset, random.randint(1, 1 * M))
+ buf = h.pread(count, overlay_offset + offset)
+ assert buf == overlay[offset:offset+count]
+ c = random.randint(1, 255)
+ buf = bytearray([c]) * count
+ h.pwrite(buf, overlay_offset + offset)
+ overlay[offset:offset+count] = buf
+ buf = h.pread(count, overlay_offset + offset)
+ assert buf == overlay[offset:offset+count]
+
+ if random.randint(0, 10) >= 7:
+ offset = random.randint(0, len(overlay) - 10)
+ count = min(len(overlay) - offset, random.randint(1, 1 * M))
+ zbuf = bytearray(count)
+ h.trim(count, overlay_offset + offset)
+ overlay[offset:offset+count] = zbuf
+ buf = h.pread(count, overlay_offset + offset)
+ assert buf == zbuf
+
+EOF
+export script
+
+nbdkit -fv --filter=cow null $size --run ' nbdsh -u "$uri" -c "$script" '
--
2.47.3

View File

@ -0,0 +1,133 @@
From 5261fbf8b7579bd6592fbfd51b6a99ce09a8b3c1 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Fri, 3 Apr 2026 13:41:36 +0100
Subject: [PATCH] cache, cow: Add prefix before more calls
Try to diagnose the origin of more error messages.
Related: commit 63416a8347f9865cc69e162a1c1e42015d394b24
(cherry picked from commit 25e1d1fdaedfd3c0cdce0600585f42478c93eda7)
---
filters/cache/blk.c | 14 +++++++-------
filters/cow/blk.c | 10 +++++-----
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/filters/cache/blk.c b/filters/cache/blk.c
index 9a200dde..02c33633 100644
--- a/filters/cache/blk.c
+++ b/filters/cache/blk.c
@@ -157,7 +157,7 @@ blk_init (void)
* least as large as the filesystem block size.
*/
if (fstatvfs (fd, &statvfs) == -1) {
- nbdkit_error ("fstatvfs: %s: %m", tmpdir);
+ nbdkit_error ("cache: fstatvfs: %s: %m", tmpdir);
return -1;
}
blksize = MAX (min_block_size, statvfs.f_bsize);
@@ -263,7 +263,7 @@ _blk_read_multiple (nbdkit_next *next,
if (full_pwrite (fd, block, blksize * runblocks, offset) == -1) {
*err = errno;
- nbdkit_error ("pwrite: %m");
+ nbdkit_error ("cache: pwrite: %m");
return -1;
}
for (b = 0; b < runblocks; ++b) {
@@ -275,7 +275,7 @@ _blk_read_multiple (nbdkit_next *next,
else { /* Read cache. */
if (full_pread (fd, block, blksize * runblocks, offset) == -1) {
*err = errno;
- nbdkit_error ("pread: %m");
+ nbdkit_error ("cache: pread: %m");
return -1;
}
for (b = 0; b < runblocks; ++b)
@@ -349,7 +349,7 @@ blk_cache (nbdkit_next *next,
if (full_pwrite (fd, block, blksize, offset) == -1) {
*err = errno;
- nbdkit_error ("pwrite: %m");
+ nbdkit_error ("cache: pwrite: %m");
return -1;
}
bitmap_set_blk (&bm, blknum, BLOCK_CLEAN);
@@ -360,7 +360,7 @@ blk_cache (nbdkit_next *next,
int r = posix_fadvise (fd, offset, blksize, POSIX_FADV_WILLNEED);
if (r) {
errno = r;
- nbdkit_error ("posix_fadvise: %m");
+ nbdkit_error ("cache: posix_fadvise: %m");
return -1;
}
#endif
@@ -390,7 +390,7 @@ blk_writethrough (nbdkit_next *next,
if (full_pwrite (fd, block, blksize, offset) == -1) {
*err = errno;
- nbdkit_error ("pwrite: %m");
+ nbdkit_error ("cache: pwrite: %m");
return -1;
}
@@ -424,7 +424,7 @@ blk_write (nbdkit_next *next,
if (full_pwrite (fd, block, blksize, offset) == -1) {
*err = errno;
- nbdkit_error ("pwrite: %m");
+ nbdkit_error ("cache: pwrite: %m");
return -1;
}
bitmap_set_blk (&bm, blknum, BLOCK_DIRTY);
diff --git a/filters/cow/blk.c b/filters/cow/blk.c
index defd0d94..afb48af9 100644
--- a/filters/cow/blk.c
+++ b/filters/cow/blk.c
@@ -418,7 +418,7 @@ blk_read_multiple (struct blk_overlay *blk,
if (full_pwrite (fd, block, blksize * runblocks, offset) == -1) {
*err = errno;
- nbdkit_error ("pwrite: %m");
+ nbdkit_error ("cow: pwrite: %m");
return -1;
}
for (b = 0; b < runblocks; ++b)
@@ -428,7 +428,7 @@ blk_read_multiple (struct blk_overlay *blk,
else if (state == BLOCK_ALLOCATED) { /* Read overlay. */
if (full_pread (fd, block, blksize * runblocks, offset) == -1) {
*err = errno;
- nbdkit_error ("pread: %m");
+ nbdkit_error ("cow: pread: %m");
return -1;
}
}
@@ -482,7 +482,7 @@ blk_cache (struct blk_overlay *blk,
int r = posix_fadvise (fd, offset, blksize, POSIX_FADV_WILLNEED);
if (r) {
errno = r;
- nbdkit_error ("posix_fadvise: %m");
+ nbdkit_error ("cow: posix_fadvise: %m");
return -1;
}
#endif
@@ -506,7 +506,7 @@ blk_cache (struct blk_overlay *blk,
if (mode == BLK_CACHE_COW) {
if (full_pwrite (fd, block, blksize, offset) == -1) {
*err = errno;
- nbdkit_error ("pwrite: %m");
+ nbdkit_error ("cow: pwrite: %m");
return -1;
}
bitmap_set_blk (&blk->bm, blknum, BLOCK_ALLOCATED);
@@ -527,7 +527,7 @@ blk_write (struct blk_overlay *blk,
if (full_pwrite (fd, block, blksize, offset) == -1) {
*err = errno;
- nbdkit_error ("pwrite: %m");
+ nbdkit_error ("cow: pwrite: %m");
return -1;
}
--
2.47.3

View File

@ -0,0 +1,30 @@
From ed6aca2c0a58a8b96531f015cf4d629f5208348b Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Fri, 3 Apr 2026 14:06:31 +0100
Subject: [PATCH] cow: Fix block -> fd calculation
This calculation was plainly wrong, so the other overlay files were
never used.
Fixes: commit eb79a0e0c63ba1884bde02dc884a26aea2fc4324
(cherry picked from commit 7180bbf0f3d7b29e7eccf30207980849f3586583)
---
filters/cow/blk.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/filters/cow/blk.c b/filters/cow/blk.c
index afb48af9..3a416d55 100644
--- a/filters/cow/blk.c
+++ b/filters/cow/blk.c
@@ -324,7 +324,7 @@ blk_set_size (struct blk_overlay *blk, uint64_t new_size)
static int
get_fd_for_blknum (struct blk_overlay *blk, uint64_t blknum)
{
- size_t i = blknum / MAX_FILE_SIZE / blksize;
+ size_t i = blknum * blksize / MAX_FILE_SIZE;
assert (i < blk->fds.len);
return blk->fds.ptr[i];
}
--
2.47.3

View File

@ -0,0 +1,112 @@
From 24d4313cddced9920a262e43e7dde70bb4aeae90 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Fri, 3 Apr 2026 13:50:37 +0100
Subject: [PATCH] cow: Fix offsets when overlay is split
Commit eb79a0e0c6 ("cow: Support overlays larger than 16T on ext4")
splits the overlay into 8T chunks. However we neglected to change the
offset we are writing to with the result that it still tried to write
beyond the end of files > 16T in size. Fix the offsets so they are
correct.
This is not a data corruptor since this and the previous commit
together were causing only the first overlay to be used with full
offsets. However it completely broke the intent of commit eb79a0e0c6.
The test that was added only tests around the 8T mark, so does not hit
this case even if TMPDIR is ext4. Adjust the test also.
The error seen is:
nbdkit: file.0: error: pwrite: File too large
Reported-by: Ming Xie
Fixes: commit eb79a0e0c63ba1884bde02dc884a26aea2fc4324
(cherry picked from commit c495b7f46b60a8e549327b2444591fe9e3173da0)
---
filters/cow/blk.c | 18 ++++++++++++------
tests/test-cow-huge.sh | 4 ++--
2 files changed, 14 insertions(+), 8 deletions(-)
diff --git a/filters/cow/blk.c b/filters/cow/blk.c
index 3a416d55..7a3f40c1 100644
--- a/filters/cow/blk.c
+++ b/filters/cow/blk.c
@@ -114,7 +114,8 @@ enum bm_entry {
BLOCK_TRIMMED = 3,
};
-#define MAX_FILE_SIZE (UINT64_C (8) * 1024 * 1024 * 1024 * 1024) /* 8T */
+/* Maximum overlay file size, 8T. This must be a power of 2. */
+#define MAX_FILE_SIZE (UINT64_C (8) * 1024 * 1024 * 1024 * 1024)
static const char *
state_to_string (enum bm_entry state)
@@ -416,7 +417,8 @@ blk_read_multiple (struct blk_overlay *blk,
"at offset %" PRIu64 " into the cache",
runblocks, offset);
- if (full_pwrite (fd, block, blksize * runblocks, offset) == -1) {
+ if (full_pwrite (fd, block, blksize * runblocks,
+ offset & (MAX_FILE_SIZE - 1)) == -1) {
*err = errno;
nbdkit_error ("cow: pwrite: %m");
return -1;
@@ -426,7 +428,8 @@ blk_read_multiple (struct blk_overlay *blk,
}
}
else if (state == BLOCK_ALLOCATED) { /* Read overlay. */
- if (full_pread (fd, block, blksize * runblocks, offset) == -1) {
+ if (full_pread (fd, block, blksize * runblocks,
+ offset & (MAX_FILE_SIZE - 1)) == -1) {
*err = errno;
nbdkit_error ("cow: pread: %m");
return -1;
@@ -479,7 +482,8 @@ blk_cache (struct blk_overlay *blk,
if (state == BLOCK_ALLOCATED) {
#if HAVE_POSIX_FADVISE
- int r = posix_fadvise (fd, offset, blksize, POSIX_FADV_WILLNEED);
+ int r = posix_fadvise (fd, offset & (MAX_FILE_SIZE - 1),
+ blksize, POSIX_FADV_WILLNEED);
if (r) {
errno = r;
nbdkit_error ("cow: posix_fadvise: %m");
@@ -504,7 +508,8 @@ blk_cache (struct blk_overlay *blk,
memset (block + n, 0, tail);
if (mode == BLK_CACHE_COW) {
- if (full_pwrite (fd, block, blksize, offset) == -1) {
+ if (full_pwrite (fd, block, blksize,
+ offset & (MAX_FILE_SIZE - 1)) == -1) {
*err = errno;
nbdkit_error ("cow: pwrite: %m");
return -1;
@@ -525,7 +530,8 @@ blk_write (struct blk_overlay *blk,
nbdkit_debug ("cow: blk_write block %" PRIu64 " (offset %" PRIu64 ")",
blknum, (uint64_t) offset);
- if (full_pwrite (fd, block, blksize, offset) == -1) {
+ if (full_pwrite (fd, block, blksize,
+ offset & (MAX_FILE_SIZE - 1)) == -1) {
*err = errno;
nbdkit_error ("cow: pwrite: %m");
return -1;
diff --git a/tests/test-cow-huge.sh b/tests/test-cow-huge.sh
index cad1f6cc..b459b1b8 100755
--- a/tests/test-cow-huge.sh
+++ b/tests/test-cow-huge.sh
@@ -53,9 +53,9 @@ random.seed(None)
M = 1024*1024
T = 1024*1024*1024*1024
-# Test a few megabytes either side of the 8T boundary.
+# Test a few megabytes either side of the 16T boundary.
overlay = bytearray(10 * M)
-overlay_offset = int(8 * T - len(overlay)/2)
+overlay_offset = int(16 * T - len(overlay)/2)
assert h.get_size() > overlay_offset + len(overlay)
for iter in range(1, 200):
--
2.47.3

View File

@ -0,0 +1,55 @@
From aba2a885ab8dc8796fdc8f100cd35ad621bb307a Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Fri, 3 Apr 2026 14:10:51 +0100
Subject: [PATCH] cow: Make some offset variables const
These variables are not modified so set them to const.
(cherry picked from commit 6ed71d8709cd03dd9a3c53cba9dd03e5b884031d)
---
filters/cow/blk.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/filters/cow/blk.c b/filters/cow/blk.c
index 7a3f40c1..4c1ee9d5 100644
--- a/filters/cow/blk.c
+++ b/filters/cow/blk.c
@@ -353,7 +353,7 @@ blk_read_multiple (struct blk_overlay *blk,
uint64_t blknum, uint64_t nrblocks,
uint8_t *block, bool cow_on_read, int *err)
{
- off_t offset = blknum * blksize;
+ const off_t offset = blknum * blksize;
enum bm_entry state;
uint64_t b, runblocks;
const int fd = get_fd_for_blknum (blk, blknum);
@@ -466,7 +466,7 @@ blk_cache (struct blk_overlay *blk,
{
/* XXX Could make this lock more fine-grained with some thought. */
ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&blk->lock);
- off_t offset = blknum * blksize;
+ const off_t offset = blknum * blksize;
enum bm_entry state = bitmap_get_blk (&blk->bm, blknum, BLOCK_NOT_ALLOCATED);
unsigned n = blksize, tail = 0;
const int fd = get_fd_for_blknum (blk, blknum);
@@ -523,7 +523,7 @@ int
blk_write (struct blk_overlay *blk,
uint64_t blknum, const uint8_t *block, int *err)
{
- off_t offset = blknum * blksize;
+ const off_t offset = blknum * blksize;
const int fd = get_fd_for_blknum (blk, blknum);
if (cow_debug_verbose)
@@ -547,7 +547,7 @@ int
blk_trim (struct blk_overlay *blk,
uint64_t blknum, int *err)
{
- off_t offset = blknum * blksize;
+ const off_t offset = blknum * blksize;
if (cow_debug_verbose)
nbdkit_debug ("cow: blk_trim block %" PRIu64 " (offset %" PRIu64 ")",
--
2.47.3

0
copy-patches.sh Normal file → Executable file
View File

0
nbdkit-find-provides Normal file → Executable file
View File

View File

@ -55,7 +55,7 @@
Name: nbdkit
Version: 1.44.1
Release: 4%{?dist}
Release: 6%{?dist}
Summary: NBD server
License: BSD-3-Clause
@ -91,6 +91,16 @@ Patch0008: 0008-count-Clarify-documentation.patch
Patch0009: 0009-vddk-Don-t-use-FNM_PATHNAME-when-matching-export-par.patch
Patch0010: 0010-file-Don-t-advertise-minimum_io_size-64K-the-max-sup.patch
Patch0011: 0011-file-Change-calculations-of-block-size-hints-for-blo.patch
Patch0012: 0012-cache-cow-Prefix-ftruncate-error-with-filter-name.patch
Patch0013: 0013-cow-Don-t-leak-blk-lock-and-blk-bm-on-error-paths.patch
Patch0014: 0014-cow-Use-a-vector-to-store-the-overlay-file-descripto.patch
Patch0015: 0015-cow-Split-out-function-to-create-temporary-files.patch
Patch0016: 0016-cow-Add-name-of-the-temporary-file-to-debug-output.patch
Patch0017: 0017-cow-Support-overlays-larger-than-16T-on-ext4.patch
Patch0018: 0018-cache-cow-Add-prefix-before-more-calls.patch
Patch0019: 0019-cow-Fix-block-fd-calculation.patch
Patch0020: 0020-cow-Fix-offsets-when-overlay-is-split.patch
Patch0021: 0021-cow-Make-some-offset-variables-const.patch
# For automatic RPM Provides generation.
# See: https://rpm-software-management.github.io/rpm/manual/dependency_generators.html
@ -1561,6 +1571,10 @@ fi
%changelog
* Fri Apr 03 2026 Richard W.M. Jones <rjones@redhat.com> - 1.44.1-6
- cow: Support overlays larger than 16T on ext4 + further fixes
resolves: RHEL-164552
* Mon Jan 12 2026 Richard W.M. Jones <rjones@redhat.com> - 1.44.1-4
- Fix v2v conversion failure when minimum_io_size > 64K
resolves: RHEL-140707