Skip to content

Commit

Permalink
Fix noop receive of raw send stream
Browse files Browse the repository at this point in the history
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.

Fixes: #9173

Signed-off-by: Tom Caputi <[email protected]>
  • Loading branch information
Tom Caputi committed Sep 5, 2019
1 parent 65a91b1 commit 4d5f236
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 6 deletions.
39 changes: 33 additions & 6 deletions lib/libzfs/libzfs_sendrecv.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -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);
Expand All @@ -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) {
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"

0 comments on commit 4d5f236

Please sign in to comment.