Rebase to nbdkit 1.46.2

Backport nbdkit_timestamp and --port=0 fix from nbdkit 1.47.
resolves: RHEL-111242
This commit is contained in:
Richard W.M. Jones 2026-02-09 21:53:14 +00:00
parent f06a5d96be
commit d32b4b2509
34 changed files with 1979 additions and 855 deletions

View File

@ -1,143 +0,0 @@
From a54aedafc2738a4ce5cba078a9bfa632e3ced85b Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Sat, 3 Jan 2026 11:46:13 +0000
Subject: [PATCH] python: Link to C man pages for module functions
Since first writing the Python documentation, we now have much more
consistent coverage of the underlying C functions in separate man
pages, so link to those consistently here.
(cherry picked from commit 458928a627718b30456c47300366137d1dc2c109)
---
plugins/python/nbdkit-python-plugin.pod | 45 ++++++++++++++++++++-----
1 file changed, 36 insertions(+), 9 deletions(-)
diff --git a/plugins/python/nbdkit-python-plugin.pod b/plugins/python/nbdkit-python-plugin.pod
index 0bc99bdc..87750ea1 100644
--- a/plugins/python/nbdkit-python-plugin.pod
+++ b/plugins/python/nbdkit-python-plugin.pod
@@ -119,46 +119,62 @@ methods in the C<nbdkit> module:
Send a debug message to stderr or syslog if verbose messages are
enabled.
+See: L<nbdkit_debug(3)>
+
=head3 C<nbdkit.disconnect(force)>
Disconnect from the client. If C<force> is C<True> then nbdkit will
disconnect the client immediately.
+See: L<nbdkit_disconnect(3)>
+
=head3 C<nbdkit.export_name()>
Return the export name negotiated with the client as a Unicode string.
Note this should not be trusted because the client can send whatever
it wants.
+See: L<nbdkit_export_name(3)>
+
=head3 C<nbdkit.is_tls()>
Returns C<True> if the client completed TLS authentication, or
C<False> if the connection is plaintext.
+See: L<nbdkit_is_tls(3)>
+
=head3 C<nbdkit.nanosleep(secs, nsecs)>
Sleep for seconds and nanoseconds.
+See: L<nbdkit_nanosleep(3)>
+
=head3 C<nbdkit.parse_bool(str)>
Parse a human-readable boolean (such as "yes" or "false"), returning
C<True> or C<False>. Wraps the L<nbdkit_parse_bool(3)> function.
+See: L<nbdkit_parse_bool(3)>
+
=head3 C<nbdkit.parse_delay(what, str)>
Parse a delay or sleep (such as "10ms") into a pair (sec, nsec).
Wraps the L<nbdkit_parse_delay(3)> function.
+See: L<nbdkit_parse_delay(3)>
+
=head3 C<nbdkit.parse_size(str)>
-Parse a string (such as "100M") into a size in bytes. Wraps the
-L<nbdkit_parse_size(3)> C function.
+Parse a string (such as "100M") into a size in bytes.
+
+See: L<nbdkit_parse_size(3)>
=head3 C<nbdkit.parse_probability(what, str)>
Parse a string (such as "100%") into a probability, returning a
-floating point number. Wraps the L<nbdkit_parse_probability(3)>
-function.
+floating point number.
+
+See: L<nbdkit_parse_probability(3)>
=head3 C<nbdkit.peer_pid()>,
C<nbdkit.peer_uid()>,
@@ -171,22 +187,27 @@ Unix domain socket, and then only on some operating systems. The
security context is usually the SELinux label, IPSEC label or
NetLabel.
+See: L<nbdkit_peer_pid(3)>, L<nbdkit_peer_uid(3)>,
+L<nbdkit_peer_gid(3)> and L<nbdkit_peer_security_context(3)>
+
=head3 C<nbdkit.peer_tls_dn()>
Return the client TLS Distinguished Name.
-See L<nbdkit_peer_tls_dn(3)>.
+
+See: L<nbdkit_peer_tls_dn(3)>
=head3 C<nbdkit.peer_tls_issuer_dn()>
Return the client certificate issuer's TLS Distinguished Name.
-See L<nbdkit_peer_tls_issuer_dn(3)>.
+
+See: L<nbdkit_peer_tls_issuer_dn(3)>
=head3 C<nbdkit.read_password(value)>
Read a password from a config parameter. This returns the password as
-a Python C<bytes> object. See L<nbdkit_read_password(3)> for more
-information on the different ways that the C<value> parameter can be
-parsed.
+a Python C<bytes> object.
+
+See: L<nbdkit_read_password(3)>
=head3 C<nbdkit.set_error(err)>
@@ -201,15 +222,21 @@ C<nbdkit.set_error>:
nbdkit.set_error(errno.EPERM)
raise RuntimeError()
+See: L<nbdkit_set_error(3)>
+
=head3 C<nbdkit.shutdown()>
Request asynchronous server shutdown.
+See: L<nbdkit_shutdown(3)>
+
=head3 C<nbdkit.stdio_safe()>
Returns C<True> if it is safe to interact with stdin and stdout
during the configuration phase.
+See: L<nbdkit_stdio_safe(3)>
+
=head2 Module constants
After C<import nbdkit> the following constants are available. These
--
2.47.3

View File

@ -0,0 +1,78 @@
From 71158f3d0091027caf8a08b0cbe825e7875e4700 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Sun, 8 Feb 2026 21:53:17 +0000
Subject: [PATCH] vram: Cast cl_ulong to uint64_t before printing
On i686, the code did not compile:
vram.c: In function 'vram_dump_plugin':
vram.c:301:37: error: format '%lu' expects argument of type 'long unsigned int', but argument 2 has type 'cl_ulong' {aka 'long long unsigned int'} [-Werror=format=]
301 | printf ("vram_device_mem_size=%lu\n", all_devices.ptr[i].global_mem_size);
| ~~^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| long unsigned int cl_ulong {aka long long unsigned int}
| %llu
vram.c:302:50: error: format '%lu' expects argument of type 'long unsigned int', but argument 2 has type 'cl_ulong' {aka 'long long unsigned int'} [-Werror=format=]
302 | printf ("vram_device_max_memory_allocation=%lu\n",
| ~~^
| |
| long unsigned int
| %llu
303 | all_devices.ptr[i].max_mem_alloc_size);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| cl_ulong {aka long long unsigned int}
vram.c: In function 'vram_config_complete':
vram.c:364:52: error: format '%lu' expects argument of type 'long unsigned int', but argument 3 has type 'cl_ulong' {aka 'long long unsigned int'} [-Werror=format=]
364 | nbdkit_error ("OpenCL device '%s' has size %lu which is smaller than "
| ~~^
| |
| long unsigned int
| %llu
......
367 | device.name, device.global_mem_size);
| ~~~~~~~~~~~~~~~~~~~~~~
| |
| cl_ulong {aka long long unsigned int}
cc1: all warnings being treated as errors
(cherry picked from commit 47c5cceeb262273c6c25c3b7f979ea90f1371e0b)
---
plugins/vram/vram.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/plugins/vram/vram.c b/plugins/vram/vram.c
index fe8bf60e..0ed1e841 100644
--- a/plugins/vram/vram.c
+++ b/plugins/vram/vram.c
@@ -298,9 +298,10 @@ vram_dump_plugin (void)
printf ("vram_device_vendor=%s\n", all_devices.ptr[i].vendor);
printf ("vram_device_available=%s\n",
all_devices.ptr[i].available ? "yes" : "no");
- printf ("vram_device_mem_size=%lu\n", all_devices.ptr[i].global_mem_size);
- printf ("vram_device_max_memory_allocation=%lu\n",
- all_devices.ptr[i].max_mem_alloc_size);
+ printf ("vram_device_mem_size=%" PRIu64 "\n",
+ (uint64_t) all_devices.ptr[i].global_mem_size);
+ printf ("vram_device_max_memory_allocation=%" PRIu64 "\n",
+ (uint64_t) all_devices.ptr[i].max_mem_alloc_size);
printf ("vram_device_queue_on_device_max_size=%u\n",
all_devices.ptr[i].queue_on_device_max_size);
printf ("vram_device_queue_on_device_preferred_size=%u\n",
@@ -361,10 +362,11 @@ vram_config_complete (void)
/* Pick the size. */
if (size >= 0) { /* size appeared on the command line */
if (size > device.global_mem_size) {
- nbdkit_error ("OpenCL device '%s' has size %lu which is smaller than "
+ nbdkit_error ("OpenCL device '%s' has size %" PRIu64" "
+ "which is smaller than "
"the size given on the command line. To allocate the "
"maximum size, omit the size parameter.",
- device.name, device.global_mem_size);
+ device.name, (uint64_t) device.global_mem_size);
return -1;
}
}
--
2.47.3

View File

@ -1,44 +0,0 @@
From ed37c814307ad027529b052ce7a7a3d3d15cdc58 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Sat, 3 Jan 2026 11:54:07 +0000
Subject: [PATCH] python: Sort documentation for module functions in order
Updates: commit 02df929dd5ac7f24a969dd4e680b8cb50bf585a7
(cherry picked from commit e4dd09af5e0f836f4b306274517f381c23f6e461)
---
plugins/python/nbdkit-python-plugin.pod | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/plugins/python/nbdkit-python-plugin.pod b/plugins/python/nbdkit-python-plugin.pod
index 87750ea1..9ea00f5e 100644
--- a/plugins/python/nbdkit-python-plugin.pod
+++ b/plugins/python/nbdkit-python-plugin.pod
@@ -163,12 +163,6 @@ Wraps the L<nbdkit_parse_delay(3)> function.
See: L<nbdkit_parse_delay(3)>
-=head3 C<nbdkit.parse_size(str)>
-
-Parse a string (such as "100M") into a size in bytes.
-
-See: L<nbdkit_parse_size(3)>
-
=head3 C<nbdkit.parse_probability(what, str)>
Parse a string (such as "100%") into a probability, returning a
@@ -176,6 +170,12 @@ floating point number.
See: L<nbdkit_parse_probability(3)>
+=head3 C<nbdkit.parse_size(str)>
+
+Parse a string (such as "100M") into a size in bytes.
+
+See: L<nbdkit_parse_size(3)>
+
=head3 C<nbdkit.peer_pid()>,
C<nbdkit.peer_uid()>,
C<nbdkit.peer_gid()>,
--
2.47.3

View File

@ -1,4 +1,4 @@
From 4a492dc14d14a68bd4500c95be5dca442d448b65 Mon Sep 17 00:00:00 2001
From b546cd6870fcf16422d7fb240cb717fe4a298ce4 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 30 Dec 2025 15:24:13 +0000
Subject: [PATCH] server: Add nbdkit_debug_hexdiff function

View File

@ -1,253 +0,0 @@
From 5610d1a4cc6da743e99973bded79cf7761ee612f Mon Sep 17 00:00:00 2001
From: Eric Blake <eblake@redhat.com>
Date: Mon, 5 Jan 2026 10:43:46 -0600
Subject: [PATCH] blocksize-policy: Fix assertion failure on unaligned block
status
The blocksize-policy filter had a minor denial-of-service security
flaw, where a client could trigger the server to die from an assertion
failure by sending an unaligned block status request in violation of
NBD protocol recommendations (see the updated test within the patch
for a sample trigger). Note that libnbd makes it difficult to
trigger, as by default an unaligned request won't be sent to the
server. Additionally, use of blocksize-error-policy=error is not
impacted; and although the blocksize-policy filter defaults to an
error policy of allow, it makes less sense to use the filter in
production without opting in to blocksize-error-policy=error.
Rather than complicating the blocksize-policy filter to manually munge
its extents requests to an aligned boundary, I opted to instead relax
the server's nbdkit_extents_aligned to support unaligned inputs by
first widening the request to alignment boundaries and then truncating
back to the original offset after at least one aligned extent is
learned. The function still stops at the first unaligned extent,
rather than trying harder to use all of the plugin's underlying
information; I have plans to add a parameter in a later patch to
optionally behave more like nbdkit_extents_full, but wanted this patch
to focus on merely the assertion failure.
An audit of all callers of nbdkit_extents_aligned shows that only
blocksize-policy was vulnerable; the blocksize and swab filters only
ever pass in aligned values. And while at it, I made the interface
accept a 64-bit count, which makes usage easier when a client widens a
request near the 4G boundary up to an alignment boundary.
Since the flaw is minor, I've gone ahead and made this patch public.
However, in parallel I am pursuing with Red Hat security on whether a
CVE needs to be assigned.
Fixes: 82b60fcd ("blocksize-policy: Round extents to match minimum alignment", v1.43.11)
Signed-off-by: Eric Blake <eblake@redhat.com>
(cherry picked from commit d4fe15c78295be3ec5f162bf41f9b9da8a669689)
---
docs/nbdkit-filter.pod | 7 ++--
docs/nbdkit-security.pod | 6 +++
include/nbdkit-filter.h | 2 +-
server/extents.c | 25 +++++++----
tests/test-blocksize-policy-extents.sh | 57 ++++++++++++++++++++++++++
5 files changed, 85 insertions(+), 12 deletions(-)
diff --git a/docs/nbdkit-filter.pod b/docs/nbdkit-filter.pod
index 022799f4..1bb01675 100644
--- a/docs/nbdkit-filter.pod
+++ b/docs/nbdkit-filter.pod
@@ -1043,7 +1043,7 @@ A convenience function is provided to filters only which makes it
easier to ensure that the client only encounters aligned extents.
int nbdkit_extents_aligned (nbdkit_next *next,
- uint32_t count, uint64_t offset,
+ uint64_t count, uint64_t offset,
uint32_t flags, uint32_t align,
struct nbdkit_extents *extents, int *err);
@@ -1052,8 +1052,9 @@ obtained, where C<align> is a power of 2. Anywhere the underlying
plugin returns differing extents within C<align> bytes, this function
treats that portion of the disk as a single extent with zero and
sparse status bits determined by the intersection of all underlying
-extents. It is an error to call this function with C<count> or
-C<offset> that is not already aligned.
+extents. This function supports unaligned C<offset> or C<count>, but
+the given C<extents> must begin at C<offset> and not have any extents
+added yet.
=head2 C<.cache>
diff --git a/docs/nbdkit-security.pod b/docs/nbdkit-security.pod
index 64435231..38887bc6 100644
--- a/docs/nbdkit-security.pod
+++ b/docs/nbdkit-security.pod
@@ -47,6 +47,12 @@ See the full announcement and links to mitigation, tests and fixes
here:
L<https://lists.libguestfs.org/archives/list/guestfs@lists.libguestfs.org/message/67E7AASHHADIY7VAD3FFW2I67LTWVWYF/>
+=head2 denial of service attack by client sending unaligned block status
+
+See the patch here:
+L<https://lists.libguestfs.org/archives/list/guestfs@lists.libguestfs.org/thread/EQTMAKGDP53WYOUBCEKWLCWAJHVUNSXX/>
+Full announcement and links to mitigation coming.
+
=head1 SEE ALSO
L<nbdkit(1)>.
diff --git a/include/nbdkit-filter.h b/include/nbdkit-filter.h
index ffa7fa5d..aa99af65 100644
--- a/include/nbdkit-filter.h
+++ b/include/nbdkit-filter.h
@@ -135,7 +135,7 @@ NBDKIT_EXTERN_DECL (struct nbdkit_extents *, nbdkit_extents_full,
NBDKIT_ATTRIBUTE_NONNULL ((1, 5)));
NBDKIT_EXTERN_DECL (int, nbdkit_extents_aligned,
(nbdkit_next *next,
- uint32_t count, uint64_t offset,
+ uint64_t count, uint64_t offset,
uint32_t flags, uint32_t align,
struct nbdkit_extents *extents, int *err)
NBDKIT_ATTRIBUTE_NONNULL ((1, 6, 7)));
diff --git a/server/extents.c b/server/extents.c
index e0a2b224..8e507a94 100644
--- a/server/extents.c
+++ b/server/extents.c
@@ -213,7 +213,7 @@ nbdkit_add_extent (struct nbdkit_extents *exts,
/* Compute aligned extents on behalf of a filter. */
NBDKIT_DLL_PUBLIC int
nbdkit_extents_aligned (struct context *next_c,
- uint32_t count, uint64_t offset,
+ uint64_t count, uint64_t offset,
uint32_t flags, uint32_t align,
struct nbdkit_extents *exts, int *err)
{
@@ -222,22 +222,25 @@ nbdkit_extents_aligned (struct context *next_c,
struct nbdkit_extent *e, *e2;
int64_t size;
+ assert (exts->extents.len == 0);
+ assert (exts->start == offset);
+
size = next->get_size (next_c);
if (size == -1) {
*err = EIO;
return -1;
}
- assert (IS_ALIGNED (offset, align));
- assert (IS_ALIGNED (count, align) || offset + count == size);
+ exts->start = ROUND_DOWN (offset, align);
+ count = MIN (ROUND_UP (offset + count, align), size) - exts->start;
/* Perform an initial query, then scan for the first unaligned extent. */
- if (next->extents (next_c, count, offset, flags, exts, err) == -1)
+ if (next->extents (next_c, count, exts->start, flags, exts, err) == -1)
return -1;
for (i = 0; i < exts->extents.len; ++i) {
e = &exts->extents.ptr[i];
if (!IS_ALIGNED (e->length, align)) {
/* If the unalignment is past align, just truncate and return early */
- if (e->offset + e->length > offset + align) {
+ if (e->offset + e->length > exts->start + align) {
e->length = ROUND_DOWN (e->length, align);
exts->extents.len = i + !!e->length;
exts->next = e->offset + e->length;
@@ -271,13 +274,13 @@ nbdkit_extents_aligned (struct context *next_c,
CLEANUP_EXTENTS_FREE struct nbdkit_extents *extents2 = NULL;
extents2 = nbdkit_extents_new (e->offset + e->length,
- offset + align);
+ exts->start + align);
if (extents2 == NULL) {
*err = errno;
return -1;
}
if (next->extents (next_c, align - e->length,
- offset + e->length,
+ exts->start + e->length,
flags & ~NBDKIT_FLAG_REQ_ONE,
extents2, err) == -1)
return -1;
@@ -298,7 +301,13 @@ nbdkit_extents_aligned (struct context *next_c,
break;
}
}
- /* Once we get here, all extents are aligned. */
+ /* Once we get here, all extents are aligned. Trim back to the
+ * original offset if it was unaligned.
+ */
+ e = &exts->extents.ptr[0];
+ e->length -= offset - exts->start;
+ e->offset += offset - exts->start;
+ exts->start = offset;
return 0;
}
diff --git a/tests/test-blocksize-policy-extents.sh b/tests/test-blocksize-policy-extents.sh
index 46f804bb..688161ba 100755
--- a/tests/test-blocksize-policy-extents.sh
+++ b/tests/test-blocksize-policy-extents.sh
@@ -40,6 +40,8 @@ set -u
requires_run
requires_plugin data
requires_nbdinfo
+requires nbdsh --base-allocation --version
+requires_nbdsh_uri
files="blocksize-policy-extents.out"
rm -f $files
@@ -69,3 +71,58 @@ diff -u - blocksize-policy-extents.out <<EOF
0 4294967296 3 hole,zero
4294967296 512 0 data
EOF
+
+# Check that unaligned requests are rejected when required
+define script <<\EOF
+def print_extents(context, offset, extents, err):
+ assert context == nbd.CONTEXT_BASE_ALLOCATION;
+ print(extents)
+
+h.set_strict_mode(0)
+try:
+ h.block_status(511, 512, print_extents)
+except nbd.Error:
+ print("detected misaligned count")
+try:
+ h.block_status(512, 511, print_extents)
+except nbd.Error:
+ print("detected misaligned offset")
+h.block_status(513, 32256, print_extents)
+h.block_status(1, 32768, print_extents)
+EOF
+export script
+nbdkit data "@32k 1" --filter=blocksize-policy \
+ blocksize-minimum=512 blocksize-error-policy=error \
+ --run 'nbdsh --base-allocation -u "$uri" -c "$script"' \
+ > blocksize-policy-extents.out
+diff -u - blocksize-policy-extents.out <<EOF
+detected misaligned count
+detected misaligned offset
+[512, 3]
+[1, 0]
+EOF
+
+# Check that unaligned requests still work when permitted (a user could trigger
+# an assertion failure prior to 1.48, as a minor security flaw)
+define script <<\EOF
+def print_extents(context, offset, extents, err):
+ assert context == nbd.CONTEXT_BASE_ALLOCATION;
+ print(extents)
+
+h.set_strict_mode(0)
+h.block_status(511, 512, print_extents)
+h.block_status(512, 511, print_extents)
+h.block_status(2, 32767, print_extents)
+h.block_status(1, 32768, print_extents)
+EOF
+export script
+nbdkit data "@32k 1" --filter=blocksize-policy \
+ blocksize-minimum=512 blocksize-error-policy=allow \
+ --run 'nbdsh --base-allocation -u "$uri" -c "$script"' \
+ > blocksize-policy-extents.out
+diff -u - blocksize-policy-extents.out <<EOF
+[32256, 3]
+[32257, 3]
+[1, 3]
+[1, 0]
+EOF
--
2.47.3

View File

@ -1,4 +1,4 @@
From e9eed2a484ee659eeec11db6b066973435d99110 Mon Sep 17 00:00:00 2001
From 567f7449c9e9486b85530b9a56a4e9ba735a5faf Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 30 Dec 2025 17:09:07 +0000
Subject: [PATCH] checkwrite: Display differences if -D checkwrite.showdiffs=1

View File

@ -1,4 +1,4 @@
From 66b5fdc13d30025daf9e7b953eaa2f83670d094e Mon Sep 17 00:00:00 2001
From ad67a7bbd2b596291c696e9718d68800f9a02734 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 30 Dec 2025 21:59:24 +0000
Subject: [PATCH] docs/nbdkit_debug_hexdump.pod: Document when hexdiff was

View File

@ -1,46 +0,0 @@
From fcda6008ff61253a2415194b85b43328be5e1910 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Wed, 7 Jan 2026 20:25:56 +0000
Subject: [PATCH] file: Don't advertise minimum_io_size > 64K (the max
supported by NBD)
If you create an LVM thin pool with chunk size of 128K then the kernel
will advertise /sys/block/dm-X/queue/minimum_io_size as 131072 (128K)
and the file plugin will set the minimum block size to 128K as well.
However the NBD protocol only supports a max of 64K, thus we get the
following failure:
nbdkit: file[2]: error: plugin must set minimum block size between 1 and 64K
This is not a hard limit and the kernel will split smaller requests,
so the simplest solution is to round down larger sizes to 64K.
Fixes: https://issues.redhat.com/browse/RHEL-139390
Reported-by: Nijin Ashok
Thanks: Eric Blake
See: https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md#size-constraints
(cherry picked from commit 42b72dc751030c1210bc2d50f6ff0c1c1a902afc)
---
plugins/file/file.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/plugins/file/file.c b/plugins/file/file.c
index 6a8e65c9..c69d837c 100644
--- a/plugins/file/file.c
+++ b/plugins/file/file.c
@@ -748,6 +748,12 @@ file_open (int readonly)
if (ioctl (h->fd, BLKIOMIN, &minimum_io_size) == -1)
nbdkit_debug ("cannot get BLKIOMIN: %s: %m", h->name);
+ /* This is the maximum that NBD supports. For Linux devices,
+ * minimum_io_size is only a hint and smaller operations work.
+ */
+ if (minimum_io_size > 65536)
+ minimum_io_size = 65536;
+
if (ioctl (h->fd, BLKIOOPT, &optimal_io_size) == -1)
nbdkit_debug ("cannot get BLKIOOPT: %s: %m", h->name);
else if (optimal_io_size == 0)
--
2.47.3

View File

@ -1,4 +1,4 @@
From 0296eb8b718bfd5345027c307bc9df69c25fc8b4 Mon Sep 17 00:00:00 2001
From 3a75b229eec051b473886f7a324751c89d87a35f Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 30 Dec 2025 22:01:04 +0000
Subject: [PATCH] docs/nbdkit_debug_hexdump.pod: Add a link back to

View File

@ -1,33 +0,0 @@
From ca21ff4ca62b26028a7792b51189188fd5049f65 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Thu, 8 Jan 2026 21:05:23 +0000
Subject: [PATCH] todo: Add note about problems with file plugin block_size
(cherry picked from commit 2155ff48364a4d1a7368cf4f43ed78ce1ee47dd3)
---
TODO.md | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/TODO.md b/TODO.md
index 8488dd95..ff72ed25 100644
--- a/TODO.md
+++ b/TODO.md
@@ -199,6 +199,15 @@ nbdkit-file-plugin:
- For more, see comments in
https://gitlab.com/nbdkit/nbdkit/-/merge_requests/88
+* Block size constraints mapping between Linux hints and the
+ file_block_size callback could be improved. Our correspondent
+ writes:
+
+ > the kernel's minimum_io_size is a hint, while logical_block_size
+ is the hard limit. nbdkit should probably be setting "minimum" to
+ logical_block_size, and using the
+ max(minimum_io_size,optimal_io_size) for "preferred"
+
nbdkit-vram-plugin:
* Investigate why, on AMD Radeon, trim does not immediately free Video
--
2.47.3

View File

@ -1,4 +1,4 @@
From c3092c8ff0d1cd7056103f1bc855f8ab9bccc1d8 Mon Sep 17 00:00:00 2001
From baad76a69681c0442d12010a53eb54677ff47953 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Thu, 15 Jan 2026 12:07:40 +0000
Subject: [PATCH] Add new nbdkit_name() function

View File

@ -1,111 +0,0 @@
From 6a0ec916add4d66f1dd94a97b627a09321dda0d8 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Thu, 8 Jan 2026 21:38:36 +0000
Subject: [PATCH] file: Change calculations of block size hints for block
devices
Related: https://issues.redhat.com/browse/RHEL-139390
Related: commit 42b72dc751030c1210bc2d50f6ff0c1c1a902afc
Thanks: Eric Blake, Mikulas Patocka
(cherry picked from commit d0ee3c8a6dde446385c9b8e02f8d4623cb4d4184)
---
TODO.md | 9 ---------
plugins/file/file.c | 45 ++++++++++++++++++++++++++++-----------------
2 files changed, 28 insertions(+), 26 deletions(-)
diff --git a/TODO.md b/TODO.md
index ff72ed25..8488dd95 100644
--- a/TODO.md
+++ b/TODO.md
@@ -199,15 +199,6 @@ nbdkit-file-plugin:
- For more, see comments in
https://gitlab.com/nbdkit/nbdkit/-/merge_requests/88
-* Block size constraints mapping between Linux hints and the
- file_block_size callback could be improved. Our correspondent
- writes:
-
- > the kernel's minimum_io_size is a hint, while logical_block_size
- is the hard limit. nbdkit should probably be setting "minimum" to
- logical_block_size, and using the
- max(minimum_io_size,optimal_io_size) for "preferred"
-
nbdkit-vram-plugin:
* Investigate why, on AMD Radeon, trim does not immediately free Video
diff --git a/plugins/file/file.c b/plugins/file/file.c
index c69d837c..abd78ffc 100644
--- a/plugins/file/file.c
+++ b/plugins/file/file.c
@@ -414,7 +414,7 @@ file_config_complete (void)
static void
file_dump_plugin (void)
{
-#if defined(BLKIOMIN) && defined(BLKIOOPT)
+#if defined(BLKSSZGET) && defined(BLKIOMIN) && defined(BLKIOOPT)
printf ("file_block_size=yes\n");
#endif
#ifdef BLKROTATIONAL
@@ -741,32 +741,43 @@ file_open (int readonly)
#endif
h->minimum = h->preferred = h->maximum = 0;
-#if defined(BLKIOMIN) && defined(BLKIOOPT)
+#if defined(BLKSSZGET) && defined(BLKIOMIN) && defined(BLKIOOPT)
if (h->is_block_device) {
- unsigned int minimum_io_size = 0, optimal_io_size = 0;
+ int logical_block_size = 0;
+ unsigned minimum_io_size = 0, optimal_io_size = 0;
+
+ if (ioctl (h->fd, BLKSSZGET, &logical_block_size) == -1)
+ nbdkit_debug ("ioctl: %s: %s: %m", "BLKSSZGET", h->name);
if (ioctl (h->fd, BLKIOMIN, &minimum_io_size) == -1)
- nbdkit_debug ("cannot get BLKIOMIN: %s: %m", h->name);
-
- /* This is the maximum that NBD supports. For Linux devices,
- * minimum_io_size is only a hint and smaller operations work.
- */
- if (minimum_io_size > 65536)
- minimum_io_size = 65536;
+ nbdkit_debug ("ioctl: %s: %s: %m", "BLKIOMIN", h->name);
if (ioctl (h->fd, BLKIOOPT, &optimal_io_size) == -1)
- nbdkit_debug ("cannot get BLKIOOPT: %s: %m", h->name);
+ nbdkit_debug ("ioctl: %s: %s: %m", "BLKIOOPT", h->name);
else if (optimal_io_size == 0)
/* All devices in the Linux kernel except for MD report optimal
- * as 0. In that case guess a good value.
+ * as 0. In that case use logical_block_size.
*/
- optimal_io_size = MAX (minimum_io_size, 4096);
+ optimal_io_size = logical_block_size;
/* Check the values are sane before using them. */
- if (minimum_io_size >= 512 && is_power_of_2 (minimum_io_size) &&
- optimal_io_size >= minimum_io_size && is_power_of_2 (optimal_io_size)) {
- h->minimum = minimum_io_size;
- h->preferred = optimal_io_size;
+ if (logical_block_size >= 512 && is_power_of_2 (logical_block_size) &&
+ minimum_io_size >= 512 && is_power_of_2 (minimum_io_size) &&
+ minimum_io_size >= logical_block_size &&
+ optimal_io_size >= 512 && is_power_of_2 (optimal_io_size) &&
+ optimal_io_size >= logical_block_size) {
+ /* The mapping from Linux kernel settings to NBD protocol block
+ * sizes is not obvious. logical_block_size is the sector size,
+ * and anything smaller than this will cause a RMW cycle. For
+ * the preferred size, we are advised to use the largest of
+ * minimum_io_size and optimal_io_size. For this plugin maximum
+ * can be the largest that NBD can handle.
+ */
+
+ /* 64K is the largest minimum that the NBD protocol supports. */
+ h->minimum = MIN (65536, logical_block_size);
+
+ h->preferred = MAX (minimum_io_size, optimal_io_size);
h->maximum = 0xffffffff;
}
}
--
2.47.3

View File

@ -1,55 +0,0 @@
From 121dc9e9d576bba22731dcc89fdc6b9b21a3f742 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Mon, 12 Jan 2026 14:08:25 +0000
Subject: [PATCH] qcow2dec: Don't pass flags from .extents through to .pread
Using the qcow2dec filter could pass the flags parameter from the
.extents callback through to the .pread callback. Since pread flags
should always be 0, but extents can contain flags, this could cause
the following failure:
nbdkit: backend.c:666: backend_pread: Assertion `flags == 0' failed.
This failure especially happened when the client was qemu-img convert
since that client uses req_one. It did not happen for nbdcopy.
The relevant section of the stack trace was:
#7 0x0000555555556440 in backend_pread (c=0x7ffff0000f70, buf=0x7ffff00ba3c0,
count=65536, offset=262144, flags=4, err=0x7ffff6cb6810)
at /home/rjones/d/nbdkit/server/backend.c:666
#8 0x00007ffff753a436 in read_l2_entry (next=next@entry=0x7ffff0000f70,
offset=offset@entry=0, flags=flags@entry=4,
l2_present=l2_present@entry=0x7ffff6cb664f,
l2_entry=l2_entry@entry=0x7ffff6cb6650, err=err@entry=0x7ffff6cb6810)
at /home/rjones/d/nbdkit/filters/qcow2dec/qcow2dec.c:611
#9 0x00007ffff753af83 in qcow2dec_extents (next=0x7ffff0000f70,
handle=<optimized out>, count32=<optimized out>, offset=0, flags=4,
extents=0x7ffff00775d0, err=0x7ffff6cb6810)
at /home/rjones/d/nbdkit/filters/qcow2dec/qcow2dec.c:927
Fixes: commit 4c5e65c9a14f2f923d56877f041023682d13e2ea
(cherry picked from commit 2f176e58c13e42d3d32ef2e42ff81fb3e74df83b)
---
filters/qcow2dec/qcow2dec.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/filters/qcow2dec/qcow2dec.c b/filters/qcow2dec/qcow2dec.c
index 02769b69..0349cd11 100644
--- a/filters/qcow2dec/qcow2dec.c
+++ b/filters/qcow2dec/qcow2dec.c
@@ -924,7 +924,10 @@ qcow2dec_extents (nbdkit_next *next,
uint64_t file_offset;
struct nbdkit_extent e = { .offset = offset, .length = cluster_size };
- if (read_l2_entry (next, offset, flags, &l2_present, &l2_entry, err) == -1)
+ /* Note: Don't pass flags here, since this is expecting pread
+ * flags (always 0).
+ */
+ if (read_l2_entry (next, offset, 0, &l2_present, &l2_entry, err) == -1)
return -1;
/* L2 table is unallocated. */
--
2.47.3

View File

@ -1,4 +1,4 @@
From 67ce065641e4da313e54b80352c6d7a8599d08ab Mon Sep 17 00:00:00 2001
From dad58135284cb5e884bccdc8868b1ba2e245a3d3 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Thu, 15 Jan 2026 15:02:53 +0000
Subject: [PATCH] server/test-public.c: Add process_name dummy variable

View File

@ -0,0 +1,577 @@
From 630f3eecbecab4c5d46c84fe48c112b503effb5a Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Thu, 29 Jan 2026 10:24:58 +0000
Subject: [PATCH] Add new nbdkit_timestamp() function
This function can be used to flexibly add timestamps to debug and
error messages (or any kind of logging).
(cherry picked from commit 773d958c8f2a5043053d385b8bfe287c43ee63d4)
---
.gitignore | 1 +
docs/Makefile.am | 7 +++
docs/nbdkit_debug.pod | 4 ++
docs/nbdkit_error.pod | 6 +++
docs/nbdkit_timestamp.pod | 75 ++++++++++++++++++++++++++++++++
include/nbdkit-common.h | 1 +
server/internal.h | 1 +
server/nbdkit.syms | 1 +
server/public.c | 55 +++++++++++++++++++++++-
server/test-public.c | 6 +++
server/threadlocal.c | 20 +++++++++
tests/Makefile.am | 25 +++++++++++
tests/test-timestamp-plugin.c | 81 +++++++++++++++++++++++++++++++++++
tests/test-timestamp.sh | 61 ++++++++++++++++++++++++++
14 files changed, 343 insertions(+), 1 deletion(-)
create mode 100644 docs/nbdkit_timestamp.pod
create mode 100644 tests/test-timestamp-plugin.c
create mode 100755 tests/test-timestamp.sh
diff --git a/.gitignore b/.gitignore
index 55d971e6..2b86a056 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,6 +48,7 @@ docs/nbdkit_realpath.3
docs/nbdkit_shutdown.3
docs/nbdkit_stdio_safe.3
docs/nbdkit_strdup_intern.3
+docs/nbdkit_timestamp.3
docs/nbdkit-tracing.3
filters/*/*.1
plugins/*/*.1
diff --git a/docs/Makefile.am b/docs/Makefile.am
index a43b13ed..742f5460 100644
--- a/docs/Makefile.am
+++ b/docs/Makefile.am
@@ -85,6 +85,7 @@ EXTRA_DIST = \
nbdkit_shutdown.pod \
nbdkit_stdio_safe.pod \
nbdkit_strdup_intern.pod \
+ nbdkit_timestamp.pod \
nbdkit-tls.pod \
nbdkit-tracing.pod \
synopsis.txt \
@@ -176,6 +177,7 @@ generated_mans = \
nbdkit_shutdown.3 \
nbdkit_stdio_safe.3 \
nbdkit_strdup_intern.3 \
+ nbdkit_timestamp.3 \
nbdkit-tls.1 \
nbdkit-tracing.3 \
$(NULL)
@@ -348,6 +350,11 @@ nbdkit_strdup_intern.3: nbdkit_strdup_intern.pod $(top_builddir)/podwrapper.pl
--html $(top_builddir)/html/$@.html \
$<
+nbdkit_timestamp.3: nbdkit_timestamp.pod $(top_builddir)/podwrapper.pl
+ $(PODWRAPPER) --section=3 --man $@ \
+ --html $(top_builddir)/html/$@.html \
+ $<
+
nbdkit-tls.1: nbdkit-tls.pod $(top_builddir)/podwrapper.pl
$(PODWRAPPER) --section=1 --man $@ \
--html $(top_builddir)/html/$@.html \
diff --git a/docs/nbdkit_debug.pod b/docs/nbdkit_debug.pod
index d58d9bda..ee1ba719 100644
--- a/docs/nbdkit_debug.pod
+++ b/docs/nbdkit_debug.pod
@@ -25,6 +25,9 @@ and also support the glibc extension of a single C<%m> in a format
string expanding to C<strerror(errno)>, even on platforms that don't
support that natively.
+C<nbdkit_debug> does not timestamp messages. To add timestamps, see
+L<nbdkit_timestamp(3)>.
+
=head1 LANGUAGE BINDINGS
In L<nbdkit-golang-plugin(3)>:
@@ -60,6 +63,7 @@ L<nbdkit(1)>,
L<nbdkit_debug_hexdiff(3)>,
L<nbdkit_debug_hexdump(3)>,
L<nbdkit_error(3)>,
+L<nbdkit_timestamp(3)>,
L<nbdkit-plugin(3)>,
L<nbdkit-filter(3)>.
diff --git a/docs/nbdkit_error.pod b/docs/nbdkit_error.pod
index 2b41a4f0..d9283baa 100644
--- a/docs/nbdkit_error.pod
+++ b/docs/nbdkit_error.pod
@@ -61,6 +61,11 @@ The default is to send error messages to stderr, unless nbdkit forks
into the background in which case they are sent to syslog. For more
information read the description in L<nbdkit(1)>.
+=head2 Timestamps
+
+C<nbdkit_error> does not timestamp messages. To add timestamps, see
+L<nbdkit_timestamp(3)>.
+
=head1 LANGUAGE BINDINGS
Most language bindings do not expose these functions explicitly.
@@ -78,6 +83,7 @@ C<nbdkit_set_error> was added in nbdkit 1.2.
L<nbdkit(1)>,
L<nbdkit_debug(3)>,
+L<nbdkit_timestamp(3)>,
L<nbdkit-plugin(3)>,
L<nbdkit-filter(3)>.
diff --git a/docs/nbdkit_timestamp.pod b/docs/nbdkit_timestamp.pod
new file mode 100644
index 00000000..2662b204
--- /dev/null
+++ b/docs/nbdkit_timestamp.pod
@@ -0,0 +1,75 @@
+=head1 NAME
+
+nbdkit_timestamp - generate a timestamp for log messages
+
+=head1 SYNOPSIS
+
+ #include <nbdkit-plugin.h>
+
+ const char *nbdkit_timestamp (void);
+
+=head1 DESCRIPTION
+
+C<nbdkit_timestamp> generates a timestamp as a printable string which
+may be added to debug or error messages (see L<nbdkit_debug(3)>,
+L<nbdkit_error(3)>).
+
+It can be used like this:
+
+ nbdkit_debug ("%s: this is a debug message", nbdkit_timestamp ());
+
+which would produce a debug message like this:
+
+ debug: 2026-01-01 12:00:00.000333: this is a debug message
+
+The timestamps show the wallclock time in approximately ISO 8601
+format, but less ugly. They are always shown in UTC, in 24 hour
+notation, to the nearest microsecond. (On Windows, timestamps use the
+same format but are rounded to the nearest millisecond.)
+
+You can also separate the nbdkit_timestamp() call from the place where
+it is used, allowing you to accurately timestamp, for example, the
+point when a system call was invoked:
+
+ const char *ts = nbdkit_timestamp ();
+ int r = pwrite (fd, ...);
+ if (r == -1) {
+ /* The error message shows the time that pwrite started. */
+ nbdkit_error ("%s: pwrite: %m", ts);
+ return -1;
+ }
+
+=head1 RETURN VALUE
+
+The function returns a constant string allocated in thread-local
+storage. The string is valid until the next call to
+C<nbdkit_timestamp> in the same thread.
+
+It never returns C<NULL>. In the (very unlikely) case that there was
+an error generating the timestamp, the string C<"!"> is returned.
+
+=begin comment
+
+=head1 LANGUAGE BINDINGS
+
+=end comment
+
+=head1 HISTORY
+
+C<nbdkit_name> was added in nbdkit 1.48.
+
+=head1 SEE ALSO
+
+L<nbdkit(1)>,
+L<nbdkit_debug(3)>,
+L<nbdkit_error(3)>,
+L<nbdkit-plugin(3)>,
+L<nbdkit-filter(3)>.
+
+=head1 AUTHORS
+
+Richard W.M. Jones
+
+=head1 COPYRIGHT
+
+Copyright Red Hat
diff --git a/include/nbdkit-common.h b/include/nbdkit-common.h
index 4b42c9d7..230365db 100644
--- a/include/nbdkit-common.h
+++ b/include/nbdkit-common.h
@@ -205,6 +205,7 @@ NBDKIT_EXTERN_DECL (char *, nbdkit_peer_tls_issuer_dn,
NBDKIT_EXTERN_DECL (void, nbdkit_shutdown, (void));
NBDKIT_EXTERN_DECL (void, nbdkit_disconnect, (int force));
NBDKIT_EXTERN_DECL (const char *, nbdkit_name, (void));
+NBDKIT_EXTERN_DECL (const char *, nbdkit_timestamp, (void));
NBDKIT_EXTERN_DECL (const char *, nbdkit_strdup_intern,
(const char *str)
diff --git a/server/internal.h b/server/internal.h
index f10de3ef..511ec536 100644
--- a/server/internal.h
+++ b/server/internal.h
@@ -633,6 +633,7 @@ extern int threadlocal_get_errno (void);
extern void threadlocal_set_last_error (char *msg);
extern void threadlocal_clear_last_error (void);
extern const char *threadlocal_get_last_error (void);
+extern int threadlocal_set_timestamp (char *timestamp);
extern void *threadlocal_buffer (size_t size);
extern void threadlocal_set_conn (struct connection *conn);
extern struct connection *threadlocal_get_conn (void);
diff --git a/server/nbdkit.syms b/server/nbdkit.syms
index bf6bb378..fb732e9e 100644
--- a/server/nbdkit.syms
+++ b/server/nbdkit.syms
@@ -93,6 +93,7 @@
nbdkit_stdio_safe;
nbdkit_strdup_intern;
nbdkit_strndup_intern;
+ nbdkit_timestamp;
nbdkit_use_default_export;
nbdkit_vdebug;
nbdkit_verror;
diff --git a/server/public.c b/server/public.c
index d08930dd..8d0b69d3 100644
--- a/server/public.c
+++ b/server/public.c
@@ -47,9 +47,11 @@
#include <limits.h>
#include <errno.h>
#include <signal.h>
-#include <sys/types.h>
#include <math.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif
@@ -1226,3 +1228,54 @@ nbdkit_name (void)
{
return process_name;
}
+
+NBDKIT_DLL_PUBLIC const char *
+nbdkit_timestamp (void)
+{
+ char *timestamp = NULL;
+ const size_t len = 64;
+
+ timestamp = malloc (len);
+ if (timestamp == NULL)
+ goto err;
+
+#ifndef WIN32
+ struct timeval tv;
+ struct tm tm;
+ size_t n;
+
+ if (gettimeofday (&tv, NULL) == -1)
+ goto err;
+ gmtime_r (&tv.tv_sec, &tm);
+
+ n = strftime (timestamp, len, "%F %T", &tm);
+ if (n == 0)
+ goto err;
+ snprintf (timestamp + n, len - n, ".%06" PRIu64, (uint64_t) tv.tv_usec);
+#else /* WIN32 */
+ /* Windows doesn't have thread-safe gmtime, or the strftime %F and
+ * %T formatters. Let's try to do this in a Windows native way.
+ * Windows SYSTEMTIME only returns milliseconds.
+ */
+ SYSTEMTIME st;
+ GetSystemTime (&st);
+ snprintf (timestamp, len, "%04d-%02d-%02d %02d:%02d:%02d.%03d000",
+ st.wYear, st.wMonth, st.wDay,
+ st.wHour, st.wMinute, st.wSecond,
+ st.wMilliseconds);
+#endif /* WIN32 */
+
+ /* Store the timestamp in thread-local storage. This passes
+ * ownership to threadlocal which will free it either on the next
+ * call or if the thread exits. This can fail (although it would be
+ * extremely unusual), and in that case we have to return something.
+ */
+ if (threadlocal_set_timestamp (timestamp) == -1)
+ goto err;
+
+ return timestamp;
+
+ err:
+ free (timestamp);
+ return "!";
+}
diff --git a/server/test-public.c b/server/test-public.c
index 3ef530ff..0edd3f49 100644
--- a/server/test-public.c
+++ b/server/test-public.c
@@ -92,6 +92,12 @@ threadlocal_get_context (void)
abort ();
}
+int
+threadlocal_set_timestamp (char *timestamp)
+{
+ abort ();
+}
+
conn_status
connection_get_status (void)
{
diff --git a/server/threadlocal.c b/server/threadlocal.c
index 74a3c4e5..108b9793 100644
--- a/server/threadlocal.c
+++ b/server/threadlocal.c
@@ -57,6 +57,7 @@ struct threadlocal {
size_t instance_num; /* Can be 0. */
int err;
char *last_error; /* Can be NULL. */
+ char *timestamp; /* Can be NULL. */
void *buffer; /* Can be NULL. */
size_t buffer_size;
struct connection *conn; /* Can be NULL. */
@@ -72,6 +73,7 @@ free_threadlocal (void *threadlocalv)
free (threadlocal->name);
free (threadlocal->last_error);
+ free (threadlocal->timestamp);
free (threadlocal->buffer);
free (threadlocal);
}
@@ -216,6 +218,24 @@ threadlocal_get_last_error (void)
return threadlocal ? threadlocal->last_error : NULL;
}
+/* Set the timestamp. Ownership is passed to TLS. This is used by
+ * nbdkit_timestamp(). Calling this frees any existing timestamp in
+ * the thread.
+ */
+int
+threadlocal_set_timestamp (char *timestamp)
+{
+ struct threadlocal *threadlocal = pthread_getspecific (threadlocal_key);
+
+ if (threadlocal) {
+ free (threadlocal->timestamp);
+ threadlocal->timestamp = timestamp;
+ return 0;
+ }
+ else
+ return -1;
+}
+
/* Return the single pread/pwrite buffer for this thread. The buffer
* size is increased to size bytes if required.
*
diff --git a/tests/Makefile.am b/tests/Makefile.am
index f0665926..8b7cb851 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -273,6 +273,7 @@ TESTS += \
test-name.sh \
test-no-name.sh \
test-name-plugin.sh \
+ test-timestamp.sh \
test-dump-plugin-example1.sh \
test-dump-plugin.sh \
test-dump-plugin-example2.sh \
@@ -413,6 +414,7 @@ EXTRA_DIST += \
test-timeout.sh \
test-timeout.py \
test-timeout-cancel.sh \
+ test-timestamp.sh \
test-tls-priority.sh \
test-tls-psk.sh \
test-tls.sh \
@@ -603,6 +605,29 @@ test_name_plugin_la_LDFLAGS = \
$(NULL)
test_name_plugin_la_LIBADD = $(IMPORT_LIBRARY_ON_WINDOWS)
+# check_LTLIBRARIES won't build a shared library (see automake manual).
+# So we have to do this and add a dependency.
+noinst_LTLIBRARIES += \
+ test-timestamp-plugin.la \
+ $(NULL)
+test-timestamp.sh: test-timestamp-plugin.la
+
+test_timestamp_plugin_la_SOURCES = \
+ test-timestamp-plugin.c \
+ $(top_srcdir)/include/nbdkit-plugin.h \
+ $(NULL)
+test_timestamp_plugin_la_CPPFLAGS = \
+ -I$(top_srcdir)/include \
+ -I$(top_builddir)/include \
+ $(NULL)
+test_timestamp_plugin_la_CFLAGS = $(WARNINGS_CFLAGS)
+# For use of the -rpath option, see:
+# https://lists.gnu.org/archive/html/libtool/2007-07/msg00067.html
+test_timestamp_plugin_la_LDFLAGS = \
+ -module -avoid-version -shared $(NO_UNDEFINED_ON_WINDOWS) -rpath /nowhere \
+ $(NULL)
+test_timestamp_plugin_la_LIBADD = $(IMPORT_LIBRARY_ON_WINDOWS)
+
endif HAVE_PLUGINS
# Test the header files can be included on their own.
diff --git a/tests/test-timestamp-plugin.c b/tests/test-timestamp-plugin.c
new file mode 100644
index 00000000..f7efe270
--- /dev/null
+++ b/tests/test-timestamp-plugin.c
@@ -0,0 +1,81 @@
+/* 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.
+ */
+
+/* This plugin tests nbdkit_timestamp().
+ *
+ * The corresponding test is 'test-timestamp.sh'.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define NBDKIT_API_VERSION 2
+#include <nbdkit-plugin.h>
+
+static void *
+timestamp_open (int readonly)
+{
+ nbdkit_debug ("%s: timestamped open", nbdkit_timestamp ());
+ return NBDKIT_HANDLE_NOT_NEEDED;
+}
+
+static int64_t
+timestamp_get_size (void *handle)
+{
+ return 1024*1024*1024;
+}
+
+#define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
+
+static int
+timestamp_pread (void *handle, void *buf, uint32_t count, uint64_t offset,
+ uint32_t flags)
+{
+ const char *ts = nbdkit_timestamp ();
+ memset (buf, 0, count);
+ nbdkit_debug ("%s: timestamped memset", ts);
+
+ return 0;
+}
+
+static struct nbdkit_plugin plugin = {
+ .name = "timestamp",
+ .version = PACKAGE_VERSION,
+ .open = timestamp_open,
+ .get_size = timestamp_get_size,
+ .pread = timestamp_pread,
+};
+
+NBDKIT_REGISTER_PLUGIN (plugin)
diff --git a/tests/test-timestamp.sh b/tests/test-timestamp.sh
new file mode 100755
index 00000000..5479fd37
--- /dev/null
+++ b/tests/test-timestamp.sh
@@ -0,0 +1,61 @@
+#!/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 the nbdkit_timestamp() API.
+
+source ./functions.sh
+set -e
+set -x
+set -u
+
+requires_run
+requires_nbdcopy
+
+plugin=.libs/test-timestamp-plugin.$SOEXT
+requires test -f $plugin
+
+out=timestamp.out
+cleanup_fn rm -f "$out"
+rm -f "$out"
+
+# Print the current date for comparison when checking the log.
+date
+
+# The plugin is large (but empty). Run nbdcopy which will invoke
+# the .pread method in the plugin in parallel.
+nbdkit -fv "$plugin" --run 'nbdcopy "$uri" null:' 2> $out
+cat $out
+
+# We are mainly interested that (a) the plugin nor nbdkit crash and
+# (b) that valgrind checks pass. So here we only need to check that
+# some timestamps were generated in the log.
+grep 'debug: [[:digit:]]\{4\}-[[:digit:]]\{2\}-[[:digit:]]\{2\} [[:digit:]]\{2\}:[[:digit:]]\{2\}:[[:digit:]]\{2\}\.[[:digit:]]\{6\}: timestamped' $out
--
2.47.3

View File

@ -1,30 +0,0 @@
From cb6383e611a070e180200e05980e5efd4f72ec3b Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Mon, 12 Jan 2026 19:54:31 +0000
Subject: [PATCH] vddk: Test with VDDK 9.0.1.0
There are almost no changes in this version compared to 9.0.0.0.
Reported-by: Ajay Victor
Fixes: https://issues.redhat.com/browse/RHEL-140615
(cherry picked from commit e43f9aab6bba2d31dd365f14fddc653926c662d7)
---
plugins/vddk/nbdkit-vddk-plugin.pod | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/plugins/vddk/nbdkit-vddk-plugin.pod b/plugins/vddk/nbdkit-vddk-plugin.pod
index 4685ba6b..2d34fe14 100644
--- a/plugins/vddk/nbdkit-vddk-plugin.pod
+++ b/plugins/vddk/nbdkit-vddk-plugin.pod
@@ -439,7 +439,7 @@ This is also the first version that supported the
C<VixDiskLib_QueryAllocatedBlocks> API. This is used to provide
sparseness (extent) information over NBD.
-=item VDDK 9.0.0.0 (released Jun 2025)
+=item VDDK 9.0.1.0 (released Sep 2025)
This is the latest version of VDDK that we have tested at the time of
writing, but the plugin should work with future versions.
--
2.47.3

View File

@ -1,30 +0,0 @@
From 70346d26c037809c1cf1ac35c45ac95d0840389f Mon Sep 17 00:00:00 2001
From: Christopher Byrne <salah.coronya@gmail.com>
Date: Wed, 14 Jan 2026 18:50:03 -0600
Subject: [PATCH] configure.ac: Remove use of "which" command
It's not a required command, use "command -v" which is POSIX shell
compliant. See https://bugs.gentoo.org/646588 .
Signed-off-by: Christopher Byrne <salah.coronya@gmail.com>
(cherry picked from commit 59e0f6d4537b67c37d0f9fc7c8d819208ce72929)
---
configure.ac | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/configure.ac b/configure.ac
index 64aa44a5..7a19fb95 100644
--- a/configure.ac
+++ b/configure.ac
@@ -205,7 +205,7 @@ AC_PROG_RANLIB
dnl Bash must be at least version 4. If it is too old, fail hard
dnl with a good diagnostic. Note macOS ships an ancient version
dnl of bash (https://gitlab.com/nbdkit/nbdkit/-/issues/21)
-bash=`which bash`
+bash=$(command -v bash)
AC_MSG_CHECKING([for the major version of $bash])
bash_major=`bash -c 'echo ${BASH_VERSINFO:-0}'`
AC_MSG_RESULT([$bash_major])
--
2.47.3

View File

@ -0,0 +1,45 @@
From f1c763c8e15e23be656836e4b8ed740434776c3e Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Thu, 29 Jan 2026 11:14:49 +0000
Subject: [PATCH] log: Use nbdkit_timestamp()
After adding the new public nbdkit_timestamp function we can replace
the open-coded timestamp generation in the log filter. Note the
format is identical (since the nbdkit_timestamp code was copied from
here).
(cherry picked from commit 2942d8381dcbe573093e819360b5b1d5d38b80b7)
---
filters/log/output.c | 16 +---------------
1 file changed, 1 insertion(+), 15 deletions(-)
diff --git a/filters/log/output.c b/filters/log/output.c
index 4a116046..72a0ba3c 100644
--- a/filters/log/output.c
+++ b/filters/log/output.c
@@ -60,21 +60,7 @@ static void
to_file (struct handle *h, log_id_t id, const char *act, enum type type,
const char *fmt, va_list args)
{
- struct timeval tv;
- struct tm tm;
- char timestamp[27] = "Time unknown";
-
- /* Logging is best effort, so ignore failure to get timestamp */
- if (!gettimeofday (&tv, NULL)) {
- size_t s;
-
- gmtime_r (&tv.tv_sec, &tm);
- s = strftime (timestamp, sizeof timestamp - sizeof ".000000" + 1,
- "%F %T", &tm);
- assert (s);
- snprintf (timestamp + s, sizeof timestamp - s, ".%06ld",
- 0L + tv.tv_usec);
- }
+ const char *timestamp = nbdkit_timestamp ();
#ifdef HAVE_FLOCKFILE
flockfile (logfile);
--
2.47.3

View File

@ -0,0 +1,29 @@
From 69e58c8e3f98c845cc13884d3ce4b6e768d2afb7 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Thu, 29 Jan 2026 15:18:12 +0000
Subject: [PATCH] docs/nbdkit-plugin.pod: Add a link to nbdkit_timestamp(3)
The plugin page should link to every API page, and I forgot to add a
new link to nbdkit_timestamp(3).
Fixes: commit 773d958c8f2a5043053d385b8bfe287c43ee63d4
(cherry picked from commit a1a659645fa39cdba0996cb57673033be1a17385)
---
docs/nbdkit-plugin.pod | 1 +
1 file changed, 1 insertion(+)
diff --git a/docs/nbdkit-plugin.pod b/docs/nbdkit-plugin.pod
index 41f65908..3a5dc4a7 100644
--- a/docs/nbdkit-plugin.pod
+++ b/docs/nbdkit-plugin.pod
@@ -1661,6 +1661,7 @@ L<nbdkit_shutdown(3)>,
L<nbdkit_stdio_safe(3)>,
L<nbdkit_strdup_intern(3)>,
L<nbdkit_strndup_intern(3)>,
+L<nbdkit_timestamp(3)>,
L<nbdkit_vdebug(3)>,
L<nbdkit_verror(3)>,
L<nbdkit_vprintf_intern(3)>.
--
2.47.3

View File

@ -1,40 +0,0 @@
From 0282cc8d29c789a0872eaa7f364fa948167cfb8f Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Sat, 17 Jan 2026 20:57:22 +0000
Subject: [PATCH] map: Fix documentation about changing the size of the disk
Since map-size was implemented in commit 058710d26c ("map: Implement
map-size feature") this filter can in fact change the size of the
disk. However using nbdkit-truncate-filter is still better.
Fixes: commit 058710d26c94904535135a9b85f788cd9bcc0477
(cherry picked from commit 84edd687b0080e693aafc97592efb18947b7a8ab)
---
filters/map/nbdkit-map-filter.pod | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/filters/map/nbdkit-map-filter.pod b/filters/map/nbdkit-map-filter.pod
index 8fce70ce..92e4a92f 100644
--- a/filters/map/nbdkit-map-filter.pod
+++ b/filters/map/nbdkit-map-filter.pod
@@ -10,11 +10,12 @@ nbdkit-map-filter - remap disk blocks
=head1 DESCRIPTION
C<nbdkit-map-filter> is an L<nbdkit(1)> filter which can remap parts
-of the underlying plugin, such as moving a sector or partition. To
-select part of a disk, use L<nbdkit-offset-filter(1)> instead. To
-select a partition, use L<nbdkit-partition-filter(1)>. This filter
-cannot change the size of the disk, use L<nbdkit-truncate-filter(1)>
-to do that.
+of the underlying plugin, such as moving a sector or partition.
+
+To select part of a disk, use L<nbdkit-offset-filter(1)> instead. To
+select a partition, use L<nbdkit-partition-filter(1)>. Although this
+filter can change the size of the disk, L<nbdkit-truncate-filter(1)>
+is easier to use.
=head1 EXAMPLES
--
2.47.3

View File

@ -1,40 +0,0 @@
From b3502c4e9294ba5b760ca54312b1cc73eab1883d Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Sun, 18 Jan 2026 18:33:38 +0000
Subject: [PATCH] common/utils/utils.h: Add C++ boilerplate
Allow these utility functions to be called from internal plugins
written in C++.
(cherry picked from commit 3db92f66452525309422329304f25f61fa807709)
---
common/utils/utils.h | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/common/utils/utils.h b/common/utils/utils.h
index 20cf0abc..3f9e4512 100644
--- a/common/utils/utils.h
+++ b/common/utils/utils.h
@@ -33,6 +33,10 @@
#ifndef NBDKIT_UTILS_H
#define NBDKIT_UTILS_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include <stdbool.h>
extern void shell_quote (const char *str, FILE *fp);
@@ -54,4 +58,8 @@ extern ssize_t full_pwrite (int fd, const void *buf, size_t count,
extern int64_t device_size (int fd, const struct stat *statbuf);
#endif
+#ifdef __cplusplus
+}
+#endif
+
#endif /* NBDKIT_UTILS_H */
--
2.47.3

View File

@ -0,0 +1,28 @@
From 90547ea1ee742cf79a46dc0166f5d642df58b480 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Thu, 29 Jan 2026 19:27:07 +0000
Subject: [PATCH] docs/nbdkit_timestamp.pod: Fix copy and paste error in man
page
Fixes: commit 773d958c8f2a5043053d385b8bfe287c43ee63d4
(cherry picked from commit 0aea0a18f4665a885b77e1c1461c2aba05a0c604)
---
docs/nbdkit_timestamp.pod | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/nbdkit_timestamp.pod b/docs/nbdkit_timestamp.pod
index 2662b204..2b6859cf 100644
--- a/docs/nbdkit_timestamp.pod
+++ b/docs/nbdkit_timestamp.pod
@@ -56,7 +56,7 @@ an error generating the timestamp, the string C<"!"> is returned.
=head1 HISTORY
-C<nbdkit_name> was added in nbdkit 1.48.
+C<nbdkit_timestamp> was added in nbdkit 1.48.
=head1 SEE ALSO
--
2.47.3

View File

@ -0,0 +1,33 @@
From 996bb5a3acbf4478d4d568ddfdf32aa45fffb633 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Sat, 31 Jan 2026 08:41:56 +0000
Subject: [PATCH] todo: Add item about nbdkit_timestamp on the main thread
Updates: commit 773d958c8f2a5043053d385b8bfe287c43ee63d4
(cherry picked from commit e3d6ffc314b9774af31235c3d7ae760f62978861)
---
TODO.md | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/TODO.md b/TODO.md
index 8488dd95..46e2a0c6 100644
--- a/TODO.md
+++ b/TODO.md
@@ -87,6 +87,14 @@
`-4` or `-6` option). Once this mess is fixed, the tests should be
updated to use this.
+* `nbdkit_timestamp()` does not work when called on the main thread.
+ This is because the main thread does not allocate thread-local
+ storage (see [threadlocal.c](server/threadlocal.c)). We could
+ either special-case this in `nbdkit_timestamp` or we could implement
+ thread-local storage for the main thread (but the latter may change
+ some assumptions in the code). There may be other APIs that are
+ affected in this way too.
+
## Suggestions for plugins
Note: qemu supports other formats such as iscsi and ceph/rbd, and
--
2.47.3

View File

@ -0,0 +1,121 @@
From 02626a83a89068944e8840fef9e6deada9ae67b1 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Sat, 31 Jan 2026 13:41:36 +0000
Subject: [PATCH] server/sockets.c: Print the actual bound addresses and ports
in debug output
Previously when listening on a TCP/IP port, we printed (in debug
output) this:
nbdkit: debug: bound to IP address <any>:10809 (2 socket(s))
Change this to print the actual socket addresses for each socket:
nbdkit: debug: bound to IPv4 address 0.0.0.0:10809
nbdkit: debug: bound to IPv6 address [::]:10809
We just print the raw IP addresses to avoid doing name lookups. This
is still way harder than it should be.
(cherry picked from commit 6ef03ffdd451b730ac90d889e758e9151b14db82)
---
configure.ac | 7 ++++++
server/sockets.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 62 insertions(+), 2 deletions(-)
diff --git a/configure.ac b/configure.ac
index a7a021e0..1cc87a44 100644
--- a/configure.ac
+++ b/configure.ac
@@ -509,6 +509,13 @@ AC_CHECK_MEMBERS([struct sockpeercred.uid], [], [],
#endif
]])
+dnl Check for types.
+AC_CHECK_TYPES([in_port_t], [], [], [[
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+]])
+
dnl Replacement functions that we provide for some platforms.
AC_CONFIG_LIBOBJ_DIR([common/replacements])
AC_REPLACE_FUNCS([\
diff --git a/server/sockets.c b/server/sockets.c
index 348da029..cef27d57 100644
--- a/server/sockets.c
+++ b/server/sockets.c
@@ -46,6 +46,10 @@
#include <sys/socket.h>
#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
@@ -271,8 +275,57 @@ bind_tcpip_socket (sockets *socks)
exit (EXIT_FAILURE);
}
- debug ("bound to IP address %s:%s (%zu socket(s))",
- ipaddr ? ipaddr : "<any>", ipport, socks->len);
+ /* Print the IP address of each socket in debug output. */
+ if (verbose) {
+ char addr_str[128];
+ size_t i;
+ struct sockaddr_storage ss;
+ socklen_t sslen = sizeof ss;
+ const struct sockaddr_in *sin;
+ const struct sockaddr_in6 *sin6;
+ const void *addr;
+ const char *familyname;
+#ifdef HAVE_IN_PORT_T
+ in_port_t portno;
+#else
+ int portno;
+#endif
+ bool square_brackets = false;
+
+ for (i = 0; i < socks->len; ++i) {
+ if (getsockname (socks->ptr[i], (struct sockaddr *) &ss, &sslen) == -1) {
+ debug ("getsockname: %m");
+ continue;
+ }
+ switch (ss.ss_family) {
+ case AF_INET:
+ familyname = "IPv4";
+ sin = (const struct sockaddr_in *) &ss;
+ addr = &sin->sin_addr;
+ portno = sin->sin_port;
+ break;
+ case AF_INET6:
+ familyname = "IPv6";
+ square_brackets = true;
+ sin6 = (const struct sockaddr_in6 *) &ss;
+ addr = &sin6->sin6_addr;
+ portno = sin6->sin6_port;
+ break;
+ default:
+ /* Probably can never happen? */
+ debug ("unknown address family: %d", ss.ss_family);
+ continue;
+ }
+ if (inet_ntop (ss.ss_family, addr, addr_str, sizeof addr_str) == NULL) {
+ debug ("inet_ntop: %m");
+ continue;
+ }
+ debug ("bound to %s address %s%s%s:%d",
+ familyname,
+ square_brackets ? "[" : "", addr_str, square_brackets ? "]" : "",
+ (int) ntohs (portno));
+ }
+ }
}
void
--
2.47.3

View File

@ -0,0 +1,214 @@
From 410ee57d3708133527769e8b7eeddd5840226cb5 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Sat, 31 Jan 2026 14:16:30 +0000
Subject: [PATCH] server: Partially fix --port=0
Using nbdkit --port=0 causes nbdkit to open a TCP/IP socket with the
kernel choosing a random port number (usually two port numbers, one
for IPv4 and IPv6). This already worked, mostly, but it didn't update
the port variable and so $port was not set correctly in --run scripts,
making this option useless for our testing.
Also it didn't, and still does not, set the $uri or the output of
--print-uri correctly. The URI still has ":0" as the port number.
This is more difficult to fix, see my update to the TODO.md file.
(cherry picked from commit 8251486ec7d899628de246bc3232ddb20dceaa53)
---
TODO.md | 15 +++++-------
docs/nbdkit.pod | 12 ++++++++++
server/sockets.c | 47 ++++++++++++++++++++++++++++++++++++++
tests/Makefile.am | 2 ++
tests/test-ip-port.sh | 53 +++++++++++++++++++++++++++++++++++++++++++
5 files changed, 120 insertions(+), 9 deletions(-)
create mode 100755 tests/test-ip-port.sh
diff --git a/TODO.md b/TODO.md
index 46e2a0c6..af272405 100644
--- a/TODO.md
+++ b/TODO.md
@@ -77,15 +77,12 @@
particular `SOL_TCP` + `TCP_KEEPCNT`, `SOL_TCP` + `TCP_KEEPIDLE`,
and `SOL_TCP` + `TCP_KEEPINTVL`.
-* Fix `--port=0` / allow nbdkit to choose a TCP port: Several tests
- rely on picking a random TCP port, which is racy. The kernel can
- pick a port for us, and nbdkit `--print-uri` function can be used to
- display the random port to the user. Because of a bug, nbdkit lets
- you choose `--port=0`, causing the kernel to pick a port, but
- `--print-uri` doesn't display the port, and a different port is
- picked for IPv4 and IPv6 (so it only makes sense to use this with
- `-4` or `-6` option). Once this mess is fixed, the tests should be
- updated to use this.
+* Using `--port=0` (to get the kernel to pick a random port) works,
+ but the URI in `--print-uri` and `$uri` is wrong. Fixing this is
+ difficult because we generate and print the URI early, long before
+ we bind to ports. There is a case for binding to ports before
+ closing stdio, but that is quite a large change and needs some
+ careful thought.
* `nbdkit_timestamp()` does not work when called on the main thread.
This is because the main thread does not allocate thread-local
diff --git a/docs/nbdkit.pod b/docs/nbdkit.pod
index 3104d751..ac8a39b3 100644
--- a/docs/nbdkit.pod
+++ b/docs/nbdkit.pod
@@ -422,6 +422,18 @@ delete the file when it exits.
Change the TCP/IP port number on which nbdkit serves requests.
The default is C<10809>. See also I<-i>.
+=item B<-p 0>
+
+=item B<--port=0>
+
+Setting port to C<0> causes nbdkit to bind to a random free port
+number. On a dual-stack host it will usually bind to a I<different>
+localhost port number for IPv4 and IPv6.
+
+Currently I<--print-uri> will still print the port as C<":0"> (this is
+a bug), but the true port is available using the C<$port> variable in
+I<--run> scripts.
+
=item B<--print-uri>
Print the URI. See L</NBD URIs and endpoints> above.
diff --git a/server/sockets.c b/server/sockets.c
index cef27d57..dad7fb3c 100644
--- a/server/sockets.c
+++ b/server/sockets.c
@@ -326,6 +326,53 @@ bind_tcpip_socket (sockets *socks)
(int) ntohs (portno));
}
}
+
+ /* If port == "0" then we let the kernel choose the port number.
+ * (In the normal dual-stack case, it will actually choose two port
+ * numbers, one for IPv4 and one for IPv6). In this case we can
+ * overwrite the port variable with the chosen port number,
+ * preferring IPv6.
+ *
+ * In theory this loop could be combined with the one above but it
+ * makes the code very intricate.
+ */
+ if (strcmp (port, "0") == 0) {
+ static char port_str[16] = { 0 };
+ size_t i;
+ struct sockaddr_storage ss;
+ socklen_t sslen = sizeof ss;
+ const struct sockaddr_in *sin;
+ const struct sockaddr_in6 *sin6;
+
+ for (i = 0; i < socks->len; ++i) {
+ if (getsockname (socks->ptr[i], (struct sockaddr *) &ss, &sslen) == -1) {
+ debug ("getsockname: %m");
+ continue;
+ }
+ switch (ss.ss_family) {
+ case AF_INET:
+ sin = (const struct sockaddr_in *) &ss;
+ /* For IPv4, don't overwrite if the port_str is already set,
+ * so IPv6 takes priority.
+ */
+ if (port_str[0] == 0)
+ snprintf (port_str, sizeof port_str, "%d",
+ (int) ntohs (sin->sin_port));
+ break;
+ case AF_INET6:
+ sin6 = (const struct sockaddr_in6 *) &ss;
+ /* Prefer IPv6, so always override the port_str here. */
+ snprintf (port_str, sizeof port_str, "%d",
+ (int) ntohs (sin6->sin6_port));
+ break;
+ default:
+ abort ();
+ }
+ }
+
+ if (port_str[0] != 0)
+ port = port_str;
+ }
}
void
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 8b7cb851..e2bc640d 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -302,6 +302,7 @@ TESTS += \
test-tls-priority.sh \
test-tls-psk.sh \
test-not-linked-to-libssl.sh \
+ test-ip-port.sh \
test-ipv4-lo.sh \
test-ipv6-lo.sh \
test-foreground.sh \
@@ -379,6 +380,7 @@ EXTRA_DIST += \
test-foreground.sh \
test-help-example1.sh \
test-help-plugin.sh \
+ test-ip-port.sh \
test-ipv4-lo.sh \
test-ipv6-lo.sh \
test-keepalive.sh \
diff --git a/tests/test-ip-port.sh b/tests/test-ip-port.sh
new file mode 100755
index 00000000..cc1aa32e
--- /dev/null
+++ b/tests/test-ip-port.sh
@@ -0,0 +1,53 @@
+#!/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 that --run '$port' has the correct (non-zero) port number when
+# we use --port=0.
+
+source ./functions.sh
+set -e
+set -x
+set -u
+
+requires_run
+requires_nbdinfo
+
+define script <<'EOF'
+echo port = $port
+test "$port" -gt "0"
+
+# nbdkit does not yet construct $uri correctly when using --port=0
+# but it does set $port so this will work:
+nbdinfo nbd://localhost:$port
+EOF
+
+nbdkit -v --port=0 null --run "$script"
--
2.47.3

View File

@ -0,0 +1,104 @@
From 05b8884ccab68a224fe4f240557082fb575bc407 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Sat, 31 Jan 2026 14:34:39 +0000
Subject: [PATCH] tests/test-ip-filter.sh: Remove use of pick_unused_port
Since nbdkit --port=0 is working somewhat now, we can remove use of
the racy pick_unused_port function.
We can now improve this test to check IPv6 only filtering.
(cherry picked from commit 1e1ba9b58a5988af746de0b3c0472e12462b8eb0)
---
TODO.md | 16 ++++++++++------
tests/test-ip-filter.sh | 40 ++++++++++++++++++++++++++++++----------
2 files changed, 40 insertions(+), 16 deletions(-)
diff --git a/TODO.md b/TODO.md
index af272405..ecb36f29 100644
--- a/TODO.md
+++ b/TODO.md
@@ -77,12 +77,16 @@
particular `SOL_TCP` + `TCP_KEEPCNT`, `SOL_TCP` + `TCP_KEEPIDLE`,
and `SOL_TCP` + `TCP_KEEPINTVL`.
-* Using `--port=0` (to get the kernel to pick a random port) works,
- but the URI in `--print-uri` and `$uri` is wrong. Fixing this is
- difficult because we generate and print the URI early, long before
- we bind to ports. There is a case for binding to ports before
- closing stdio, but that is quite a large change and needs some
- careful thought.
+* Using `--port=0` (to get the kernel to pick a random port) works:
+
+ - But the URI in `--print-uri` and `$uri` is wrong. Fixing this is
+ difficult because we generate and print the URI early, long before
+ we bind to ports. There is a case for binding to ports before
+ closing stdio, but that is quite a large change and needs some
+ careful thought.
+
+ - Consider exposing `$port4` and `$port6` to `--run` scripts. This
+ would allow `tests/test-ip-filter.sh` to work.
* `nbdkit_timestamp()` does not work when called on the main thread.
This is because the main thread does not allocate thread-local
diff --git a/tests/test-ip-filter.sh b/tests/test-ip-filter.sh
index cb7d6315..c8f7b051 100755
--- a/tests/test-ip-filter.sh
+++ b/tests/test-ip-filter.sh
@@ -38,7 +38,7 @@ set -e
set -x
set -u
-requires ip -V
+requires_run
requires_nbdinfo
requires_ipv6_loopback
@@ -46,15 +46,35 @@ requires_ipv6_loopback
# "nbd://[::1]:$port" URIs (commit 17df436cea5 added in 1.7.7).
requires_libnbd_version 1.8
-rm -f ip-filter.pid
-cleanup_fn rm -f ip-filter.pid
+fail=0
-# Find an unused port to listen on.
-pick_unused_port
+# Allow IPv4 and IPv6.
+nbdkit -v --port=0 --filter=ip null \
+ -D ip.rules=1 \
+ allow=allipv4,allipv6 deny=all \
+ --run '
+ nbdinfo "nbd://127.0.0.1:$port"
+ nbdinfo "nbd://[::1]:$port"
+'
-start_nbdkit -P ip-filter.pid -p $port --filter=ip null \
- -D ip.rules=1 \
- allow=allipv4,allipv6 deny=all
+# Allow IPv4 only, deny IPv6.
+# XXX We cannot test this because $port is set preferentially to the
+# IPv6 port, and we cannot find the IPv4 port here.
+#nbdkit -v --port=0 --filter=ip null \
+# -D ip.rules=1 \
+# allow=allipv4 deny=all --run 'nbdinfo "nbd://127.0.0.1:$port"'
+#
+#nbdkit -v --port=0 --filter=ip null \
+# -D ip.rules=1 \
+# allow=allipv4 deny=all --run 'nbdinfo "nbd://[::1]:$port"' && fail=1
-nbdinfo "nbd://127.0.0.1:$port"
-nbdinfo "nbd://[::1]:$port"
+# Allow IPv6 only, deny IPv4.
+nbdkit -v --port=0 --filter=ip null \
+ -D ip.rules=1 \
+ allow=allipv6 deny=all --run 'nbdinfo "nbd://[::1]:$port"'
+
+nbdkit -v --port=0 --filter=ip null \
+ -D ip.rules=1 \
+ allow=allipv6 deny=all --run 'nbdinfo "nbd://127.0.0.1:$port"' && fail=1
+
+exit $fail
--
2.47.3

View File

@ -0,0 +1,109 @@
From 2cb53b22419fa33f7a85efda0c940116f5c60ea5 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Sat, 31 Jan 2026 15:31:59 +0000
Subject: [PATCH] tests/test-ipv4-lo.sh, tests/test-ipv6-lo.sh: Remove
pick_unused_port
Remove use of pick_unused_port, and greatly simply these tests by
using --run and nbdinfo.
(cherry picked from commit e1ee17022c63d90dd7469c66ca4980e489fa3f95)
---
tests/test-ipv4-lo.sh | 35 ++++-------------------------------
tests/test-ipv6-lo.sh | 35 ++++-------------------------------
2 files changed, 8 insertions(+), 62 deletions(-)
diff --git a/tests/test-ipv4-lo.sh b/tests/test-ipv4-lo.sh
index e0e1878a..9b6e5c42 100755
--- a/tests/test-ipv4-lo.sh
+++ b/tests/test-ipv4-lo.sh
@@ -38,36 +38,9 @@ set -e
set -x
set -u
-# Cannot use kill pidfile below to test if the process is running on
-# Windows.
-if is_windows; then
- echo "$0: this test needs to be revised to work on Windows"
- exit 77
-fi
-
-requires ip -V
-requires "$QEMU_IMG" --version
-requires "$QEMU_IMG" info --image-opts driver=file,filename=functions.sh
-
-rm -f ipv4lo.pid ipv4lo.out
-cleanup_fn rm -f ipv4lo.pid ipv4lo.out
-
-# Find an unused port to listen on.
-pick_unused_port
+requires_run
+requires_nbdinfo
# By default nbdkit will listen on all available interfaces, ie.
-# IPv4 and IPv6.
-start_nbdkit -P ipv4lo.pid -p $port example1
-pid="$(cat ipv4lo.pid)"
-
-# Check the process exists.
-kill -s 0 $pid
-
-# Check we can connect over the IPv4 loopback interface.
-ipv4_lo="$(ip -o -4 addr show scope host)"
-if test -n "$ipv4_lo"; then
- "$QEMU_IMG" info --output=json \
- --image-opts "file.driver=nbd,file.host=127.0.0.1,file.port=$port" > ipv4lo.out
- cat ipv4lo.out
- grep -sq '"virtual-size": *104857600\b' ipv4lo.out
-fi
+# IPv4 and IPv6, so use -4 to force IPv4 only.
+nbdkit -v -4 --port=0 example1 --run 'nbdinfo nbd://127.0.0.1:$port/'
diff --git a/tests/test-ipv6-lo.sh b/tests/test-ipv6-lo.sh
index d30ae205..ea17bafc 100755
--- a/tests/test-ipv6-lo.sh
+++ b/tests/test-ipv6-lo.sh
@@ -38,37 +38,10 @@ set -e
set -x
set -u
-# Cannot use kill pidfile below to test if the process is running on
-# Windows.
-if is_windows; then
- echo "$0: this test needs to be revised to work on Windows"
- exit 77
-fi
-
-requires ip -V
-requires "$QEMU_IMG" --version
-requires "$QEMU_IMG" info --image-opts driver=file,filename=functions.sh
+requires_run
+requires_nbdinfo
requires_ipv6_loopback
-rm -f ipv6lo.pid ipv6lo.out
-cleanup_fn rm -f ipv6lo.pid ipv6lo.out
-
-# Find an unused port to listen on.
-pick_unused_port
-
# By default nbdkit will listen on all available interfaces, ie.
-# IPv4 and IPv6.
-start_nbdkit -P ipv6lo.pid -p $port example1
-pid="$(cat ipv6lo.pid)"
-
-# Check the process exists.
-kill -s 0 $pid
-
-# Check we can connect over the IPv6 loopback interface.
-ipv6_lo="$(ip -o -6 addr show scope host)"
-if test -n "$ipv6_lo"; then
- "$QEMU_IMG" info --output=json \
- --image-opts "file.driver=nbd,file.host=::1,file.port=$port" > ipv6lo.out
- cat ipv6lo.out
- grep -sq '"virtual-size": *104857600\b' ipv6lo.out
-fi
+# IPv4 and IPv6, so use -6 to force IPv6 only.
+nbdkit -v -6 --port=0 example1 --run 'nbdinfo nbd://[::1]:$port/'
--
2.47.3

View File

@ -0,0 +1,51 @@
From 53f021813744745f0ec0996f4e16bc71d3484336 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Sun, 1 Feb 2026 13:40:01 +0000
Subject: [PATCH] server/sockets.c: Don't crash if TCP/IP selected without
--port
Because of a thinko in commit 8251486ec7 ("server: Partially fix
--port=0") I neglected the case where we select TCP/IP but don't use
the --port parameter (thus port == NULL), eg:
$ nbdkit file disk.img
Segmentation fault (core dumped) nbdkit file disk.img
Ooops. The fix is to use ioport which is never NULL in this function.
Our tests don't test this case, since we cannot in general open port
10809 safely from the test suite.
Fixes: commit 8251486ec7d899628de246bc3232ddb20dceaa53
(cherry picked from commit 490c1a6980de65bb21f007dddb4a5a7ffa136c66)
---
server/sockets.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/server/sockets.c b/server/sockets.c
index dad7fb3c..473f8ee6 100644
--- a/server/sockets.c
+++ b/server/sockets.c
@@ -327,8 +327,8 @@ bind_tcpip_socket (sockets *socks)
}
}
- /* If port == "0" then we let the kernel choose the port number.
- * (In the normal dual-stack case, it will actually choose two port
+ /* If --port=0 then we let the kernel choose the port number. (In
+ * the normal dual-stack case, it will actually choose two port
* numbers, one for IPv4 and one for IPv6). In this case we can
* overwrite the port variable with the chosen port number,
* preferring IPv6.
@@ -336,7 +336,7 @@ bind_tcpip_socket (sockets *socks)
* In theory this loop could be combined with the one above but it
* makes the code very intricate.
*/
- if (strcmp (port, "0") == 0) {
+ if (strcmp (ipport, "0") == 0) {
static char port_str[16] = { 0 };
size_t i;
struct sockaddr_storage ss;
--
2.47.3

View File

@ -0,0 +1,416 @@
From d48364b33bea7997c38461ba2d85015a8901f2f3 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Sun, 1 Feb 2026 19:01:54 +0000
Subject: [PATCH] sparse-random: Make block size configurable
For trying to reproduce a virt-v2v corruption case, we would like to
use this plugin to simulate quite closely a VDDK source. VDDK uses a
64K block size for extents, which is different from the default (4K)
for this plugin. This change allows us to use 'blocksize=64K' to
simulate this.
(cherry picked from commit 5612598a49aaf4ac49f1b3e096dc4945ea7df640)
---
.../nbdkit-sparse-random-plugin.pod | 11 ++-
plugins/sparse-random/sparse-random.c | 99 +++++++++++--------
tests/Makefile.am | 2 +
tests/test-sparse-random-blocksize.sh | 49 +++++++++
4 files changed, 117 insertions(+), 44 deletions(-)
create mode 100755 tests/test-sparse-random-blocksize.sh
diff --git a/plugins/sparse-random/nbdkit-sparse-random-plugin.pod b/plugins/sparse-random/nbdkit-sparse-random-plugin.pod
index 0d64633f..d0472e00 100644
--- a/plugins/sparse-random/nbdkit-sparse-random-plugin.pod
+++ b/plugins/sparse-random/nbdkit-sparse-random-plugin.pod
@@ -4,8 +4,8 @@ nbdkit-sparse-random-plugin - make sparse random disks
=head1 SYNOPSIS
- nbdkit sparse-random [size=]SIZE [seed=SEED]
- [percent=N] [runlength=N]
+ nbdkit sparse-random [size=]SIZE [blocksize=N]
+ [seed=SEED] [percent=N] [runlength=N]
[random-content=true]
=head1 DESCRIPTION
@@ -52,6 +52,13 @@ See also L<nbdkit-checkwrite-filter(1)>.
=over 4
+=item B<blocksize=>N
+
+Set the block size. This is the granularity that this plugin operates
+at. Sparse extents will be aligned to the block size.
+
+It must be a power of 2 and E<ge> 1024. The default is 4096.
+
=item B<percent=>N
Specify the approximate percentage of the disk which contains random
diff --git a/plugins/sparse-random/sparse-random.c b/plugins/sparse-random/sparse-random.c
index 823f85a1..8d1c2944 100644
--- a/plugins/sparse-random/sparse-random.c
+++ b/plugins/sparse-random/sparse-random.c
@@ -36,6 +36,7 @@
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
+#include <limits.h>
#include <string.h>
#include <errno.h>
#include <time.h>
@@ -46,6 +47,7 @@
#include "bitmap.h"
#include "cleanup.h"
#include "isaligned.h"
+#include "ispowerof2.h"
#include "iszero.h"
#include "minmax.h"
#include "random.h"
@@ -60,7 +62,7 @@ static int random_content; /* false: Repeat same byte true: Random bytes*/
/* We need to store 1 bit per block. Using a 4K block size means we
* need 32M to map each 1T of virtual disk.
*/
-#define BLOCKSIZE 4096
+static unsigned blocksize = 4096;
static struct bitmap bm; /* Bitmap of data blocks. */
@@ -72,8 +74,6 @@ sparse_random_load (void)
* parameter.
*/
seed = time (NULL);
-
- bitmap_init (&bm, BLOCKSIZE, 1 /* bits per block */);
}
static void
@@ -97,6 +97,18 @@ sparse_random_config (const char *key, const char *value)
return -1;
size = r;
}
+ else if (strcmp (key, "blocksize") == 0 ||
+ strcmp (key, "block-size") == 0) {
+ r = nbdkit_parse_size (value);
+ if (r == -1)
+ return -1;
+ if (r < 1024 || r > 0x10000000 || !is_power_of_2 (r)) {
+ nbdkit_error ("block size must be a power of 2, "
+ "and between 1024 and 2^28");
+ return -1;
+ }
+ blocksize = r;
+ }
else if (strcmp (key, "percent") == 0) {
if (sscanf (value, "%lf", &percent) != 1 ||
percent < 0 || percent > 100) {
@@ -129,6 +141,7 @@ sparse_random_config (const char *key, const char *value)
#define sparse_random_config_help \
"size=<SIZE> (required) Size of the backing disk\n" \
+ "blocksize=<SIZE> Set block size (default: 4K)\n" \
"seed=<SEED> Random number generator seed\n" \
"percent=<PERCENT> Percentage of data\n" \
"runlength=<BYTES> Expected average run length of data\n" \
@@ -182,6 +195,8 @@ sparse_random_get_ready (void)
uint64_t data_run_length = 0;
uint64_t avg_data_run_length = 0;
+ bitmap_init (&bm, blocksize, 1 /* bits per block */);
+
if (bitmap_resize (&bm, size) == -1)
return -1;
@@ -196,7 +211,7 @@ sparse_random_get_ready (void)
}
/* Otherwise calculate the probability parameters as above. */
- P_dh = 1. / ((double) runlength / BLOCKSIZE);
+ P_dh = 1. / ((double) runlength / blocksize);
P_hd = (percent / 100.) * P_dh / (1. - (percent / 100.));
nbdkit_debug ("percent requested = %g%%, "
@@ -242,8 +257,8 @@ sparse_random_get_ready (void)
avg_data_run_length = 0;
nbdkit_debug ("percent actual = %g%%, "
"average run length = %" PRIu64,
- 100. * BLOCKSIZE * nr_data_blocks / size,
- avg_data_run_length * BLOCKSIZE);
+ 100. * blocksize * nr_data_blocks / size,
+ avg_data_run_length * blocksize);
return 0;
}
@@ -270,7 +285,7 @@ sparse_random_block_size (void *handle,
uint32_t *maximum)
{
*minimum = 1;
- *preferred = BLOCKSIZE;
+ *preferred = blocksize;
*maximum = 0xffffffff;
return 0;
}
@@ -301,20 +316,20 @@ read_block (uint64_t blknum, uint64_t offset, void *buf)
struct random_state state;
if (bitmap_get_blk (&bm, blknum, 0) == 0) /* hole */
- memset (buf, 0, BLOCKSIZE);
+ memset (buf, 0, blocksize);
else if (!random_content) { /* data when random-content=false */
xsrandom (seed + offset, &state);
s = xrandom (&state);
s &= 255;
if (s == 0) s = 1;
- memset (buf, (int)s, BLOCKSIZE);
+ memset (buf, (int)s, blocksize);
}
else { /* data when random-content=true */
/* This produces repeatable data for the same offset. Note it
* works because we are called on whole blocks only.
*/
xsrandom (seed + offset, &state);
- for (i = 0; i < BLOCKSIZE; ++i) {
+ for (i = 0; i < blocksize; ++i) {
s = xrandom (&state);
s &= 255;
b[i] = s;
@@ -330,20 +345,20 @@ sparse_random_pread (void *handle, void *buf, uint32_t count, uint64_t offset,
CLEANUP_FREE uint8_t *block = NULL;
uint64_t blknum, blkoffs;
- if (!IS_ALIGNED (count | offset, BLOCKSIZE)) {
- block = malloc (BLOCKSIZE);
+ if (!IS_ALIGNED (count | offset, blocksize)) {
+ block = malloc (blocksize);
if (block == NULL) {
nbdkit_error ("malloc: %m");
return -1;
}
}
- blknum = offset / BLOCKSIZE; /* block number */
- blkoffs = offset % BLOCKSIZE; /* offset within the block */
+ blknum = offset / blocksize; /* block number */
+ blkoffs = offset % blocksize; /* offset within the block */
/* Unaligned head */
if (blkoffs) {
- uint64_t n = MIN (BLOCKSIZE - blkoffs, count);
+ uint64_t n = MIN (blocksize - blkoffs, count);
read_block (blknum, offset, block);
memcpy (buf, &block[blkoffs], n);
@@ -355,12 +370,12 @@ sparse_random_pread (void *handle, void *buf, uint32_t count, uint64_t offset,
}
/* Aligned body */
- while (count >= BLOCKSIZE) {
+ while (count >= blocksize) {
read_block (blknum, offset, buf);
- buf += BLOCKSIZE;
- count -= BLOCKSIZE;
- offset += BLOCKSIZE;
+ buf += blocksize;
+ count -= blocksize;
+ offset += blocksize;
blknum++;
}
@@ -386,18 +401,18 @@ sparse_random_pwrite (void *handle, const void *buf,
CLEANUP_FREE uint8_t *block;
uint64_t blknum, blkoffs;
- block = malloc (BLOCKSIZE);
+ block = malloc (blocksize);
if (block == NULL) {
nbdkit_error ("malloc: %m");
return -1;
}
- blknum = offset / BLOCKSIZE; /* block number */
- blkoffs = offset % BLOCKSIZE; /* offset within the block */
+ blknum = offset / blocksize; /* block number */
+ blkoffs = offset % blocksize; /* offset within the block */
/* Unaligned head */
if (blkoffs) {
- uint64_t n = MIN (BLOCKSIZE - blkoffs, count);
+ uint64_t n = MIN (blocksize - blkoffs, count);
read_block (blknum, offset, block);
if (memcmp (buf, &block[blkoffs], n) != 0) {
@@ -414,23 +429,23 @@ sparse_random_pwrite (void *handle, const void *buf,
}
/* Aligned body */
- while (count >= BLOCKSIZE) {
+ while (count >= blocksize) {
/* As an optimization, skip calling read_block if we know this is
* a hole. Call is_zero instead which should be faster.
*/
if (bitmap_get_blk (&bm, blknum, 0) == 0) {
- if (! is_zero (buf, BLOCKSIZE))
+ if (! is_zero (buf, blocksize))
goto unexpected_data;
}
else {
read_block (blknum, offset, block);
- if (memcmp (buf, block, BLOCKSIZE) != 0)
+ if (memcmp (buf, block, blocksize) != 0)
goto unexpected_data;
}
- buf += BLOCKSIZE;
- count -= BLOCKSIZE;
- offset += BLOCKSIZE;
+ buf += blocksize;
+ count -= blocksize;
+ offset += blocksize;
blknum++;
}
@@ -465,12 +480,12 @@ sparse_random_trim_zero (void *handle, uint32_t count, uint64_t offset,
{
uint64_t blknum, blkoffs;
- blknum = offset / BLOCKSIZE; /* block number */
- blkoffs = offset % BLOCKSIZE; /* offset within the block */
+ blknum = offset / blocksize; /* block number */
+ blkoffs = offset % blocksize; /* offset within the block */
/* Unaligned head */
if (blkoffs) {
- uint64_t n = MIN (BLOCKSIZE - blkoffs, count);
+ uint64_t n = MIN (blocksize - blkoffs, count);
if (bitmap_get_blk (&bm, blknum, 0) != 0) {
unexpected_trim:
@@ -485,12 +500,12 @@ sparse_random_trim_zero (void *handle, uint32_t count, uint64_t offset,
}
/* Aligned body */
- while (count >= BLOCKSIZE) {
+ while (count >= blocksize) {
if (bitmap_get_blk (&bm, blknum, 0) != 0)
goto unexpected_trim;
- count -= BLOCKSIZE;
- offset += BLOCKSIZE;
+ count -= blocksize;
+ offset += blocksize;
blknum++;
}
@@ -510,12 +525,12 @@ sparse_random_extents (void *handle, uint32_t count, uint64_t offset,
uint64_t blknum, blkoffs;
uint32_t type;
- blknum = offset / BLOCKSIZE; /* block number */
- blkoffs = offset % BLOCKSIZE; /* offset within the block */
+ blknum = offset / blocksize; /* block number */
+ blkoffs = offset % blocksize; /* offset within the block */
/* Unaligned head */
if (blkoffs) {
- uint64_t n = MIN (BLOCKSIZE - blkoffs, count);
+ uint64_t n = MIN (blocksize - blkoffs, count);
if (bitmap_get_blk (&bm, blknum, 0) == 0)
type = NBDKIT_EXTENT_HOLE | NBDKIT_EXTENT_ZERO;
@@ -530,16 +545,16 @@ sparse_random_extents (void *handle, uint32_t count, uint64_t offset,
}
/* Aligned body */
- while (count >= BLOCKSIZE) {
+ while (count >= blocksize) {
if (bitmap_get_blk (&bm, blknum, 0) == 0)
type = NBDKIT_EXTENT_HOLE | NBDKIT_EXTENT_ZERO;
else
type = 0; /* data */
- if (nbdkit_add_extent (extents, offset, BLOCKSIZE, type) == -1)
+ if (nbdkit_add_extent (extents, offset, blocksize, type) == -1)
return -1;
- count -= BLOCKSIZE;
- offset += BLOCKSIZE;
+ count -= blocksize;
+ offset += blocksize;
blknum++;
}
diff --git a/tests/Makefile.am b/tests/Makefile.am
index e2bc640d..2ae0c3c2 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1293,10 +1293,12 @@ EXTRA_DIST += \
# sparse-random plugin test.
TESTS += \
+ test-sparse-random-blocksize.sh \
test-sparse-random-copy.sh \
test-sparse-random-info.sh \
$(NULL)
EXTRA_DIST += \
+ test-sparse-random-blocksize.sh \
test-sparse-random-copy.sh \
test-sparse-random-info.sh \
$(NULL)
diff --git a/tests/test-sparse-random-blocksize.sh b/tests/test-sparse-random-blocksize.sh
new file mode 100755
index 00000000..c1230809
--- /dev/null
+++ b/tests/test-sparse-random-blocksize.sh
@@ -0,0 +1,49 @@
+#!/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 the sparse-random plugin with non-standard blocksize.
+
+source ./functions.sh
+set -e
+set -x
+set -u
+
+requires_run
+
+# nbdcopy >= 1.5.9 required for this test.
+requires_nbdcopy
+requires_libnbd_version 1.5.9
+
+nbdkit -v \
+ sparse-random \
+ size=10G blocksize=64K \
+ --run 'nbdcopy "$uri" "$uri"'
--
2.47.3

View File

@ -0,0 +1,39 @@
From bf567e9488c0241cd58c13ef998b208a7f8a58f3 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Sun, 1 Feb 2026 21:21:59 +0000
Subject: [PATCH] sparse-random: Clamp preferred block size
The preferred block size must be between 512 and 32M. It was possible
to set a larger block size, so we must clamp it. (I clamped it at
both ends, even though currently the smallest block size is 1024).
Fixes: commit 5612598a49aaf4ac49f1b3e096dc4945ea7df640
(cherry picked from commit 7a7a103b0711a89a1912d6768db4a91bec3a5f17)
---
plugins/sparse-random/sparse-random.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/plugins/sparse-random/sparse-random.c b/plugins/sparse-random/sparse-random.c
index 8d1c2944..3d7da426 100644
--- a/plugins/sparse-random/sparse-random.c
+++ b/plugins/sparse-random/sparse-random.c
@@ -285,7 +285,15 @@ sparse_random_block_size (void *handle,
uint32_t *maximum)
{
*minimum = 1;
- *preferred = blocksize;
+
+ /* Preferred blocksize must be 512..32M so clamp this value. */
+ if (blocksize < 512)
+ *preferred = 512;
+ else if (blocksize > 32*1024*1024)
+ *preferred = 32*1024*1024;
+ else
+ *preferred = blocksize;
+
*maximum = 0xffffffff;
return 0;
}
--
2.47.3

View File

@ -0,0 +1,60 @@
From 26dd53f15d522ae137ec92afa2f49fd010ff2c15 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Sun, 1 Feb 2026 21:09:04 +0000
Subject: [PATCH] tests/test-sparse-random-blocksize.sh: Enhance the test
Check that all extents are aligned to the block size, and randomly
select the block size so we are testing both small and large blocks.
Updates: commit 5612598a49aaf4ac49f1b3e096dc4945ea7df640
(cherry picked from commit e11011043556b6ee4333212264bdefa5324eedc4)
---
tests/test-sparse-random-blocksize.sh | 28 +++++++++++++++++++++++++--
1 file changed, 26 insertions(+), 2 deletions(-)
diff --git a/tests/test-sparse-random-blocksize.sh b/tests/test-sparse-random-blocksize.sh
index c1230809..3ddc1f26 100755
--- a/tests/test-sparse-random-blocksize.sh
+++ b/tests/test-sparse-random-blocksize.sh
@@ -38,12 +38,36 @@ set -x
set -u
requires_run
+requires_nbdinfo
# nbdcopy >= 1.5.9 required for this test.
requires_nbdcopy
requires_libnbd_version 1.5.9
+out=sparse-random-blocksize.out
+cleanup_fn rm -f $out
+rm -f $out
+
+#blocksize=65536
+blocksize_r="$(( 10 + (RANDOM % 19) ))" ;# 10..28
+blocksize="$(( 1 << blocksize_r ))"
+
+export out
nbdkit -v \
sparse-random \
- size=10G blocksize=64K \
- --run 'nbdcopy "$uri" "$uri"'
+ size=10G blocksize=$blocksize \
+ --run '
+ nbdinfo --map "$uri" > $out &&
+ nbdcopy "$uri" "$uri"
+'
+
+# Check all the extents are aligned to $blocksize
+cat $out
+( while read offset size rest ; do
+ echo checking $offset $size ...
+ if test "$(( $offset % blocksize ))" != 0 ||
+ test "$(( $size % blocksize ))" != 0 ; then
+ echo error: extent is not aligned to $blocksize: offset $offset size $size
+ exit 1
+ fi
+done ) <$out
--
2.47.3

View File

@ -0,0 +1,40 @@
From 2e4212b86bd56ff6aaec6166cc3198050fcb172a Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Sun, 8 Feb 2026 22:10:17 +0000
Subject: [PATCH] tests/test-sparse-random-blocksize.sh: Reduce maximum block
size
On i686 this would fail if blocksize=32M was chosen, because we could
allocate (up to) 4 connections * 16 threads * 32M == 2G of RAM.
Probably we are not allocating that much, but it still often failed
with blocksize=32M.
Reduce the maximum we will choose down to 8M.
(cherry picked from commit 5edcc592dc9d4466596618da2d7507b575492a02)
---
tests/test-sparse-random-blocksize.sh | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/tests/test-sparse-random-blocksize.sh b/tests/test-sparse-random-blocksize.sh
index 3ddc1f26..29eec3f8 100755
--- a/tests/test-sparse-random-blocksize.sh
+++ b/tests/test-sparse-random-blocksize.sh
@@ -49,7 +49,13 @@ cleanup_fn rm -f $out
rm -f $out
#blocksize=65536
-blocksize_r="$(( 10 + (RANDOM % 19) ))" ;# 10..28
+
+# We could do this:
+#blocksize_r="$(( 10 + (RANDOM % 19) ))" ;# 10..28
+# but if this picks a 32M block size, then this could consume up to
+# 4 * 16 * 32 == 2048 MB of RAM. This is a problem on smaller systems
+# (and especially 32 bit), so choose a lesser maximum.
+blocksize_r="$(( 10 + (RANDOM % 17) ))" ;# 10..26
blocksize="$(( 1 << blocksize_r ))"
export out
--
2.47.3

View File

@ -54,8 +54,8 @@
%global source_directory 1.46-stable
Name: nbdkit
Version: 1.46.1
Release: 3%{?dist}
Version: 1.46.2
Release: 1%{?dist}
Summary: NBD server
License: BSD-3-Clause
@ -80,23 +80,27 @@ Source3: copy-patches.sh
# https://gitlab.com/nbdkit/nbdkit/-/commits/rhel-10.2/
# Patches.
Patch0001: 0001-python-Link-to-C-man-pages-for-module-functions.patch
Patch0002: 0002-python-Sort-documentation-for-module-functions-in-or.patch
Patch0003: 0003-blocksize-policy-Fix-assertion-failure-on-unaligned-.patch
Patch0004: 0004-file-Don-t-advertise-minimum_io_size-64K-the-max-sup.patch
Patch0005: 0005-todo-Add-note-about-problems-with-file-plugin-block_.patch
Patch0006: 0006-file-Change-calculations-of-block-size-hints-for-blo.patch
Patch0007: 0007-qcow2dec-Don-t-pass-flags-from-.extents-through-to-..patch
Patch0008: 0008-vddk-Test-with-VDDK-9.0.1.0.patch
Patch0009: 0009-configure.ac-Remove-use-of-which-command.patch
Patch0010: 0010-map-Fix-documentation-about-changing-the-size-of-the.patch
Patch0011: 0011-common-utils-utils.h-Add-C-boilerplate.patch
Patch0012: 0012-server-Add-nbdkit_debug_hexdiff-function.patch
Patch0013: 0013-checkwrite-Display-differences-if-D-checkwrite.showd.patch
Patch0014: 0014-docs-nbdkit_debug_hexdump.pod-Document-when-hexdiff-.patch
Patch0015: 0015-docs-nbdkit_debug_hexdump.pod-Add-a-link-back-to-nbd.patch
Patch0016: 0016-Add-new-nbdkit_name-function.patch
Patch0017: 0017-server-test-public.c-Add-process_name-dummy-variable.patch
Patch0001: 0001-vram-Cast-cl_ulong-to-uint64_t-before-printing.patch
Patch0002: 0002-server-Add-nbdkit_debug_hexdiff-function.patch
Patch0003: 0003-checkwrite-Display-differences-if-D-checkwrite.showd.patch
Patch0004: 0004-docs-nbdkit_debug_hexdump.pod-Document-when-hexdiff-.patch
Patch0005: 0005-docs-nbdkit_debug_hexdump.pod-Add-a-link-back-to-nbd.patch
Patch0006: 0006-Add-new-nbdkit_name-function.patch
Patch0007: 0007-server-test-public.c-Add-process_name-dummy-variable.patch
Patch0008: 0008-Add-new-nbdkit_timestamp-function.patch
Patch0009: 0009-log-Use-nbdkit_timestamp.patch
Patch0010: 0010-docs-nbdkit-plugin.pod-Add-a-link-to-nbdkit_timestam.patch
Patch0011: 0011-docs-nbdkit_timestamp.pod-Fix-copy-and-paste-error-i.patch
Patch0012: 0012-todo-Add-item-about-nbdkit_timestamp-on-the-main-thr.patch
Patch0013: 0013-server-sockets.c-Print-the-actual-bound-addresses-an.patch
Patch0014: 0014-server-Partially-fix-port-0.patch
Patch0015: 0015-tests-test-ip-filter.sh-Remove-use-of-pick_unused_po.patch
Patch0016: 0016-tests-test-ipv4-lo.sh-tests-test-ipv6-lo.sh-Remove-p.patch
Patch0017: 0017-server-sockets.c-Don-t-crash-if-TCP-IP-selected-with.patch
Patch0018: 0018-sparse-random-Make-block-size-configurable.patch
Patch0019: 0019-sparse-random-Clamp-preferred-block-size.patch
Patch0020: 0020-tests-test-sparse-random-blocksize.sh-Enhance-the-te.patch
Patch0021: 0021-tests-test-sparse-random-blocksize.sh-Reduce-maximum.patch
# For automatic RPM Provides generation.
# See: https://rpm-software-management.github.io/rpm/manual/dependency_generators.html
@ -1604,9 +1608,10 @@ fi
%changelog
* Tue Jan 20 2026 Richard W.M. Jones <rjones@redhat.com> - 1.46.1-3
- Rebase to nbdkit 1.46.1
- Backport nbdkit_debug_hexdiff and nbdkit_name from nbdkit 1.47.
* Mon Feb 09 2026 Richard W.M. Jones <rjones@redhat.com> - 1.46.2-1
- Rebase to nbdkit 1.46.2
- Backport nbdkit_debug_hexdiff, nbdkit_name, nbdkit_timestamp
from nbdkit 1.47.
resolves: RHEL-111242
- Synchronize spec file with Fedora.
- vddk: Don't use FNM_PATHNAME when matching export parameter

View File

@ -1,2 +1,2 @@
SHA512 (nbdkit-1.46.1.tar.gz) = 7a4223e4b22314dd711cfa09e24d248b02a084ffef35b4cd4df83f9c9e5c485abc1cd2dfc609be82ef2c35841259035b3a388dd0b779bf17d20318132da69747
SHA512 (nbdkit-1.46.1.tar.gz.sig) = 53140ec57584b7f2946f193036fedd1ea232f6364f75716c11dfb6d6d9941a7a28d82a51d959ae1b7a075b36604c013182c10a9cab7f6ee3b58cba937b150e4c
SHA512 (nbdkit-1.46.2.tar.gz) = ce01ac7a90cb995ec3ef46c8cec0be7b47d7266edc4383df6e867881fd4ba66b5f36ccc4de356a0df0f1d47f86adb40212e8de62332d19014b293649f25d4b37
SHA512 (nbdkit-1.46.2.tar.gz.sig) = 73df3e276564c292ef5d94e6f5db0e25c77c65d1f6dab4d090528e7546340cfbea5e96bb385841ac4c2de29eebf7e52db8e74fbf3b59bbb48cef64e50dfa0790