]> granicus.if.org Git - zfs/commitdiff
Fix noop receive of raw send stream
authorTom Caputi <tcaputi@datto.com>
Thu, 5 Sep 2019 23:22:05 +0000 (19:22 -0400)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Thu, 5 Sep 2019 23:22:05 +0000 (16:22 -0700)
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 <mail@gmelikov.ru>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Paul Dagnelie <pcd@delphix.com>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #9221
Closes #9173

lib/libzfs/libzfs_sendrecv.c
tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_raw.ksh

index e63404b8ad3ef7d4f6e85bf4b9327bebc617b023..33d3eb6a5345a14ea59daa739368ca87b68b509b 100644 (file)
@@ -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;
        }
index 7d5606acea09c86e9a264eeeba2fd1348fd59b77..9740caf72508811853602ddd270979b5459d5030 100755 (executable)
@@ -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"