From a49294b11b72834e9bb182a838bdec5b1126d4f9 Mon Sep 17 00:00:00 2001 From: Garth Snyder Date: Sat, 21 Mar 2026 16:27:16 -0700 Subject: [PATCH] Consistently encode DRR_BEGIN packed nvlist payloads with NV_ENCODE_XDR This is a fix for #18360. Currently, zfs send generates a mix of nvlist encodings in DRR_BEGIN records, some XDR and some in native byte order. The result is that most streams currently can't be zfs received on opposite-endian systems. zfs send generates the outer wrappers for compound streams in userspace, and it explicitly requests NV_ENCODE_XDR format for those records. But the BEGIN records for individual datasets are generated on the kernel side, in dmu_send.c, where fnvlist_pack() is used for encoding. That routine hard-wires NV_ENCODE_NATIVE format. This PR replaces the fnvlist_pack() call with a direct call to nvlist_pack() that specifies NV_ENCODE_XDR. Tests are included to verify that native-encoded nvlists are not generated by any kernel path that attaches nvlists to BEGIN records. There's also a check for XDR encoding in the outer wrapper of replication streams in case there is ever a regression there. There are also two tests that have a chance of triggering (and detecting) bug #18491. Non-triggering versions of those tests are already included here, so when that bug is more fully characterized, the tests can be moved to a more directly relevant category. (They are the two tests with _with_write suffixes.) This PR adds to zstream dump an output line that shows the exact encoding of any nvlists in BEGIN records. This feature is used by the tests to validate streams. Signed-off-by: Garth Snyder --- cmd/zstream/zstream_dump.c | 14 +++ module/zfs/dmu_send.c | 37 +++++- tests/runfiles/common.run | 9 ++ tests/test-runner/bin/zts-report.py.in | 2 + tests/zfs-tests/tests/Makefile.am | 18 +++ .../functional/send_xdr_encoding/cleanup.ksh | 27 ++++ .../send_xdr_encoding/send_xdr_encoding.cfg | 25 ++++ .../send_xdr_encoding.kshlib | 71 +++++++++++ .../functional/send_xdr_encoding/setup.ksh | 29 +++++ .../send_xdr_encoding/xdr_bookmark_raw.ksh | 93 ++++++++++++++ .../xdr_bookmark_raw_with_write.ksh | 107 ++++++++++++++++ .../xdr_incr_from_bookmark.ksh | 88 +++++++++++++ .../xdr_incr_from_redacted.ksh | 96 +++++++++++++++ .../functional/send_xdr_encoding/xdr_raw.ksh | 67 ++++++++++ .../send_xdr_encoding/xdr_redacted_full.ksh | 72 +++++++++++ .../xdr_redacted_received.ksh | 84 +++++++++++++ .../xdr_redacted_received_raw.ksh | 97 +++++++++++++++ .../send_xdr_encoding/xdr_replication.ksh | 90 ++++++++++++++ .../send_xdr_encoding/xdr_resume.ksh | 73 +++++++++++ .../xdr_resume_bookmark_raw.ksh | 103 ++++++++++++++++ .../xdr_resume_bookmark_raw_with_write.ksh | 116 ++++++++++++++++++ .../send_xdr_encoding/xdr_resume_raw.ksh | 79 ++++++++++++ .../send_xdr_encoding/xdr_resume_redacted.ksh | 86 +++++++++++++ 23 files changed, 1481 insertions(+), 2 deletions(-) create mode 100755 tests/zfs-tests/tests/functional/send_xdr_encoding/cleanup.ksh create mode 100644 tests/zfs-tests/tests/functional/send_xdr_encoding/send_xdr_encoding.cfg create mode 100644 tests/zfs-tests/tests/functional/send_xdr_encoding/send_xdr_encoding.kshlib create mode 100755 tests/zfs-tests/tests/functional/send_xdr_encoding/setup.ksh create mode 100755 tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_bookmark_raw.ksh create mode 100755 tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_bookmark_raw_with_write.ksh create mode 100755 tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_incr_from_bookmark.ksh create mode 100755 tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_incr_from_redacted.ksh create mode 100755 tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_raw.ksh create mode 100755 tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_redacted_full.ksh create mode 100755 tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_redacted_received.ksh create mode 100755 tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_redacted_received_raw.ksh create mode 100755 tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_replication.ksh create mode 100755 tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_resume.ksh create mode 100755 tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_resume_bookmark_raw.ksh create mode 100755 tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_resume_bookmark_raw_with_write.ksh create mode 100755 tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_resume_raw.ksh create mode 100755 tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_resume_redacted.ksh diff --git a/cmd/zstream/zstream_dump.c b/cmd/zstream/zstream_dump.c index 6ccc57204c8e..7757ee3b1754 100644 --- a/cmd/zstream/zstream_dump.c +++ b/cmd/zstream/zstream_dump.c @@ -385,6 +385,20 @@ zstream_do_dump(int argc, char *argv[]) (void) ssread(buf, sz, &zc); if (ferror(send_stream)) perror("fread"); + + uint8_t *nv_header = (uint8_t *)buf; + boolean_t xdr = nv_header[0] == NV_ENCODE_XDR; + boolean_t big_endian = nv_header[1] == 0; + const char *nc; + if (xdr) { + nc = "NV_ENCODE_XDR"; + } else if (big_endian) { + nc = "NV_ENCODE_NATIVE (big-endian)"; + } else { + nc = "NV_ENCODE_NATIVE (little-endian)"; + } + printf("nvlist encoding = %s\n", nc); + err = nvlist_unpack(buf, sz, &nv, 0); if (err) { perror(strerror(err)); diff --git a/module/zfs/dmu_send.c b/module/zfs/dmu_send.c index 4c354722e4f8..d931d9432f04 100644 --- a/module/zfs/dmu_send.c +++ b/module/zfs/dmu_send.c @@ -2241,6 +2241,37 @@ setup_send_progress(struct dmu_send_params *dspp) return (dssp); } +/* + * Payloads must be multiples of 8 bytes for historical compatibility, but + * XDR-encoded nvlists are sized in multiples of 4 bytes and may need padding. + * + * Here we do the simplest possible thing and copy the data to a separate + * buffer. Not ideal in terms of performance and memory use, but most BEGIN + * nvlists are small or absent, the allocation is momentary, and we'll need + * to do this at most once per dataset. + * + * It's OK if there is extra data after a packed nvlist on the receiving + * side because packed nvlists have an internal end-of-list marker. + * + * The new buffer is allocated with kmem_alloc() and can be freed with + * fnvlist_pack_free(), like the original. + */ +static inline void +pad_packed_nvlist(char **buffer, size_t *size) +{ + size_t size_in = *size; + size_t extra_bytes = P2ROUNDUP(size_in, 8) - size_in; + if (extra_bytes != 0) { + size_t expanded_size = size_in + extra_bytes; + char *longbuf = kmem_alloc(expanded_size, KM_SLEEP); + memcpy(longbuf, *buffer, size_in); + memset(longbuf + size_in, 0, extra_bytes); + fnvlist_pack_free(*buffer, size_in); + *buffer = longbuf; + *size = expanded_size; + } +} + /* * Actually do the bulk of the work in a zfs send. * @@ -2474,7 +2505,7 @@ dmu_send_impl(struct dmu_send_params *dspp) dsl_pool_rele(dp, tag); - void *payload = NULL; + char *payload = NULL; size_t payload_len = 0; nvlist_t *nvl = fnvlist_alloc(); @@ -2548,7 +2579,9 @@ dmu_send_impl(struct dmu_send_params *dspp) } if (!nvlist_empty(nvl)) { - payload = fnvlist_pack(nvl, &payload_len); + VERIFY0(nvlist_pack(nvl, &payload, &payload_len, + NV_ENCODE_XDR, KM_SLEEP)); + pad_packed_nvlist(&payload, &payload_len); drr->drr_payloadlen = payload_len; } diff --git a/tests/runfiles/common.run b/tests/runfiles/common.run index 14e4bd79f853..f18835da74bc 100644 --- a/tests/runfiles/common.run +++ b/tests/runfiles/common.run @@ -1025,6 +1025,15 @@ tests = ['scrub_mirror_001_pos', 'scrub_mirror_002_pos', 'scrub_mirror_003_pos', 'scrub_mirror_004_pos'] tags = ['functional', 'scrub_mirror'] +[tests/functional/send_xdr_encoding] +tests = ['xdr_bookmark_raw', 'xdr_bookmark_raw_with_write', + 'xdr_incr_from_bookmark', 'xdr_incr_from_redacted', 'xdr_raw', + 'xdr_redacted_full', 'xdr_redacted_received', + 'xdr_redacted_received_raw', 'xdr_replication', 'xdr_resume', + 'xdr_resume_bookmark_raw', 'xdr_resume_bookmark_raw_with_write', + 'xdr_resume_raw', 'xdr_resume_redacted'] +tags = ['functional', 'send_xdr_encoding'] + [tests/functional/slog] tests = ['slog_001_pos', 'slog_002_pos', 'slog_003_pos', 'slog_004_pos', 'slog_005_pos', 'slog_006_pos', 'slog_007_pos', 'slog_008_neg', diff --git a/tests/test-runner/bin/zts-report.py.in b/tests/test-runner/bin/zts-report.py.in index 29d2760ccb8f..2cbd2f02a318 100755 --- a/tests/test-runner/bin/zts-report.py.in +++ b/tests/test-runner/bin/zts-report.py.in @@ -253,6 +253,8 @@ maybe = { 'renameat2/setup': ['SKIP', renameat2_reason], 'reservation/reservation_008_pos': ['FAIL', 7741], 'reservation/reservation_018_pos': ['FAIL', 5642], + 'send_xdr_encoding/xdr_bookmark_raw_with_write': ['FAIL', 18491], + 'send_xdr_encoding/xdr_resume_bookmark_raw_with_write': ['FAIL', 18491], 'snapshot/clone_001_pos': ['FAIL', known_reason], 'snapshot/snapshot_006_pos': ['FAIL', known_reason], 'snapshot/snapshot_009_pos': ['FAIL', 7961], diff --git a/tests/zfs-tests/tests/Makefile.am b/tests/zfs-tests/tests/Makefile.am index 28acc6f3af12..5dd350ece7c3 100644 --- a/tests/zfs-tests/tests/Makefile.am +++ b/tests/zfs-tests/tests/Makefile.am @@ -376,6 +376,8 @@ nobase_dist_datadir_zfs_tests_tests_DATA += \ functional/rsend/rsend.kshlib \ functional/scrub_mirror/default.cfg \ functional/scrub_mirror/scrub_mirror_common.kshlib \ + functional/send_xdr_encoding/send_xdr_encoding.cfg \ + functional/send_xdr_encoding/send_xdr_encoding.kshlib \ functional/slog/slog.cfg \ functional/slog/slog.kshlib \ functional/snapshot/snapshot.cfg \ @@ -2129,6 +2131,22 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/scrub_mirror/scrub_mirror_003_pos.ksh \ functional/scrub_mirror/scrub_mirror_004_pos.ksh \ functional/scrub_mirror/setup.ksh \ + functional/send_xdr_encoding/cleanup.ksh \ + functional/send_xdr_encoding/setup.ksh \ + functional/send_xdr_encoding/xdr_bookmark_raw.ksh \ + functional/send_xdr_encoding/xdr_bookmark_raw_with_write.ksh \ + functional/send_xdr_encoding/xdr_incr_from_bookmark.ksh \ + functional/send_xdr_encoding/xdr_incr_from_redacted.ksh \ + functional/send_xdr_encoding/xdr_raw.ksh \ + functional/send_xdr_encoding/xdr_redacted_full.ksh \ + functional/send_xdr_encoding/xdr_redacted_received.ksh \ + functional/send_xdr_encoding/xdr_redacted_received_raw.ksh \ + functional/send_xdr_encoding/xdr_replication.ksh \ + functional/send_xdr_encoding/xdr_resume.ksh \ + functional/send_xdr_encoding/xdr_resume_bookmark_raw.ksh \ + functional/send_xdr_encoding/xdr_resume_bookmark_raw_with_write.ksh \ + functional/send_xdr_encoding/xdr_resume_raw.ksh \ + functional/send_xdr_encoding/xdr_resume_redacted.ksh \ functional/slog/cleanup.ksh \ functional/slog/setup.ksh \ functional/slog/slog_001_pos.ksh \ diff --git a/tests/zfs-tests/tests/functional/send_xdr_encoding/cleanup.ksh b/tests/zfs-tests/tests/functional/send_xdr_encoding/cleanup.ksh new file mode 100755 index 000000000000..8261885e6512 --- /dev/null +++ b/tests/zfs-tests/tests/functional/send_xdr_encoding/cleanup.ksh @@ -0,0 +1,27 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# +# Copyright (c) 2026 by Garth Snyder. All rights reserved. +# + +. $STF_SUITE/tests/functional/send_xdr_encoding/send_xdr_encoding.kshlib + +destroy_pool $POOL +destroy_pool $POOL2 + +log_pass diff --git a/tests/zfs-tests/tests/functional/send_xdr_encoding/send_xdr_encoding.cfg b/tests/zfs-tests/tests/functional/send_xdr_encoding/send_xdr_encoding.cfg new file mode 100644 index 000000000000..e4999a3ca29c --- /dev/null +++ b/tests/zfs-tests/tests/functional/send_xdr_encoding/send_xdr_encoding.cfg @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# +# Copyright (c) 2026 by Garth Snyder. All rights reserved. +# + +read -r DISK1 DISK2 _ <<<"$DISKS" +export DISK1 DISK2 + +export POOL=$TESTPOOL +export POOL2=$TESTPOOL2 diff --git a/tests/zfs-tests/tests/functional/send_xdr_encoding/send_xdr_encoding.kshlib b/tests/zfs-tests/tests/functional/send_xdr_encoding/send_xdr_encoding.kshlib new file mode 100644 index 000000000000..8e36b748439f --- /dev/null +++ b/tests/zfs-tests/tests/functional/send_xdr_encoding/send_xdr_encoding.kshlib @@ -0,0 +1,71 @@ +#!/bin/ksh +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# +# Copyright (c) 2026 by Garth Snyder. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/send_xdr_encoding/send_xdr_encoding.cfg + +# +# Verify that the DRR_BEGIN records in the given send stream encode their +# nvlist payloads with NV_ENCODE_XDR (and not NV_ENCODE_NATIVE). +# +# DRR_BEGIN records that carry an nvlist payload (raw sends, redacted sends, +# resumed sends, and combinations thereof) must encode that payload with +# NV_ENCODE_XDR so the resulting stream can be portably consumed across +# endianness. Encoding the payload with NV_ENCODE_NATIVE produces a stream +# that is unreadable on a receiver of the opposite endianness. +# +# zstream dump prints a single "nvlist encoding = ..." line per DRR_BEGIN +# record that carries an nvlist payload. The possible values are: +# +# NV_ENCODE_XDR +# NV_ENCODE_NATIVE (big-endian) +# NV_ENCODE_NATIVE (little-endian) +# +# Every test in this suite generates a stream whose DRR_BEGIN record +# carries an nvlist payload, so the pass criterion is: +# +# - At least one NV_ENCODE_XDR line appears, AND +# - No NV_ENCODE_NATIVE line appears. +# +# Requiring at least one XDR line catches the case where zstream dump +# itself fails before producing any encoding output. Asserting on dump +# content rather than dump exit status means a partial dump can still +# fail the test on an NV_ENCODE_NATIVE seen before the failure point. +# +function verify_xdr_nvlist_encoding +{ + typeset stream=$1 + typeset out + + [[ -f "$stream" ]] || \ + log_fail "verify_xdr_nvlist_encoding: stream not found: $stream" + + out=$(zstream dump "$stream" 2>/dev/null) + + if echo "$out" | grep -q 'NV_ENCODE_NATIVE'; then + log_fail "verify_xdr_nvlist_encoding: " \ + "NV_ENCODE_NATIVE found in $stream" + fi + if ! echo "$out" | grep -q 'NV_ENCODE_XDR'; then + log_fail "verify_xdr_nvlist_encoding: " \ + "no NV_ENCODE_XDR found in $stream" + fi +} diff --git a/tests/zfs-tests/tests/functional/send_xdr_encoding/setup.ksh b/tests/zfs-tests/tests/functional/send_xdr_encoding/setup.ksh new file mode 100755 index 000000000000..609acba3a224 --- /dev/null +++ b/tests/zfs-tests/tests/functional/send_xdr_encoding/setup.ksh @@ -0,0 +1,29 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# +# Copyright (c) 2026 by Garth Snyder. All rights reserved. +# + +. $STF_SUITE/tests/functional/send_xdr_encoding/send_xdr_encoding.kshlib + +verify_disk_count "$DISKS" 2 + +create_pool $POOL $DISK1 +create_pool $POOL2 $DISK2 + +log_pass diff --git a/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_bookmark_raw.ksh b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_bookmark_raw.ksh new file mode 100755 index 000000000000..9ba10d9e605a --- /dev/null +++ b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_bookmark_raw.ksh @@ -0,0 +1,93 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# +# Copyright (c) 2026 by Garth Snyder. All rights reserved. +# + +. $STF_SUITE/tests/functional/send_xdr_encoding/send_xdr_encoding.kshlib + +# +# Description: +# A raw incremental send from a redaction bookmark on an encrypted dataset +# (zfs send -w -i ds#book ds@snap) carries both BEGINNV_REDACT_FROM_SNAPS +# and crypt_keydata in its DRR_BEGIN nvlist payload. Verify that this +# combined payload is XDR-encoded and the stream can be received. +# +# Strategy: +# 1. Create an encrypted source dataset with a redaction bookmark and a +# later snapshot. +# 2. Establish a raw base on the receiver via zfs send -w of the bookmark's +# source snapshot. +# 3. zfs send -w -i sendfs#book sendfs@s1 to a file. +# 4. Verify that the resulting stream is XDR-encoded. +# 5. Verify that the zfs receive succeeds. +# + +verify_runnable "both" + +sendfs="$POOL/xdr_bookmark_raw_src" +clonefs="$POOL/xdr_bookmark_raw_clone" +recvfs="$POOL2/xdr_bookmark_raw_recv" +keyfile="/$POOL/xdr_bookmark_raw.key" +full_stream="/$POOL/xdr_bookmark_raw_full.zsend" +incr_stream="/$POOL/xdr_bookmark_raw_incr.zsend" + +function cleanup +{ + datasetexists $sendfs && destroy_dataset $sendfs -R + datasetexists $recvfs && destroy_dataset $recvfs -R + rm -f $keyfile $full_stream $incr_stream +} +log_onexit cleanup + +log_assert "BEGIN nvlist of a raw incremental from a redaction bookmark is " \ + "XDR-encoded and receivable" + +log_must eval "echo 'thisisapassphrase' > $keyfile" +log_must zfs create -o encryption=on -o keyformat=passphrase \ + -o keylocation=file://$keyfile $sendfs + +log_must dd if=/dev/urandom of=/$sendfs/f1 bs=128k count=8 status=none +log_must dd if=/dev/urandom of=/$sendfs/f2 bs=128k count=8 status=none +log_must zfs snapshot $sendfs@s0 + +# The clone inherits encryption from $sendfs. +log_must zfs clone $sendfs@s0 $clonefs +log_must dd if=/dev/urandom of=/$clonefs/f1 bs=128k count=8 conv=notrunc \ + status=none +log_must zfs snapshot $clonefs@s + +log_must zfs redact $sendfs@s0 redaction-bookmark $clonefs@s + +# Take @s1 with no intervening writes. See xdr_bookmark_raw_with_write.ksh +# for a variant that includes a post-redact write; that variant exercises +# a known kernel-side issue (#18491) and may flake. +log_must zfs snapshot $sendfs@s1 + +# Establish a raw base on the receiver. +log_must eval "zfs send -w $sendfs@s0 > $full_stream" +log_must eval "zfs receive $recvfs < $full_stream" + +# Raw incremental from the redaction bookmark. This is the test focus. +log_must eval "zfs send -w -i $sendfs#redaction-bookmark $sendfs@s1 > \ + $incr_stream" +verify_xdr_nvlist_encoding $incr_stream +log_must eval "zfs receive $recvfs < $incr_stream" + +log_pass "BEGIN nvlist of a raw incremental from a redaction bookmark is " \ + "XDR-encoded and receivable" diff --git a/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_bookmark_raw_with_write.ksh b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_bookmark_raw_with_write.ksh new file mode 100755 index 000000000000..c58735f04d40 --- /dev/null +++ b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_bookmark_raw_with_write.ksh @@ -0,0 +1,107 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# +# Copyright (c) 2026 by Garth Snyder. All rights reserved. +# + +. $STF_SUITE/tests/functional/send_xdr_encoding/send_xdr_encoding.kshlib + +# +# Description: +# This is the post-redact-write variant of xdr_bookmark_raw, separated out +# because of a known issue (#18491) that causes it to fail roughly 30% of +# the time. It's included here as a test for issue #18491 until the exact +# source of that problem can be pinned down more specifically. +# +# Known issue: openzfs/zfs#18491 +# +# On a freshly-created pool, `zfs send -w -i ds#book ds@snap` intermittently +# fails with EACCES whenever there is data-modifying activity between the +# `zfs redact` that created the bookmark and the subsequent send. This EACCES +# is surfaced to userspace as the misleading message "dataset key must be +# loaded," although the key remains loaded throughout. +# +# The reproducer script included in the issue report typically triggers the +# problem within about 10 iterations on a fresh pool. Disk-sync mitigations +# (zpool sync, with or without `-f`, with or without sleep, single or doubled, +# applied at any reasonable point) do not avert the problem. CI runs that +# include the test in this file reproduce the failure regularly (though +# intermittently) across multiple distributions. xdr_resume_bookmark_raw.ksh +# removes the post-redact write (which is not essential to the test) and +# therefore runs reliably. +# +# When this test fails, the failure marker is the libzfs warning +# "dataset key must be loaded" on stderr from the first `zfs send -w -i` +# line below (the one that produces the stream we then truncate), and a +# non-zero exit from that send. The test does not attempt to distinguish +# the known-issue failure from other possible failures. +# + +verify_runnable "both" + +sendfs="$POOL/xdr_bookmark_raw_with_write_src" +clonefs="$POOL/xdr_bookmark_raw_with_write_clone" +recvfs="$POOL2/xdr_bookmark_raw_with_write_recv" +keyfile="/$POOL/xdr_bookmark_raw_with_write.key" +full_stream="/$POOL/xdr_bookmark_raw_with_write_full.zsend" +incr_stream="/$POOL/xdr_bookmark_raw_with_write_incr.zsend" + +function cleanup +{ + datasetexists $sendfs && destroy_dataset $sendfs -R + datasetexists $recvfs && destroy_dataset $recvfs -R + rm -f $keyfile $full_stream $incr_stream +} +log_onexit cleanup + +log_assert "BEGIN nvlist of a raw incremental from a redaction bookmark, " \ + "with a post-redact write, is XDR-encoded and receivable " \ + "(known to flake; see openzfs/zfs#18491)" + +log_must eval "echo 'thisisapassphrase' > $keyfile" +log_must zfs create -o encryption=on -o keyformat=passphrase \ + -o keylocation=file://$keyfile $sendfs + +log_must dd if=/dev/urandom of=/$sendfs/f1 bs=128k count=8 status=none +log_must zfs snapshot $sendfs@s0 + +# The clone inherits encryption from $sendfs. +log_must zfs clone $sendfs@s0 $clonefs +log_must dd if=/dev/urandom of=/$clonefs/f1 bs=128k count=8 conv=notrunc \ + status=none +log_must zfs snapshot $clonefs@s + +log_must zfs redact $sendfs@s0 redaction-bookmark $clonefs@s + +# Post-redact write: the trigger for openzfs/zfs#18491. +log_must dd if=/dev/urandom of=/$sendfs/f3 bs=128k count=8 status=none +log_must zfs snapshot $sendfs@s1 + +# Establish a raw base on the receiver. +log_must eval "zfs send -w $sendfs@s0 > $full_stream" +log_must eval "zfs receive $recvfs < $full_stream" + +# The next line is what races. On failure it exits with EACCES rendered +# as "dataset key must be loaded". +log_must eval "zfs send -w -i $sendfs#redaction-bookmark $sendfs@s1 > \ + $incr_stream" +verify_xdr_nvlist_encoding $incr_stream +log_must eval "zfs receive $recvfs < $incr_stream" + +log_pass "BEGIN nvlist of a raw incremental from a redaction bookmark, " \ + "with a post-redact write, is XDR-encoded and receivable" diff --git a/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_incr_from_bookmark.ksh b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_incr_from_bookmark.ksh new file mode 100755 index 000000000000..ab04f6aa6033 --- /dev/null +++ b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_incr_from_bookmark.ksh @@ -0,0 +1,88 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# +# Copyright (c) 2026 by Garth Snyder. All rights reserved. +# + +. $STF_SUITE/tests/functional/send_xdr_encoding/send_xdr_encoding.kshlib + +# +# Description: +# An incremental send from a redaction bookmark (zfs send -i ds#book ds@snap) +# carries BEGINNV_REDACT_FROM_SNAPS in its DRR_BEGIN nvlist payload (via the +# from_rl path). Verify that this payload is XDR-encoded and the stream can +# be received. +# +# Strategy: +# 1. Create a source dataset with a redaction bookmark. +# 2. Send a redacted full stream from that bookmark's source snapshot +# and receive it into a second pool as a base. +# 3. Add data and a new snapshot on the source. +# 4. zfs send -i sendfs#redaction-bookmark sendfs@snap to a file. +# 5. Verify XDR encoding in the resulting stream. +# 6. Verify that zfs receive of the stream succeeds. +# + +verify_runnable "both" + +sendfs="$POOL/xdr_incr_from_bookmark_src" +clonefs="$POOL/xdr_incr_from_bookmark_clone" +recvfs="$POOL2/xdr_incr_from_bookmark_recv" +full_stream="/$POOL/xdr_incr_from_bookmark_full.zsend" +incr_stream="/$POOL/xdr_incr_from_bookmark_incr.zsend" + +function cleanup +{ + datasetexists $sendfs && destroy_dataset $sendfs -R + datasetexists $recvfs && destroy_dataset $recvfs -R + rm -f $full_stream $incr_stream +} +log_onexit cleanup + +log_assert "BEGIN nvlist of an incremental send from a redaction bookmark " \ + "is XDR-encoded and receivable" + +log_must zfs create $sendfs +log_must dd if=/dev/urandom of=/$sendfs/f1 bs=128k count=8 status=none +log_must dd if=/dev/urandom of=/$sendfs/f2 bs=128k count=8 status=none +log_must zfs snapshot $sendfs@s0 + +log_must zfs clone $sendfs@s0 $clonefs +log_must dd if=/dev/urandom of=/$clonefs/f1 bs=128k count=8 conv=notrunc \ + status=none +log_must zfs snapshot $clonefs@s + +log_must zfs redact $sendfs@s0 redaction-bookmark $clonefs@s + +# Establish a base on the receiver. +log_must eval "zfs send --redact redaction-bookmark $sendfs@s0 > $full_stream" +log_must eval "zfs receive $recvfs < $full_stream" + +# Add a new snapshot on the source for the incremental. +log_must dd if=/dev/urandom of=/$sendfs/f3 bs=128k count=8 status=none +log_must zfs snapshot $sendfs@s1 + +# Generate an incremental send from the redaction bookmark. This fires +# BEGINNV_REDACT_FROM_SNAPS via the from_rl path because the from-side +# is a redaction bookmark. +log_must eval "zfs send -i $sendfs#redaction-bookmark $sendfs@s1 > $incr_stream" +verify_xdr_nvlist_encoding $incr_stream +log_must eval "zfs receive $recvfs < $incr_stream" + +log_pass "BEGIN nvlist of an incremental send from a redaction bookmark " \ + "is XDR-encoded and receivable" diff --git a/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_incr_from_redacted.ksh b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_incr_from_redacted.ksh new file mode 100755 index 000000000000..fc4d34c43462 --- /dev/null +++ b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_incr_from_redacted.ksh @@ -0,0 +1,96 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# +# Copyright (c) 2026 by Garth Snyder. All rights reserved. +# + +. $STF_SUITE/tests/functional/send_xdr_encoding/send_xdr_encoding.kshlib + +# +# Description: +# An incremental send whose from-side is a snapshot of a previously-redacted +# dataset carries BEGINNV_REDACT_FROM_SNAPS in its DRR_BEGIN nvlist payload +# via a different code path than incrementals from a redaction bookmark +# (the dspp->numfromredactsnaps path). Verify that this payload is +# XDR-encoded and that the stream can be received. +# +# Strategy: +# 1. Produce a redacted dataset on a receiver via a redacted full send, +# leaving the receiver with a snapshot whose from-side will carry the +# SPA_FEATURE_REDACTED_DATASETS feature. +# 2. Establish the same base on a tertiary destination so we have somewhere +# to apply the incremental. +# 3. Create a new snapshot of the receiver-side redacted dataset. +# 4. zfs send -i mid@s0 mid@s1 to a file. +# 5. Verify that the stream is XDR encoded. +# 6. Verify that we can zfs receive the incremental onto the tertiary base. +# + +verify_runnable "both" + +sendfs="$POOL/xdr_incr_from_redacted_src" +clonefs="$POOL/xdr_incr_from_redacted_clone" +midfs="$POOL2/xdr_incr_from_redacted_mid" +tertiary="$POOL/xdr_incr_from_redacted_tertiary" +full_stream="/$POOL/xdr_incr_from_redacted_full.zsend" +incr_stream="/$POOL/xdr_incr_from_redacted_incr.zsend" + +function cleanup +{ + datasetexists $sendfs && destroy_dataset $sendfs -R + datasetexists $midfs && destroy_dataset $midfs -R + datasetexists $tertiary && destroy_dataset $tertiary -R + rm -f $full_stream $incr_stream +} +log_onexit cleanup + +log_assert "BEGIN nvlist of an incremental from a previously-redacted " \ + "snapshot is XDR-encoded and receivable" + +log_must zfs create $sendfs +log_must dd if=/dev/urandom of=/$sendfs/f1 bs=128k count=8 status=none +log_must dd if=/dev/urandom of=/$sendfs/f2 bs=128k count=8 status=none +log_must zfs snapshot $sendfs@s0 + +log_must zfs clone $sendfs@s0 $clonefs +log_must dd if=/dev/urandom of=/$clonefs/f1 bs=128k count=8 conv=notrunc \ + status=none +log_must zfs snapshot $clonefs@s + +log_must zfs redact $sendfs@s0 redaction-bookmark $clonefs@s + +# Produce two receivers of the redacted full send: one we will re-send from +# (mid) and one we will receive the incremental into (tertiary). +log_must eval "zfs send --redact redaction-bookmark $sendfs@s0 > $full_stream" +log_must eval "zfs receive $midfs < $full_stream" +log_must eval "zfs receive $tertiary < $full_stream" + +# Create a fresh snapshot of the redacted receiver. The data has not changed +# (and cannot be modified without mounting), but the snapshot itself is +# enough to drive an incremental send and trigger the case-4 nvlist path. +log_must zfs snapshot $midfs@s1 + +# Create an incremental send from the redacted from-side. This fires +# BEGINNV_REDACT_FROM_SNAPS via the dspp->numfromredactsnaps path because +# $midfs@s0 has the SPA_FEATURE_REDACTED_DATASETS feature active. +log_must eval "zfs send -i $midfs@s0 $midfs@s1 > $incr_stream" +verify_xdr_nvlist_encoding $incr_stream +log_must eval "zfs receive $tertiary < $incr_stream" + +log_pass "BEGIN nvlist of an incremental from a previously-redacted snapshot " \ + "is XDR-encoded and receivable" diff --git a/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_raw.ksh b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_raw.ksh new file mode 100755 index 000000000000..c3a196650c6a --- /dev/null +++ b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_raw.ksh @@ -0,0 +1,67 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# +# Copyright (c) 2026 by Garth Snyder. All rights reserved. +# + +. $STF_SUITE/tests/functional/send_xdr_encoding/send_xdr_encoding.kshlib + +# +# Description: +# A raw send of an encrypted dataset (zfs send -w) carries a "crypt_keydata" +# nested nvlist in its DRR_BEGIN nvlist payload. Verify that this payload is +# XDR-encoded and that the stream can be received. +# +# Strategy: +# 1. Create an encrypted dataset with one snapshot. +# 2. zfs send -w to a file. +# 3. Verify that the stream is XDR-encoded. +# 4. Verify that zfs receive succeeds. +# + +verify_runnable "both" + +sendfs="$POOL/xdr_raw_src" +recvfs="$POOL2/xdr_raw_recv" +keyfile="/$POOL/xdr_raw.key" +stream="/$POOL/xdr_raw.zsend" + +function cleanup +{ + datasetexists $sendfs && destroy_dataset $sendfs -r + datasetexists $recvfs && destroy_dataset $recvfs -r + rm -f $keyfile $stream +} +log_onexit cleanup + +log_assert "BEGIN nvlist of a raw send of an encrypted dataset is " \ + "XDR-encoded and receivable" + +log_must eval "echo 'thisisapassphrase' > $keyfile" +log_must zfs create -o encryption=on -o keyformat=passphrase \ + -o keylocation=file://$keyfile $sendfs +log_must dd if=/dev/urandom of=/$sendfs/f1 bs=128k count=8 status=none +log_must zfs snapshot $sendfs@s1 + +log_must eval "zfs send -w $sendfs@s1 > $stream" + +verify_xdr_nvlist_encoding $stream +log_must eval "zfs receive $recvfs < $stream" + +log_pass "BEGIN nvlist of a raw send of an encrypted dataset is " \ + "XDR-encoded and receivable" diff --git a/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_redacted_full.ksh b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_redacted_full.ksh new file mode 100755 index 000000000000..2bad9bebdaaf --- /dev/null +++ b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_redacted_full.ksh @@ -0,0 +1,72 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# +# Copyright (c) 2026 by Garth Snyder. All rights reserved. +# + +. $STF_SUITE/tests/functional/send_xdr_encoding/send_xdr_encoding.kshlib + +# +# Description: +# A redacted send (zfs send --redact ) carries BEGINNV_REDACT_SNAPS +# in its DRR_BEGIN nvlist payload. Verify that this payload is XDR-encoded and +# the stream can be received. +# +# Strategy: +# 1. Create a source dataset and a divergent clone. +# 2. Create a redaction bookmark on the source snapshot relative to the +# clone snapshot. +# 3. zfs send --redact sendfs@snap to a file. +# 4. verify_xdr_nvlist_encoding on the stream. +# 5. Verify that zfs receive succeeds. +# + +verify_runnable "both" + +sendfs="$POOL/xdr_redacted_full_src" +clonefs="$POOL/xdr_redacted_full_clone" +recvfs="$POOL2/xdr_redacted_full_recv" +stream="/$POOL/xdr_redacted_full.zsend" + +function cleanup +{ + datasetexists $sendfs && destroy_dataset $sendfs -R + datasetexists $recvfs && destroy_dataset $recvfs -R + rm -f $stream +} +log_onexit cleanup + +log_assert "BEGIN nvlist of a redacted send is XDR-encoded and receivable" + +log_must zfs create $sendfs +log_must dd if=/dev/urandom of=/$sendfs/f1 bs=128k count=8 status=none +log_must dd if=/dev/urandom of=/$sendfs/f2 bs=128k count=8 status=none +log_must zfs snapshot $sendfs@s0 + +log_must zfs clone $sendfs@s0 $clonefs +log_must dd if=/dev/urandom of=/$clonefs/f1 bs=128k count=8 conv=notrunc \ + status=none +log_must zfs snapshot $clonefs@s + +log_must zfs redact $sendfs@s0 redaction-bookmark $clonefs@s + +log_must eval "zfs send --redact redaction-bookmark $sendfs@s0 > $stream" +verify_xdr_nvlist_encoding $stream +log_must eval "zfs receive $recvfs < $stream" + +log_pass "BEGIN nvlist of a redacted send is XDR-encoded and receivable" diff --git a/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_redacted_received.ksh b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_redacted_received.ksh new file mode 100755 index 000000000000..a18b1f405942 --- /dev/null +++ b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_redacted_received.ksh @@ -0,0 +1,84 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# +# Copyright (c) 2026 by Garth Snyder. All rights reserved. +# + +. $STF_SUITE/tests/functional/send_xdr_encoding/send_xdr_encoding.kshlib + +# +# Description: +# Sending a snapshot from a previously-redacted dataset (one with the +# SPA_FEATURE_REDACTED_DATASETS feature active, e.g., one that was received +# from a redacted send) carries BEGINNV_REDACT_SNAPS in its DRR_BEGIN +# nvlist payload via a different code path than the producer-side --redact +# flag. Verify that this payload is XDR-encoded and that the stream can be +# received. +# +# Strategy: +# 1. Produce a redacted dataset on a receiver via a redacted full send. +# 2. zfs send the received-redacted snapshot to a new dataset. +# 3. Verify XDR encoding on the new stream. +# 4. Verify that a zfs receive of the new stream succeeds. +# + +verify_runnable "both" + +sendfs="$POOL/xdr_redacted_received_src" +clonefs="$POOL/xdr_redacted_received_clone" +midfs="$POOL2/xdr_redacted_received_mid" +recvfs="$POOL2/xdr_redacted_received_recv" +full_stream="/$POOL/xdr_redacted_received_full.zsend" +resend_stream="/$POOL/xdr_redacted_received_resend.zsend" + +function cleanup +{ + datasetexists $sendfs && destroy_dataset $sendfs -R + datasetexists $midfs && destroy_dataset $midfs -R + datasetexists $recvfs && destroy_dataset $recvfs -R + rm -f $full_stream $resend_stream +} +log_onexit cleanup + +log_assert "BEGIN nvlist of a send from a previously-redacted dataset is " \ + "XDR-encoded and receivable" + +log_must zfs create $sendfs +log_must dd if=/dev/urandom of=/$sendfs/f1 bs=128k count=8 status=none +log_must dd if=/dev/urandom of=/$sendfs/f2 bs=128k count=8 status=none +log_must zfs snapshot $sendfs@s0 + +log_must zfs clone $sendfs@s0 $clonefs +log_must dd if=/dev/urandom of=/$clonefs/f1 bs=128k count=8 conv=notrunc \ + status=none +log_must zfs snapshot $clonefs@s + +log_must zfs redact $sendfs@s0 redaction-bookmark $clonefs@s + +# Produce a previously-redacted dataset on the receiver. +log_must eval "zfs send --redact redaction-bookmark $sendfs@s0 > $full_stream" +log_must eval "zfs receive $midfs < $full_stream" + +# Send the received-redacted snapshot. This fires BEGINNV_REDACT_SNAPS via +# the SPA_FEATURE_REDACTED_DATASETS code path on to_ds. +log_must eval "zfs send $midfs@s0 > $resend_stream" +verify_xdr_nvlist_encoding $resend_stream +log_must eval "zfs receive $recvfs < $resend_stream" + +log_pass "BEGIN nvlist of a send from a previously-redacted dataset is " \ + "XDR-encoded and receivable" diff --git a/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_redacted_received_raw.ksh b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_redacted_received_raw.ksh new file mode 100755 index 000000000000..2efcba32b9f3 --- /dev/null +++ b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_redacted_received_raw.ksh @@ -0,0 +1,97 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# +# Copyright (c) 2026 by Garth Snyder. All rights reserved. +# + +. $STF_SUITE/tests/functional/send_xdr_encoding/send_xdr_encoding.kshlib + +# +# Description: +# zfs send explicitly disallows the source-side combination of -w and +# --redact. However, the same nvlist combination (BEGINNV_REDACT_SNAPS +# together with crypt_keydata) can still be reached by: +# +# 1. Sending a redacted (non-raw) stream from an unencrypted source. +# 2. Receiving it with receiver-side encryption. +# 3. Re-sending the now-encrypted-and-redacted dataset with -w. +# +# The final stream's DRR_BEGIN nvlist contains both the redact-snaps array +# (via the SPA_FEATURE_REDACTED_DATASETS code path on to_ds) and +# crypt_keydata (via DMU_BACKUP_FEATURE_RAW). Verify that this combined +# payload is XDR-encoded and that the stream can be received. +# +# Strategy: +# 1. Create an unencrypted source dataset with a redaction bookmark. +# 2. zfs send --redact sendfs@snap to a file (no -w). +# 3. zfs receive into a new dataset with -o encryption=on (receiver-side +# encryption). +# 4. zfs send -w the received dataset to a second stream file. +# 5. Verify that this second stream is XDR-encoded. +# 6. Verify that the second stream can be zfs received successfully. +# + +verify_runnable "both" + +sendfs="$POOL/xdr_redacted_received_raw_src" +clonefs="$POOL/xdr_redacted_received_raw_clone" +midfs="$POOL2/xdr_redacted_received_raw_mid" +recvfs="$POOL2/xdr_redacted_received_raw_recv" +keyfile="/$POOL/xdr_redacted_received_raw.key" +full_stream="/$POOL/xdr_redacted_received_raw_full.zsend" +resend_stream="/$POOL/xdr_redacted_received_raw_resend.zsend" + +function cleanup +{ + datasetexists $sendfs && destroy_dataset $sendfs -R + datasetexists $midfs && destroy_dataset $midfs -R + datasetexists $recvfs && destroy_dataset $recvfs -R + rm -f $keyfile $full_stream $resend_stream +} +log_onexit cleanup + +log_assert "BEGIN nvlist of a raw send of a received-redacted dataset is " \ + "XDR-encoded and receivable" + +log_must zfs create $sendfs +log_must dd if=/dev/urandom of=/$sendfs/f1 bs=128k count=8 status=none +log_must dd if=/dev/urandom of=/$sendfs/f2 bs=128k count=8 status=none +log_must zfs snapshot $sendfs@s0 + +log_must zfs clone $sendfs@s0 $clonefs +log_must dd if=/dev/urandom of=/$clonefs/f1 bs=128k count=8 conv=notrunc \ + status=none +log_must zfs snapshot $clonefs@s + +log_must zfs redact $sendfs@s0 redaction-bookmark $clonefs@s + +# Redacted send (non-raw) into a receiver that establishes its own encryption. +log_must eval "zfs send --redact redaction-bookmark $sendfs@s0 > $full_stream" +log_must eval "echo 'thisisapassphrase' > $keyfile" +log_must eval "zfs receive -o encryption=on -o keyformat=passphrase " \ + "-o keylocation=file://$keyfile $midfs < $full_stream" + +# Re-send the received stream as a raw (encrypted) stream. The DRR_BEGIN +# nvlist now carries both BEGINNV_REDACT_SNAPS data and crypt_keydata +# (DMU_BACKUP_FEATURE_RAW). +log_must eval "zfs send -w $midfs@s0 > $resend_stream" +verify_xdr_nvlist_encoding $resend_stream +log_must eval "zfs receive $recvfs < $resend_stream" + +log_pass "BEGIN nvlist of a raw send of a received-redacted dataset is " \ + "XDR-encoded and receivable" diff --git a/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_replication.ksh b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_replication.ksh new file mode 100755 index 000000000000..22d0bf20410f --- /dev/null +++ b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_replication.ksh @@ -0,0 +1,90 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# +# Copyright (c) 2026 by Garth Snyder. All rights reserved. +# + +. $STF_SUITE/tests/functional/send_xdr_encoding/send_xdr_encoding.kshlib + +# +# Description: +# A replication send (zfs send -R) may emit two distinct categories of +# DRR_BEGIN record: +# +# 1. A wrapper BEGIN of type DMU_COMPOUNDSTREAM, generated in libzfs +# (lib/libzfs/libzfs_sendrecv.c), whose nvlist describes the package +# stream. This BEGIN has always been XDR-encoded and is not affected +# by the kernel-side encoding changes introduced in PR #18372. +# +# 2. One inner BEGIN record per dataset whose contents are included, +# generated in the kernel (module/zfs/dmu_send.c). These are the BEGIN +# records whose encoding the kernel-side change consolidates to XDR. +# +# All other tests in this suite exercise category (2). This test exercises +# both categories together: it verifies that no BEGIN record produced +# anywhere on the userspace+kernel send path is encoded with NV_ENCODE_NATIVE, +# so a future regression in either layer would be caught. +# +# Strategy: +# 1. Create an unencrypted parent dataset and an encrypted child filesystem +# underneath it, with some data in each. The encrypted child is what +# causes the kernel-side inner BEGIN to actually carry an nvlist payload +# (crypt_keydata) rather than passing through silently. +# 2. Snapshot recursively. +# 3. zfs send -wR parent@snap to a file. The resulting stream contains a +# libzfs-generated wrapper BEGIN with its compound-stream nvlist plus +# one kernel-generated inner BEGIN per dataset; the child's inner BEGIN +# carries crypt_keydata. +# 4. Verify the encoding for the whole stream — this checks every BEGIN +# nvlist line that zstream dump emits, so it covers both the wrapper +# and the encrypted child's inner record. +# 5. Verify that the stream can be zfs received successfully. +# + +verify_runnable "both" + +sendfs="$POOL/xdr_replication_src" +childfs="$POOL/xdr_replication_src/child" +recvfs="$POOL2/xdr_replication_recv" +keyfile="/$POOL/xdr_replication.key" +stream="/$POOL/xdr_replication.zsend" + +function cleanup +{ + datasetexists $sendfs && destroy_dataset $sendfs -R + datasetexists $recvfs && destroy_dataset $recvfs -R + rm -f $keyfile $stream +} +log_onexit cleanup + +log_assert "BEGIN nvlists in a recursive replication stream (wrapper and inner) are XDR-encoded and receivable" + +log_must zfs create $sendfs +log_must eval "echo 'thisisapassphrase' > $keyfile" +log_must zfs create -o encryption=on -o keyformat=passphrase \ + -o keylocation=file://$keyfile $childfs +log_must dd if=/dev/urandom of=/$sendfs/f1 bs=128k count=4 status=none +log_must dd if=/dev/urandom of=/$childfs/f1 bs=128k count=4 status=none +log_must zfs snapshot -r $sendfs@s0 + +log_must eval "zfs send -wR $sendfs@s0 > $stream" +verify_xdr_nvlist_encoding $stream +log_must eval "zfs receive $recvfs < $stream" + +log_pass "BEGIN nvlists in a recursive replication stream (wrapper and inner) are XDR-encoded and receivable" + diff --git a/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_resume.ksh b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_resume.ksh new file mode 100755 index 000000000000..e98de4c47f44 --- /dev/null +++ b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_resume.ksh @@ -0,0 +1,73 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# +# Copyright (c) 2026 by Garth Snyder. All rights reserved. +# + +. $STF_SUITE/tests/functional/rsend/rsend.kshlib +. $STF_SUITE/tests/functional/send_xdr_encoding/send_xdr_encoding.kshlib + +# +# Description: +# A token-resumed send (zfs send -t ) carries BEGINNV_RESUME_OBJECT +# and BEGINNV_RESUME_OFFSET in its DRR_BEGIN nvlist payload. Verify that +# this payload is XDR-encoded and that the resumed stream can be received. +# +# Strategy: +# 1. Create a small dataset with one snapshot. +# 2. zfs send the snapshot to a file, truncate it, then attempt receive +# so that a resume token is left behind. +# 3. zfs send -t to produce the resumed stream. +# 4. Verify that the resumed stream is XDR-encoded. +# 5. Verify that zfs receive -s on the resumed stream is successful. +# + +verify_runnable "both" + +sendfs="$POOL/xdr_resume_src" +recvfs="$POOL2/xdr_resume_recv" +full_stream="/$POOL/xdr_resume_full.zsend" +resumed_stream="/$POOL/xdr_resume_resumed.zsend" + +function cleanup +{ + datasetexists $sendfs && destroy_dataset $sendfs -r + datasetexists $recvfs && destroy_dataset $recvfs -r + rm -f $full_stream $resumed_stream +} +log_onexit cleanup + +log_assert "BEGIN nvlist of a token-resumed send is XDR-encoded and receivable" + +log_must zfs create $sendfs +log_must dd if=/dev/urandom of=/$sendfs/f1 bs=128k count=8 status=none +log_must zfs snapshot $sendfs@s1 + +log_must eval "zfs send $sendfs@s1 > $full_stream" +mess_send_file $full_stream +log_mustnot eval "zfs receive -s $recvfs < $full_stream" + +token=$(get_prop receive_resume_token $recvfs) +[[ -n "$token" && "$token" != "-" ]] || \ + log_fail "no resume token left behind by partial receive" +log_must eval "zfs send -t $token > $resumed_stream" + +verify_xdr_nvlist_encoding $resumed_stream +log_must eval "zfs receive -s $recvfs < $resumed_stream" + +log_pass "BEGIN nvlist of a token-resumed send is XDR-encoded and receivable" diff --git a/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_resume_bookmark_raw.ksh b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_resume_bookmark_raw.ksh new file mode 100755 index 000000000000..6645315fcd7b --- /dev/null +++ b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_resume_bookmark_raw.ksh @@ -0,0 +1,103 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# +# Copyright (c) 2026 by Garth Snyder. All rights reserved. +# + +. $STF_SUITE/tests/functional/rsend/rsend.kshlib +. $STF_SUITE/tests/functional/send_xdr_encoding/send_xdr_encoding.kshlib + +# +# Description: +# The most populated DRR_BEGIN nvlist in the kernel: a token-resumed raw +# incremental from a redaction bookmark carries BEGINNV_REDACT_FROM_SNAPS, +# crypt_keydata, and BEGINNV_RESUME_{OBJECT,OFFSET}. Verify that this +# combined payload is XDR-encoded and the resumed stream can be received. +# +# Strategy: +# 1. Create an encrypted source with a redaction bookmark and a later +# snapshot, mirroring xdr_bookmark_raw. +# 2. Establish a raw base on the receiver. +# 3. zfs send -w -i sendfs#book sendfs@s1 to a file, truncate it, then +# attempt receive so that a resume token is left behind. +# 4. zfs send -t to produce the resumed stream. +# 5. Verify that the resumed stream is XDR-encoded. +# 6. Verify that zfs receive -s of the resumed stream is successful. +# + +verify_runnable "both" + +sendfs="$POOL/xdr_resume_bookmark_raw_src" +clonefs="$POOL/xdr_resume_bookmark_raw_clone" +recvfs="$POOL2/xdr_resume_bookmark_raw_recv" +keyfile="/$POOL/xdr_resume_bookmark_raw.key" +full_stream="/$POOL/xdr_resume_bookmark_raw_full.zsend" +incr_stream="/$POOL/xdr_resume_bookmark_raw_incr.zsend" +resumed_stream="/$POOL/xdr_resume_bookmark_raw_resumed.zsend" + +function cleanup +{ + datasetexists $sendfs && destroy_dataset $sendfs -R + datasetexists $recvfs && destroy_dataset $recvfs -R + rm -f $keyfile $full_stream $incr_stream $resumed_stream +} +log_onexit cleanup + +log_assert "BEGIN nvlist of a token-resumed raw incremental from a redaction " \ + "bookmark is XDR-encoded and receivable" + +log_must eval "echo 'thisisapassphrase' > $keyfile" +log_must zfs create -o encryption=on -o keyformat=passphrase \ + -o keylocation=file://$keyfile $sendfs + +log_must dd if=/dev/urandom of=/$sendfs/f1 bs=128k count=16 status=none +log_must dd if=/dev/urandom of=/$sendfs/f2 bs=128k count=16 status=none +log_must zfs snapshot $sendfs@s0 + +log_must zfs clone $sendfs@s0 $clonefs +log_must dd if=/dev/urandom of=/$clonefs/f1 bs=128k count=16 conv=notrunc \ + status=none +log_must zfs snapshot $clonefs@s + +log_must zfs redact $sendfs@s0 redaction-bookmark $clonefs@s + +# Take @s1 with no intervening write. See xdr_resume_bookmark_raw_with_write.ksh +# for a variant that includes a post-redact write; that variant exercises +# a known kernel-side issue (#18491) and may flake. +log_must zfs snapshot $sendfs@s1 + +# Establish a raw base on the receiver. +log_must eval "zfs send -w $sendfs@s0 > $full_stream" +log_must eval "zfs receive $recvfs < $full_stream" + +# Truncate-and-resume on the raw incremental from the redaction bookmark. +log_must eval "zfs send -w -i $sendfs#redaction-bookmark $sendfs@s1 > \ + $incr_stream" +mess_send_file $incr_stream +log_mustnot eval "zfs receive -s $recvfs < $incr_stream" + +token=$(get_prop receive_resume_token $recvfs) +[[ -n "$token" && "$token" != "-" ]] || \ + log_fail "no resume token left behind by partial receive" +log_must eval "zfs send -t $token > $resumed_stream" + +verify_xdr_nvlist_encoding $resumed_stream +log_must eval "zfs receive -s $recvfs < $resumed_stream" + +log_pass "BEGIN nvlist of a token-resumed raw incremental from a redaction " \ + "bookmark is XDR-encoded and receivable" diff --git a/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_resume_bookmark_raw_with_write.ksh b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_resume_bookmark_raw_with_write.ksh new file mode 100755 index 000000000000..6c0b6b5b4ec3 --- /dev/null +++ b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_resume_bookmark_raw_with_write.ksh @@ -0,0 +1,116 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# +# Copyright (c) 2026 by Garth Snyder. All rights reserved. +# + +. $STF_SUITE/tests/functional/rsend/rsend.kshlib +. $STF_SUITE/tests/functional/send_xdr_encoding/send_xdr_encoding.kshlib + +# +# Description: +# This is the post-redact-write variant of xdr_resume_bookmark_raw, +# separated out because of a known issue (#18491) that causes it to fail +# roughly 30% of the time. It's included here as a test for issue #18491 +# until the exact source of that problem can be pinned down more specifically. +# +# Known issue: openzfs/zfs#18491 +# +# On a freshly-created pool, `zfs send -w -i ds#book ds@snap` intermittently +# fails with EACCES whenever there is data-modifying activity between the +# `zfs redact` that created the bookmark and the subsequent send. This EACCES +# is surfaced to userspace as the misleading message "dataset key must be +# loaded," although the key remains loaded throughout. +# +# The reproducer script included in the issue report typically triggers the +# problem within about 10 iterations on a fresh pool. Disk-sync mitigations +# (zpool sync, with or without `-f`, with or without sleep, single or doubled, +# applied at any reasonable point) do not avert the problem. CI runs that +# include the test in this file reproduce the failure regularly (though +# intermittently) across multiple distributions. xdr_resume_bookmark_raw.ksh +# removes the post-redact write (which is not essential to the test) and +# therefore runs reliably. +# +# When this test fails, the failure marker is the libzfs warning +# "dataset key must be loaded" on stderr from the first `zfs send -w -i` +# line below (the one that produces the stream we then truncate), and a +# non-zero exit from that send. The test does not attempt to distinguish +# the known-issue failure from other possible failures. +# + +verify_runnable "both" + +sendfs="$POOL/xdr_resume_bookmark_raw_with_write_src" +clonefs="$POOL/xdr_resume_bookmark_raw_with_write_clone" +recvfs="$POOL2/xdr_resume_bookmark_raw_with_write_recv" +keyfile="/$POOL/xdr_resume_bookmark_raw_with_write.key" +full_stream="/$POOL/xdr_resume_bookmark_raw_with_write_full.zsend" +incr_stream="/$POOL/xdr_resume_bookmark_raw_with_write_incr.zsend" +resumed_stream="/$POOL/xdr_resume_bookmark_raw_with_write_resumed.zsend" + +function cleanup +{ + datasetexists $sendfs && destroy_dataset $sendfs -R + datasetexists $recvfs && destroy_dataset $recvfs -R + rm -f $keyfile $full_stream $incr_stream $resumed_stream +} +log_onexit cleanup + +log_assert "BEGIN nvlist of a token-resumed raw incremental from a redaction " \ + "bookmark, with a post-redact write, is XDR-encoded and receivable " \ + "(known to flake; see openzfs/zfs#18491)" + +log_must eval "echo 'thisisapassphrase' > $keyfile" +log_must zfs create -o encryption=on -o keyformat=passphrase \ + -o keylocation=file://$keyfile $sendfs + +log_must dd if=/dev/urandom of=/$sendfs/f1 bs=128k count=16 status=none +log_must zfs snapshot $sendfs@s0 + +log_must zfs clone $sendfs@s0 $clonefs +log_must dd if=/dev/urandom of=/$clonefs/f1 bs=128k count=16 conv=notrunc \ + status=none +log_must zfs snapshot $clonefs@s + +log_must zfs redact $sendfs@s0 redaction-bookmark $clonefs@s + +# Post-redact write: the trigger for openzfs/zfs#18491. +log_must dd if=/dev/urandom of=/$sendfs/f3 bs=128k count=16 status=none +log_must zfs snapshot $sendfs@s1 + +# Establish a raw base on the receiver. +log_must eval "zfs send -w $sendfs@s0 > $full_stream" +log_must eval "zfs receive $recvfs < $full_stream" + +# The next line is what races. On failure it exits with EACCES rendered +# as "dataset key must be loaded". +log_must eval "zfs send -w -i $sendfs#redaction-bookmark $sendfs@s1 > \ + $incr_stream" +mess_send_file $incr_stream +log_mustnot eval "zfs receive -s $recvfs < $incr_stream" + +token=$(get_prop receive_resume_token $recvfs) +[[ -n "$token" && "$token" != "-" ]] || \ + log_fail "no resume token left behind by partial receive" +log_must eval "zfs send -t $token > $resumed_stream" + +verify_xdr_nvlist_encoding $resumed_stream +log_must eval "zfs receive -s $recvfs < $resumed_stream" + +log_pass "BEGIN nvlist of a token-resumed raw incremental from a redaction " \ + "bookmark, with a post-redact write, is XDR-encoded and receivable" diff --git a/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_resume_raw.ksh b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_resume_raw.ksh new file mode 100755 index 000000000000..a96df10b9454 --- /dev/null +++ b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_resume_raw.ksh @@ -0,0 +1,79 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# +# Copyright (c) 2026 by Garth Snyder. All rights reserved. +# + +. $STF_SUITE/tests/functional/rsend/rsend.kshlib +. $STF_SUITE/tests/functional/send_xdr_encoding/send_xdr_encoding.kshlib + +# +# Description: +# A resumed raw send (zfs send -t for a raw stream of an encrypted +# dataset) carries both BEGINNV_RESUME_{OBJECT,OFFSET} and the "crypt_keydata" +# nested nvlist in its DRR_BEGIN nvlist payload. Verify that this combined +# payload is XDR-encoded and the resumed stream can be received. +# +# Strategy: +# 1. Create an encrypted dataset with one snapshot. +# 2. zfs send -w to a file, truncate it, then attempt to zfs receive the +# stream so that a resume token is left behind. +# 3. zfs send -t to produce the resumed raw stream. +# 4. Verify that the resumed stream is XDR-encoded. +# 5. Verify that zfs receive -s receives the resumed stream successfully. +# + +verify_runnable "both" + +sendfs="$POOL/xdr_resume_raw_src" +recvfs="$POOL2/xdr_resume_raw_recv" +keyfile="/$POOL/xdr_resume_raw.key" +full_stream="/$POOL/xdr_resume_raw_full.zsend" +resumed_stream="/$POOL/xdr_resume_raw_resumed.zsend" + +function cleanup +{ + datasetexists $sendfs && destroy_dataset $sendfs -r + datasetexists $recvfs && destroy_dataset $recvfs -r + rm -f $keyfile $full_stream $resumed_stream +} +log_onexit cleanup + +log_assert "BEGIN nvlist of a token-resumed raw send is XDR-encoded " \ + "and receivable" + +log_must eval "echo 'thisisapassphrase' > $keyfile" +log_must zfs create -o encryption=on -o keyformat=passphrase \ + -o keylocation=file://$keyfile $sendfs +log_must dd if=/dev/urandom of=/$sendfs/f1 bs=128k count=16 status=none +log_must zfs snapshot $sendfs@s1 + +log_must eval "zfs send -w $sendfs@s1 > $full_stream" +mess_send_file $full_stream +log_mustnot eval "zfs receive -s $recvfs < $full_stream" + +token=$(get_prop receive_resume_token $recvfs) +[[ -n "$token" && "$token" != "-" ]] || \ + log_fail "no resume token left behind by partial receive" +log_must eval "zfs send -t $token > $resumed_stream" + +verify_xdr_nvlist_encoding $resumed_stream +log_must eval "zfs receive -s $recvfs < $resumed_stream" + +log_pass "BEGIN nvlist of a token-resumed raw send is XDR-encoded " \ + "and receivable" diff --git a/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_resume_redacted.ksh b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_resume_redacted.ksh new file mode 100755 index 000000000000..6cee3e51a3dd --- /dev/null +++ b/tests/zfs-tests/tests/functional/send_xdr_encoding/xdr_resume_redacted.ksh @@ -0,0 +1,86 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# +# Copyright (c) 2026 by Garth Snyder. All rights reserved. +# + +. $STF_SUITE/tests/functional/rsend/rsend.kshlib +. $STF_SUITE/tests/functional/send_xdr_encoding/send_xdr_encoding.kshlib + +# +# Description: +# A resumed redacted send (zfs send -t for a redacted stream) +# carries both BEGINNV_REDACT_SNAPS and BEGINNV_RESUME_{OBJECT,OFFSET} in +# its DRR_BEGIN nvlist payload. Verify that this combined payload is +# XDR-encoded and the resumed stream can be received. +# +# Strategy: +# 1. Create a source dataset with a redaction bookmark. +# 2. zfs send --redact sendfs@snap to a file, truncate it, then +# attempt zfs receive so that a resume token is left behind. +# 3. zfs send -t to produce a resumed redacted stream. +# 4. Verify that the resumed stream is XDR-encoded. +# 5. Verify that zfs receive -s of the resumed stream is successful. +# + +verify_runnable "both" + +sendfs="$POOL/xdr_resume_redacted_src" +clonefs="$POOL/xdr_resume_redacted_clone" +recvfs="$POOL2/xdr_resume_redacted_recv" +full_stream="/$POOL/xdr_resume_redacted_full.zsend" +resumed_stream="/$POOL/xdr_resume_redacted_resumed.zsend" + +function cleanup +{ + datasetexists $sendfs && destroy_dataset $sendfs -R + datasetexists $recvfs && destroy_dataset $recvfs -R + rm -f $full_stream $resumed_stream +} +log_onexit cleanup + +log_assert "BEGIN nvlist of a token-resumed redacted send is XDR-encoded " \ + "and receivable" + +log_must zfs create $sendfs +log_must dd if=/dev/urandom of=/$sendfs/f1 bs=128k count=16 status=none +log_must dd if=/dev/urandom of=/$sendfs/f2 bs=128k count=16 status=none +log_must zfs snapshot $sendfs@s0 + +log_must zfs clone $sendfs@s0 $clonefs +log_must dd if=/dev/urandom of=/$clonefs/f1 bs=128k count=16 conv=notrunc \ + status=none +log_must zfs snapshot $clonefs@s + +log_must zfs redact $sendfs@s0 redaction-bookmark $clonefs@s + +log_must eval "zfs send --redact redaction-bookmark $sendfs@s0 > $full_stream" +mess_send_file $full_stream +log_mustnot eval "zfs receive -s $recvfs < $full_stream" + +token=$(get_prop receive_resume_token $recvfs) +[[ -n "$token" && "$token" != "-" ]] || \ + log_fail "no resume token left behind by partial receive" +log_must eval "zfs send -t $token > $resumed_stream" + +verify_xdr_nvlist_encoding $resumed_stream +log_must eval "zfs receive -s $recvfs < $resumed_stream" + +log_pass "BEGIN nvlist of a token-resumed redacted send is XDR-encoded " \ + "and receivable" +