]> git.proxmox.com Git - mirror_zfs.git/blobdiff - module/zfs/dmu_send.c
Don't panic on unencrypted block in encrypted dataset
[mirror_zfs.git] / module / zfs / dmu_send.c
index 07c2d6ef248a7518950235751f546ee84a884e96..37c68528bf95d18a8719cbc1f6f05837ba9caa03 100644 (file)
@@ -6,7 +6,7 @@
  * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
+ * or https://opensource.org/licenses/CDDL-1.0.
  * See the License for the specific language governing permissions
  * and limitations under the License.
  *
@@ -67,7 +67,7 @@
 #endif
 
 /* Set this tunable to TRUE to replace corrupt data with 0x2f5baddb10c */
-int zfs_send_corrupt_data = B_FALSE;
+static int zfs_send_corrupt_data = B_FALSE;
 /*
  * This tunable controls the amount of data (measured in bytes) that will be
  * prefetched by zfs send.  If the main thread is blocking on reads that haven't
@@ -75,7 +75,7 @@ int zfs_send_corrupt_data = B_FALSE;
  * thread is issuing new reads because the prefetches have fallen out of the
  * cache, this may need to be decreased.
  */
-int zfs_send_queue_length = SPA_MAXBLOCKSIZE;
+static uint_t zfs_send_queue_length = SPA_MAXBLOCKSIZE;
 /*
  * This tunable controls the length of the queues that zfs send worker threads
  * use to communicate.  If the send_main_thread is blocking on these queues,
@@ -83,7 +83,7 @@ int zfs_send_queue_length = SPA_MAXBLOCKSIZE;
  * at the start of a send as these threads consume all the available IO
  * resources, this variable may need to be decreased.
  */
-int zfs_send_no_prefetch_queue_length = 1024 * 1024;
+static uint_t zfs_send_no_prefetch_queue_length = 1024 * 1024;
 /*
  * These tunables control the fill fraction of the queues by zfs send.  The fill
  * fraction controls the frequency with which threads have to be cv_signaled.
@@ -91,19 +91,19 @@ int zfs_send_no_prefetch_queue_length = 1024 * 1024;
  * down.  If the queues empty before the signalled thread can catch up, then
  * these should be tuned up.
  */
-int zfs_send_queue_ff = 20;
-int zfs_send_no_prefetch_queue_ff = 20;
+static uint_t zfs_send_queue_ff = 20;
+static uint_t zfs_send_no_prefetch_queue_ff = 20;
 
 /*
  * Use this to override the recordsize calculation for fast zfs send estimates.
  */
-int zfs_override_estimate_recordsize = 0;
+static uint_t zfs_override_estimate_recordsize = 0;
 
 /* Set this tunable to FALSE to disable setting of DRR_FLAG_FREERECORDS */
-int zfs_send_set_freerecords_bit = B_TRUE;
+static const boolean_t zfs_send_set_freerecords_bit = B_TRUE;
 
 /* Set this tunable to FALSE is disable sending unmodified spill blocks. */
-int zfs_send_unmodified_spill_blocks = B_TRUE;
+static int zfs_send_unmodified_spill_blocks = B_TRUE;
 
 static inline boolean_t
 overflow_multiply(uint64_t a, uint64_t b, uint64_t *c)
@@ -165,6 +165,7 @@ struct send_range {
                        kmutex_t                lock;
                        kcondvar_t              cv;
                        boolean_t               io_outstanding;
+                       boolean_t               io_compressed;
                        int                     io_err;
                } data;
                struct srh {
@@ -378,7 +379,7 @@ dump_free(dmu_send_cookie_t *dscp, uint64_t object, uint64_t offset,
                }
        }
        /* create a FREE record and make it pending */
-       bzero(dscp->dsc_drr, sizeof (dmu_replay_record_t));
+       memset(dscp->dsc_drr, 0, sizeof (dmu_replay_record_t));
        dscp->dsc_drr->drr_type = DRR_FREE;
        drrf->drr_object = object;
        drrf->drr_offset = offset;
@@ -437,7 +438,7 @@ dump_redact(dmu_send_cookie_t *dscp, uint64_t object, uint64_t offset,
                }
        }
        /* create a REDACT record and make it pending */
-       bzero(dscp->dsc_drr, sizeof (dmu_replay_record_t));
+       memset(dscp->dsc_drr, 0, sizeof (dmu_replay_record_t));
        dscp->dsc_drr->drr_type = DRR_REDACT;
        drrr->drr_object = object;
        drrr->drr_offset = offset;
@@ -450,7 +451,8 @@ dump_redact(dmu_send_cookie_t *dscp, uint64_t object, uint64_t offset,
 
 static int
 dmu_dump_write(dmu_send_cookie_t *dscp, dmu_object_type_t type, uint64_t object,
-    uint64_t offset, int lsize, int psize, const blkptr_t *bp, void *data)
+    uint64_t offset, int lsize, int psize, const blkptr_t *bp,
+    boolean_t io_compressed, void *data)
 {
        uint64_t payload_size;
        boolean_t raw = (dscp->dsc_featureflags & DMU_BACKUP_FEATURE_RAW);
@@ -478,7 +480,7 @@ dmu_dump_write(dmu_send_cookie_t *dscp, dmu_object_type_t type, uint64_t object,
                dscp->dsc_pending_op = PENDING_NONE;
        }
        /* write a WRITE record */
-       bzero(dscp->dsc_drr, sizeof (dmu_replay_record_t));
+       memset(dscp->dsc_drr, 0, sizeof (dmu_replay_record_t));
        dscp->dsc_drr->drr_type = DRR_WRITE;
        drrw->drr_object = object;
        drrw->drr_type = type;
@@ -487,7 +489,11 @@ dmu_dump_write(dmu_send_cookie_t *dscp, dmu_object_type_t type, uint64_t object,
        drrw->drr_logical_size = lsize;
 
        /* only set the compression fields if the buf is compressed or raw */
-       if (raw || lsize != psize) {
+       boolean_t compressed =
+           (bp != NULL ? BP_GET_COMPRESS(bp) != ZIO_COMPRESS_OFF &&
+           io_compressed : lsize != psize);
+       if (raw || compressed) {
+               ASSERT(bp != NULL);
                ASSERT(raw || dscp->dsc_featureflags &
                    DMU_BACKUP_FEATURE_COMPRESSED);
                ASSERT(!BP_IS_EMBEDDED(bp));
@@ -566,7 +572,7 @@ dump_write_embedded(dmu_send_cookie_t *dscp, uint64_t object, uint64_t offset,
 
        ASSERT(BP_IS_EMBEDDED(bp));
 
-       bzero(dscp->dsc_drr, sizeof (dmu_replay_record_t));
+       memset(dscp->dsc_drr, 0, sizeof (dmu_replay_record_t));
        dscp->dsc_drr->drr_type = DRR_WRITE_EMBEDDED;
        drrw->drr_object = object;
        drrw->drr_offset = offset;
@@ -579,7 +585,13 @@ dump_write_embedded(dmu_send_cookie_t *dscp, uint64_t object, uint64_t offset,
 
        decode_embedded_bp_compressed(bp, buf);
 
-       if (dump_record(dscp, buf, P2ROUNDUP(drrw->drr_psize, 8)) != 0)
+       uint32_t psize = drrw->drr_psize;
+       uint32_t rsize = P2ROUNDUP(psize, 8);
+
+       if (psize != rsize)
+               memset(buf + psize, 0, rsize - psize);
+
+       if (dump_record(dscp, buf, rsize) != 0)
                return (SET_ERROR(EINTR));
        return (0);
 }
@@ -599,7 +611,7 @@ dump_spill(dmu_send_cookie_t *dscp, const blkptr_t *bp, uint64_t object,
        }
 
        /* write a SPILL record */
-       bzero(dscp->dsc_drr, sizeof (dmu_replay_record_t));
+       memset(dscp->dsc_drr, 0, sizeof (dmu_replay_record_t));
        dscp->dsc_drr->drr_type = DRR_SPILL;
        drrs->drr_object = object;
        drrs->drr_length = blksz;
@@ -681,7 +693,7 @@ dump_freeobjects(dmu_send_cookie_t *dscp, uint64_t firstobj, uint64_t numobjs)
        }
 
        /* write a FREEOBJECTS record */
-       bzero(dscp->dsc_drr, sizeof (dmu_replay_record_t));
+       memset(dscp->dsc_drr, 0, sizeof (dmu_replay_record_t));
        dscp->dsc_drr->drr_type = DRR_FREEOBJECTS;
        drrfo->drr_firstobj = firstobj;
        drrfo->drr_numobjs = numobjs;
@@ -722,7 +734,7 @@ dump_dnode(dmu_send_cookie_t *dscp, const blkptr_t *bp, uint64_t object,
        }
 
        /* write an OBJECT record */
-       bzero(dscp->dsc_drr, sizeof (dmu_replay_record_t));
+       memset(dscp->dsc_drr, 0, sizeof (dmu_replay_record_t));
        dscp->dsc_drr->drr_type = DRR_OBJECT;
        drro->drr_object = object;
        drro->drr_type = dnp->dn_type;
@@ -758,6 +770,8 @@ dump_dnode(dmu_send_cookie_t *dscp, const blkptr_t *bp, uint64_t object,
                 * to send it.
                 */
                if (bonuslen != 0) {
+                       if (drro->drr_bonuslen > DN_MAX_BONUS_LEN(dnp))
+                               return (SET_ERROR(EINVAL));
                        drro->drr_raw_bonuslen = DN_MAX_BONUS_LEN(dnp);
                        bonuslen = drro->drr_raw_bonuslen;
                }
@@ -794,7 +808,7 @@ dump_dnode(dmu_send_cookie_t *dscp, const blkptr_t *bp, uint64_t object,
                struct send_range record;
                blkptr_t *bp = DN_SPILL_BLKPTR(dnp);
 
-               bzero(&record, sizeof (struct send_range));
+               memset(&record, 0, sizeof (struct send_range));
                record.type = DATA;
                record.object = object;
                record.eos_marker = B_FALSE;
@@ -834,7 +848,7 @@ dump_object_range(dmu_send_cookie_t *dscp, const blkptr_t *bp,
                dscp->dsc_pending_op = PENDING_NONE;
        }
 
-       bzero(dscp->dsc_drr, sizeof (dmu_replay_record_t));
+       memset(dscp->dsc_drr, 0, sizeof (dmu_replay_record_t));
        dscp->dsc_drr->drr_type = DRR_OBJECT_RANGE;
        drror->drr_firstobj = firstobj;
        drror->drr_numslots = numslots;
@@ -927,7 +941,7 @@ do_dump(dmu_send_cookie_t *dscp, struct send_range *range)
                ASSERT3U(range->start_blkid + 1, ==, range->end_blkid);
                if (BP_GET_TYPE(bp) == DMU_OT_SA) {
                        arc_flags_t aflags = ARC_FLAG_WAIT;
-                       enum zio_flag zioflags = ZIO_FLAG_CANFAIL;
+                       zio_flag_t zioflags = ZIO_FLAG_CANFAIL;
 
                        if (dscp->dsc_featureflags & DMU_BACKUP_FEATURE_RAW) {
                                ASSERT(BP_IS_PROTECTED(bp));
@@ -1014,7 +1028,8 @@ do_dump(dmu_send_cookie_t *dscp, struct send_range *range)
                                int n = MIN(srdp->datablksz,
                                    SPA_OLD_MAXBLOCKSIZE);
                                err = dmu_dump_write(dscp, srdp->obj_type,
-                                   range->object, offset, n, n, NULL, data);
+                                   range->object, offset, n, n, NULL, B_FALSE,
+                                   data);
                                offset += n;
                                /*
                                 * When doing dry run, data==NULL is used as a
@@ -1028,7 +1043,8 @@ do_dump(dmu_send_cookie_t *dscp, struct send_range *range)
                } else {
                        err = dmu_dump_write(dscp, srdp->obj_type,
                            range->object, offset,
-                           srdp->datablksz, srdp->datasz, bp, data);
+                           srdp->datablksz, srdp->datasz, bp,
+                           srdp->io_compressed, data);
                }
                return (err);
        }
@@ -1081,6 +1097,7 @@ range_alloc(enum type type, uint64_t object, uint64_t start_blkid,
                cv_init(&range->sru.data.cv, NULL, CV_DEFAULT, NULL);
                range->sru.data.io_outstanding = 0;
                range->sru.data.io_err = 0;
+               range->sru.data.io_compressed = B_FALSE;
        }
        return (range);
 }
@@ -1089,11 +1106,11 @@ range_alloc(enum type type, uint64_t object, uint64_t start_blkid,
  * This is the callback function to traverse_dataset that acts as a worker
  * thread for dmu_send_impl.
  */
-/*ARGSUSED*/
 static int
 send_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
     const zbookmark_phys_t *zb, const struct dnode_phys *dnp, void *arg)
 {
+       (void) zilog;
        struct send_thread_arg *sta = arg;
        struct send_range *record;
 
@@ -1106,9 +1123,7 @@ send_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
         */
        if (sta->os->os_encrypted &&
            !BP_IS_HOLE(bp) && !BP_USES_CRYPT(bp)) {
-               spa_log_error(spa, zb);
-               zfs_panic_recover("unencrypted block in encrypted "
-                   "object set %llu", dmu_objset_id(sta->os));
+               spa_log_error(spa, zb, &bp->blk_birth);
                return (SET_ERROR(EIO));
        }
 
@@ -1126,7 +1141,7 @@ send_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
                record->sru.object.bp = *bp;
                size_t size  = sizeof (*dnp) * (dnp->dn_extra_slots + 1);
                record->sru.object.dnp = kmem_alloc(size, KM_SLEEP);
-               bcopy(dnp, record->sru.object.dnp, size);
+               memcpy(record->sru.object.dnp, dnp, size);
                bqueue_enqueue(&sta->q, record, sizeof (*record));
                return (0);
        }
@@ -1224,7 +1239,7 @@ redact_list_cb(redact_block_phys_t *rb, void *arg)
  * error code of the thread in case something goes wrong, and pushes the End of
  * Stream record when the traverse_dataset call has finished.
  */
-static void
+static __attribute__((noreturn)) void
 send_traverse_thread(void *arg)
 {
        struct send_thread_arg *st_arg = arg;
@@ -1314,7 +1329,7 @@ get_next_range(bqueue_t *bq, struct send_range *prev)
        return (next);
 }
 
-static void
+static __attribute__((noreturn)) void
 redact_list_thread(void *arg)
 {
        struct redact_list_thread_arg *rlt_arg = arg;
@@ -1509,7 +1524,7 @@ find_next_range(struct send_range **ranges, bqueue_t **qs, uint64_t *out_mask)
  * data from the redact_list_thread and use that to determine which blocks
  * should be redacted.
  */
-static void
+static __attribute__((noreturn)) void
 send_merge_thread(void *arg)
 {
        struct send_merge_thread_arg *smt_arg = arg;
@@ -1576,8 +1591,6 @@ send_merge_thread(void *arg)
                }
                range_free(front_ranges[i]);
        }
-       if (range == NULL)
-               range = kmem_zalloc(sizeof (*range), KM_SLEEP);
        range->eos_marker = B_TRUE;
        bqueue_enqueue_flush(&smt_arg->q, range, 1);
        spl_fstrans_unmark(cookie);
@@ -1644,12 +1657,15 @@ issue_data_read(struct send_reader_thread_arg *srta, struct send_range *range)
            !split_large_blocks && !BP_SHOULD_BYTESWAP(bp) &&
            !BP_IS_EMBEDDED(bp) && !DMU_OT_IS_METADATA(BP_GET_TYPE(bp));
 
-       enum zio_flag zioflags = ZIO_FLAG_CANFAIL;
+       zio_flag_t zioflags = ZIO_FLAG_CANFAIL;
 
-       if (srta->featureflags & DMU_BACKUP_FEATURE_RAW)
+       if (srta->featureflags & DMU_BACKUP_FEATURE_RAW) {
                zioflags |= ZIO_FLAG_RAW;
-       else if (request_compressed)
+               srdp->io_compressed = B_TRUE;
+       } else if (request_compressed) {
                zioflags |= ZIO_FLAG_RAW_COMPRESS;
+               srdp->io_compressed = B_TRUE;
+       }
 
        srdp->datasz = (zioflags & ZIO_FLAG_RAW_COMPRESS) ?
            BP_GET_PSIZE(bp) : BP_GET_LSIZE(bp);
@@ -1701,8 +1717,10 @@ enqueue_range(struct send_reader_thread_arg *srta, bqueue_t *q, dnode_t *dn,
        struct send_range *range = range_alloc(range_type, dn->dn_object,
            blkid, blkid + count, B_FALSE);
 
-       if (blkid == DMU_SPILL_BLKID)
+       if (blkid == DMU_SPILL_BLKID) {
+               ASSERT3P(bp, !=, NULL);
                ASSERT3U(BP_GET_TYPE(bp), ==, DMU_OT_SA);
+       }
 
        switch (range_type) {
        case HOLE:
@@ -1731,7 +1749,7 @@ enqueue_range(struct send_reader_thread_arg *srta, bqueue_t *q, dnode_t *dn,
  * some indirect blocks can be discarded because they're not holes. Second,
  * it issues prefetches for the data we need to send.
  */
-static void
+static __attribute__((noreturn)) void
 send_reader_thread(void *arg)
 {
        struct send_reader_thread_arg *srta = arg;
@@ -1823,8 +1841,7 @@ send_reader_thread(void *arg)
                                continue;
                        }
                        uint64_t file_max =
-                           (dn->dn_maxblkid < range->end_blkid ?
-                           dn->dn_maxblkid : range->end_blkid);
+                           MIN(dn->dn_maxblkid, range->end_blkid);
                        /*
                         * The object exists, so we need to try to find the
                         * blkptr for each block in the range we're processing.
@@ -1900,7 +1917,7 @@ send_reader_thread(void *arg)
 
 struct dmu_send_params {
        /* Pool args */
-       void *tag; // Tag that dp was held with, will be used to release dp.
+       const void *tag; // Tag dp was held with, will be used to release dp.
        dsl_pool_t *dp;
        /* To snapshot args */
        const char *tosnap;
@@ -1936,7 +1953,7 @@ setup_featureflags(struct dmu_send_params *dspp, objset_t *os,
 {
        dsl_dataset_t *to_ds = dspp->to_ds;
        dsl_pool_t *dp = dspp->dp;
-#ifdef _KERNEL
+
        if (dmu_objset_type(os) == DMU_OST_ZFS) {
                uint64_t version;
                if (zfs_get_zplprop(os, ZFS_PROP_VERSION, &version) != 0)
@@ -1945,7 +1962,6 @@ setup_featureflags(struct dmu_send_params *dspp, objset_t *os,
                if (version >= ZPL_VERSION_SA)
                        *featureflags |= DMU_BACKUP_FEATURE_SA_SPILL;
        }
-#endif
 
        /* raw sends imply large_block_ok */
        if ((dspp->rawok || dspp->large_block_ok) &&
@@ -2054,6 +2070,8 @@ setup_to_thread(struct send_thread_arg *to_arg, objset_t *to_os,
        to_arg->flags = TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA;
        if (rawok)
                to_arg->flags |= TRAVERSE_NO_DECRYPT;
+       if (zfs_send_corrupt_data)
+               to_arg->flags |= TRAVERSE_HARD;
        to_arg->num_blocks_visited = &dssp->dss_blocks;
        (void) thread_create(NULL, 0, send_traverse_thread, to_arg, 0,
            curproc, TS_RUN, minclsyspri);
@@ -2142,6 +2160,7 @@ setup_resume_points(struct dmu_send_params *dspp,
     struct send_merge_thread_arg *smt_arg, boolean_t resuming, objset_t *os,
     redaction_list_t *redact_rl, nvlist_t *nvl)
 {
+       (void) smt_arg;
        dsl_dataset_t *to_ds = dspp->to_ds;
        int err = 0;
 
@@ -2346,7 +2365,7 @@ dmu_send_impl(struct dmu_send_params *dspp)
        dsl_dataset_t *to_ds = dspp->to_ds;
        zfs_bookmark_phys_t *ancestor_zb = &dspp->ancestor_zb;
        dsl_pool_t *dp = dspp->dp;
-       void *tag = dspp->tag;
+       const void *tag = dspp->tag;
 
        err = dmu_objset_from_ds(to_ds, &os);
        if (err != 0) {
@@ -2495,8 +2514,7 @@ dmu_send_impl(struct dmu_send_params *dspp)
        }
 
        if (featureflags & DMU_BACKUP_FEATURE_RAW) {
-               uint64_t ivset_guid = (ancestor_zb != NULL) ?
-                   ancestor_zb->zbm_ivset_guid : 0;
+               uint64_t ivset_guid = ancestor_zb->zbm_ivset_guid;
                nvlist_t *keynvl = NULL;
                ASSERT(os->os_encrypted);
 
@@ -2581,7 +2599,7 @@ dmu_send_impl(struct dmu_send_params *dspp)
         * the receive side that the stream is incomplete.
         */
        if (!dspp->savedok) {
-               bzero(drr, sizeof (dmu_replay_record_t));
+               memset(drr, 0, sizeof (dmu_replay_record_t));
                drr->drr_type = DRR_END;
                drr->drr_u.drr_end.drr_checksum = dsc.dsc_zc;
                drr->drr_u.drr_end.drr_toguid = dsc.dsc_toguid;
@@ -2626,7 +2644,7 @@ dmu_send_obj(const char *pool, uint64_t tosnap, uint64_t fromsnap,
 {
        int err;
        dsl_dataset_t *fromds;
-       ds_hold_flags_t dsflags = (rawok) ? 0 : DS_HOLD_FLAG_DECRYPT;
+       ds_hold_flags_t dsflags;
        struct dmu_send_params dspp = {0};
        dspp.embedok = embedok;
        dspp.large_block_ok = large_block_ok;
@@ -2638,6 +2656,7 @@ dmu_send_obj(const char *pool, uint64_t tosnap, uint64_t fromsnap,
        dspp.rawok = rawok;
        dspp.savedok = savedok;
 
+       dsflags = (rawok) ? DS_HOLD_FLAG_NONE : DS_HOLD_FLAG_DECRYPT;
        err = dsl_pool_hold(pool, FTAG, &dspp.dp);
        if (err != 0)
                return (err);
@@ -2681,21 +2700,28 @@ dmu_send_obj(const char *pool, uint64_t tosnap, uint64_t fromsnap,
                        uint64_t size = dspp.numfromredactsnaps *
                            sizeof (uint64_t);
                        dspp.fromredactsnaps = kmem_zalloc(size, KM_SLEEP);
-                       bcopy(fromredact, dspp.fromredactsnaps, size);
+                       memcpy(dspp.fromredactsnaps, fromredact, size);
                }
 
-               if (!dsl_dataset_is_before(dspp.to_ds, fromds, 0)) {
+               boolean_t is_before =
+                   dsl_dataset_is_before(dspp.to_ds, fromds, 0);
+               dspp.is_clone = (dspp.to_ds->ds_dir !=
+                   fromds->ds_dir);
+               dsl_dataset_rele(fromds, FTAG);
+               if (!is_before) {
+                       dsl_pool_rele(dspp.dp, FTAG);
                        err = SET_ERROR(EXDEV);
                } else {
-                       dspp.is_clone = (dspp.to_ds->ds_dir !=
-                           fromds->ds_dir);
-                       dsl_dataset_rele(fromds, FTAG);
                        err = dmu_send_impl(&dspp);
                }
        } else {
                dspp.numfromredactsnaps = NUM_SNAPS_NOT_REDACTED;
                err = dmu_send_impl(&dspp);
        }
+       if (dspp.fromredactsnaps)
+               kmem_free(dspp.fromredactsnaps,
+                   dspp.numfromredactsnaps * sizeof (uint64_t));
+
        dsl_dataset_rele(dspp.to_ds, FTAG);
        return (err);
 }
@@ -2708,12 +2734,13 @@ dmu_send(const char *tosnap, const char *fromsnap, boolean_t embedok,
     dmu_send_outparams_t *dsop)
 {
        int err = 0;
-       ds_hold_flags_t dsflags = (rawok) ? 0 : DS_HOLD_FLAG_DECRYPT;
+       ds_hold_flags_t dsflags;
        boolean_t owned = B_FALSE;
        dsl_dataset_t *fromds = NULL;
        zfs_bookmark_phys_t book = {0};
        struct dmu_send_params dspp = {0};
 
+       dsflags = (rawok) ? DS_HOLD_FLAG_NONE : DS_HOLD_FLAG_DECRYPT;
        dspp.tosnap = tosnap;
        dspp.embedok = embedok;
        dspp.large_block_ok = large_block_ok;
@@ -2763,6 +2790,7 @@ dmu_send(const char *tosnap, const char *fromsnap, boolean_t embedok,
                        }
 
                        if (err == 0) {
+                               owned = B_TRUE;
                                err = zap_lookup(dspp.dp->dp_meta_objset,
                                    dspp.to_ds->ds_object,
                                    DS_FIELD_RESUME_TOGUID, 8, 1,
@@ -2776,21 +2804,24 @@ dmu_send(const char *tosnap, const char *fromsnap, boolean_t embedok,
                                    sizeof (dspp.saved_toname),
                                    dspp.saved_toname);
                        }
-                       if (err != 0)
+                       /* Only disown if there was an error in the lookups */
+                       if (owned && (err != 0))
                                dsl_dataset_disown(dspp.to_ds, dsflags, FTAG);
 
                        kmem_strfree(name);
                } else {
                        err = dsl_dataset_own(dspp.dp, tosnap, dsflags,
                            FTAG, &dspp.to_ds);
+                       if (err == 0)
+                               owned = B_TRUE;
                }
-               owned = B_TRUE;
        } else {
                err = dsl_dataset_hold_flags(dspp.dp, tosnap, dsflags, FTAG,
                    &dspp.to_ds);
        }
 
        if (err != 0) {
+               /* Note: dsl dataset is not owned at this point */
                dsl_pool_rele(dspp.dp, FTAG);
                return (err);
        }
@@ -2862,7 +2893,7 @@ dmu_send(const char *tosnap, const char *fromsnap, boolean_t embedok,
                                            sizeof (uint64_t);
                                        dspp.fromredactsnaps = kmem_zalloc(size,
                                            KM_SLEEP);
-                                       bcopy(fromredact, dspp.fromredactsnaps,
+                                       memcpy(dspp.fromredactsnaps, fromredact,
                                            size);
                                }
                                if (!dsl_dataset_is_before(dspp.to_ds, fromds,
@@ -2903,6 +2934,10 @@ dmu_send(const char *tosnap, const char *fromsnap, boolean_t embedok,
                        /* dmu_send_impl will call dsl_pool_rele for us. */
                        err = dmu_send_impl(&dspp);
                } else {
+                       if (dspp.fromredactsnaps)
+                               kmem_free(dspp.fromredactsnaps,
+                                   dspp.numfromredactsnaps *
+                                   sizeof (uint64_t));
                        dsl_pool_rele(dspp.dp, FTAG);
                }
        } else {
@@ -2995,7 +3030,7 @@ dmu_send_estimate_fast(dsl_dataset_t *origds, dsl_dataset_t *fromds,
 
                dsl_dataset_name(origds, dsname);
                (void) strcat(dsname, "/");
-               (void) strcat(dsname, recv_clone_name);
+               (void) strlcat(dsname, recv_clone_name, sizeof (dsname));
 
                err = dsl_dataset_hold(origds->ds_dir->dd_pool,
                    dsname, FTAG, &ds);
@@ -3065,25 +3100,23 @@ out:
        return (err);
 }
 
-/* BEGIN CSTYLED */
 ZFS_MODULE_PARAM(zfs_send, zfs_send_, corrupt_data, INT, ZMOD_RW,
        "Allow sending corrupt data");
 
-ZFS_MODULE_PARAM(zfs_send, zfs_send_, queue_length, INT, ZMOD_RW,
+ZFS_MODULE_PARAM(zfs_send, zfs_send_, queue_length, UINT, ZMOD_RW,
        "Maximum send queue length");
 
 ZFS_MODULE_PARAM(zfs_send, zfs_send_, unmodified_spill_blocks, INT, ZMOD_RW,
        "Send unmodified spill blocks");
 
-ZFS_MODULE_PARAM(zfs_send, zfs_send_, no_prefetch_queue_length, INT, ZMOD_RW,
+ZFS_MODULE_PARAM(zfs_send, zfs_send_, no_prefetch_queue_length, UINT, ZMOD_RW,
        "Maximum send queue length for non-prefetch queues");
 
-ZFS_MODULE_PARAM(zfs_send, zfs_send_, queue_ff, INT, ZMOD_RW,
+ZFS_MODULE_PARAM(zfs_send, zfs_send_, queue_ff, UINT, ZMOD_RW,
        "Send queue fill fraction");
 
-ZFS_MODULE_PARAM(zfs_send, zfs_send_, no_prefetch_queue_ff, INT, ZMOD_RW,
+ZFS_MODULE_PARAM(zfs_send, zfs_send_, no_prefetch_queue_ff, UINT, ZMOD_RW,
        "Send queue fill fraction for non-prefetch queues");
 
-ZFS_MODULE_PARAM(zfs_send, zfs_, override_estimate_recordsize, INT, ZMOD_RW,
+ZFS_MODULE_PARAM(zfs_send, zfs_, override_estimate_recordsize, UINT, ZMOD_RW,
        "Override block size estimate with fixed size");
-/* END CSTYLED */