aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Caputi <[email protected]>2019-09-05 19:22:05 -0400
committerBrian Behlendorf <[email protected]>2019-09-05 16:22:05 -0700
commit870e7a52c105f26ef4254b90230d396f4ce39ea7 (patch)
tree9fafd980d77c6a6e0801108316656536fb315f7e
parent8e2c502cf3f54201a75e5b8ea983358f2b9939f9 (diff)
Fix noop receive of raw send stream
Currently, the noop receive code fails to work with raw send streams and resuming send streams. This happens because zfs_receive_impl() reads the DRR_BEGIN payload without reading the payload itself. Normally, the kernel expects to read this itself, but in this case the recv_skip() code runs instead and it is not prepared to handle the stream being left at any place other than the beginning of a record. This patch resolves this issue by manually reading the DRR_BEGIN payload in the dry-run case. This patch also includes a number of small fixups in this code path. Reviewed-by: George Melikov <[email protected]> Reviewed-by: Brian Behlendorf <[email protected]> Reviewed-by: Paul Dagnelie <[email protected]> Signed-off-by: Tom Caputi <[email protected]> Closes #9221 Closes #9173
-rw-r--r--lib/libzfs/libzfs_sendrecv.c39
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_raw.ksh3
2 files changed, 36 insertions, 6 deletions
diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c
index e63404b8a..33d3eb6a5 100644
--- a/lib/libzfs/libzfs_sendrecv.c
+++ b/lib/libzfs/libzfs_sendrecv.c
@@ -4042,10 +4042,11 @@ recv_skip(libzfs_handle_t *hdl, int fd, boolean_t byteswap)
{
dmu_replay_record_t *drr;
void *buf = zfs_alloc(hdl, SPA_MAXBLOCKSIZE);
+ uint64_t payload_size;
char errbuf[1024];
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot receive:"));
+ "cannot receive"));
/* XXX would be great to use lseek if possible... */
drr = buf;
@@ -4072,9 +4073,14 @@ recv_skip(libzfs_handle_t *hdl, int fd, boolean_t byteswap)
drr->drr_u.drr_object.drr_bonuslen =
BSWAP_32(drr->drr_u.drr_object.
drr_bonuslen);
+ drr->drr_u.drr_object.drr_raw_bonuslen =
+ BSWAP_32(drr->drr_u.drr_object.
+ drr_raw_bonuslen);
}
- (void) recv_read(hdl, fd, buf,
- P2ROUNDUP(drr->drr_u.drr_object.drr_bonuslen, 8),
+
+ payload_size =
+ DRR_OBJECT_PAYLOAD_SIZE(&drr->drr_u.drr_object);
+ (void) recv_read(hdl, fd, buf, payload_size,
B_FALSE, NULL);
break;
@@ -4087,7 +4093,7 @@ recv_skip(libzfs_handle_t *hdl, int fd, boolean_t byteswap)
BSWAP_64(
drr->drr_u.drr_write.drr_compressed_size);
}
- uint64_t payload_size =
+ payload_size =
DRR_WRITE_PAYLOAD_SIZE(&drr->drr_u.drr_write);
(void) recv_read(hdl, fd, buf,
payload_size, B_FALSE, NULL);
@@ -4096,9 +4102,15 @@ recv_skip(libzfs_handle_t *hdl, int fd, boolean_t byteswap)
if (byteswap) {
drr->drr_u.drr_spill.drr_length =
BSWAP_64(drr->drr_u.drr_spill.drr_length);
+ drr->drr_u.drr_spill.drr_compressed_size =
+ BSWAP_64(drr->drr_u.drr_spill.
+ drr_compressed_size);
}
- (void) recv_read(hdl, fd, buf,
- drr->drr_u.drr_spill.drr_length, B_FALSE, NULL);
+
+ payload_size =
+ DRR_SPILL_PAYLOAD_SIZE(&drr->drr_u.drr_spill);
+ (void) recv_read(hdl, fd, buf, payload_size,
+ B_FALSE, NULL);
break;
case DRR_WRITE_EMBEDDED:
if (byteswap) {
@@ -4841,6 +4853,21 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
}
if (flags->dryrun) {
+ void *buf = zfs_alloc(hdl, SPA_MAXBLOCKSIZE);
+
+ /*
+ * We have read the DRR_BEGIN record, but we have
+ * not yet read the payload. For non-dryrun sends
+ * this will be done by the kernel, so we must
+ * emulate that here, before attempting to read
+ * more records.
+ */
+ err = recv_read(hdl, infd, buf, drr->drr_payloadlen,
+ flags->byteswap, NULL);
+ free(buf);
+ if (err != 0)
+ goto out;
+
err = recv_skip(hdl, infd, flags->byteswap);
goto out;
}
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_raw.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_raw.ksh
index 7d5606ace..9740caf72 100755
--- a/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_raw.ksh
+++ b/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_raw.ksh
@@ -36,6 +36,7 @@
# 9. Verify the key is unavailable
# 10. Attempt to load the key and mount the dataset
# 11. Verify the checksum of the file is the same as the original
+# 12. Verify 'zfs receive -n' works with the raw stream
#
verify_runnable "both"
@@ -88,4 +89,6 @@ typeset cksum2=$(md5digest /$TESTPOOL/$TESTFS1/c1/$TESTFILE0)
[[ "$cksum2" == "$checksum" ]] || \
log_fail "Checksums differ ($cksum2 != $checksum)"
+log_must eval "zfs send -w $snap | zfs receive -n $TESTPOOL/$TESTFS3"
+
log_pass "ZFS can receive streams from raw sends"