aboutsummaryrefslogtreecommitdiffstats
path: root/module/zfs/dmu_send.c
diff options
context:
space:
mode:
authorTom Caputi <[email protected]>2018-02-27 12:04:05 -0500
committerBrian Behlendorf <[email protected]>2018-02-27 09:04:05 -0800
commit095495e0081c16a18641fc60e7c9872123a4f83e (patch)
tree6bfafa8d3c800a875fb042df43c08922b9cb8747 /module/zfs/dmu_send.c
parent8b5814393f43d7eb7e4319120529d957a5f7bf8e (diff)
Raw DRR_OBJECT records must write raw data
b1d21733 made it possible for empty metadnode blocks to be compressed to a hole, fixing a bug that would cause invalid metadnode MACs when a send stream attempted to free objects and allowing the blocks to be reclaimed when they were no longer needed. However, this patch also introduced a race condition; if a txg sync occurred after a DRR_OBJECT_RANGE record was received but before any objects were added, the metadnode block would be compressed to a hole and lose all of its encryption parameters. This would cause subsequent DRR_OBJECT records to fail when they attempted to write their data into an unencrypted block. This patch defers the DRR_OBJECT_RANGE handling to receive_object() so that the encryption parameters are set with each object that is written into that block. Reviewed-by: Kash Pande <[email protected]> Reviewed-by: Brian Behlendorf <[email protected]> Signed-off-by: Tom Caputi <[email protected]> Closes #7215 Closes #7236
Diffstat (limited to 'module/zfs/dmu_send.c')
-rw-r--r--module/zfs/dmu_send.c85
1 files changed, 46 insertions, 39 deletions
diff --git a/module/zfs/dmu_send.c b/module/zfs/dmu_send.c
index 16dc13939..f5894e6b8 100644
--- a/module/zfs/dmu_send.c
+++ b/module/zfs/dmu_send.c
@@ -2167,6 +2167,14 @@ struct receive_writer_arg {
uint64_t last_offset;
uint64_t max_object; /* highest object ID referenced in stream */
uint64_t bytes_read; /* bytes read when current record created */
+
+ /* Encryption parameters for the last received DRR_OBJECT_RANGE */
+ uint64_t or_firstobj;
+ uint64_t or_numslots;
+ uint8_t or_salt[ZIO_DATA_SALT_LEN];
+ uint8_t or_iv[ZIO_DATA_IV_LEN];
+ uint8_t or_mac[ZIO_DATA_MAC_LEN];
+ boolean_t or_byteorder;
};
struct objlist {
@@ -2448,7 +2456,13 @@ receive_object(struct receive_writer_arg *rwa, struct drr_object *drro,
}
if (rwa->raw) {
- if (drro->drr_raw_bonuslen < drro->drr_bonuslen ||
+ /*
+ * We should have received a DRR_OBJECT_RANGE record
+ * containing this block and stored it in rwa.
+ */
+ if (drro->drr_object < rwa->or_firstobj ||
+ drro->drr_object >= rwa->or_firstobj + rwa->or_numslots ||
+ drro->drr_raw_bonuslen < drro->drr_bonuslen ||
drro->drr_indblkshift > SPA_MAXBLOCKSHIFT ||
drro->drr_nlevels > DN_MAX_LEVELS ||
drro->drr_nblkptr > DN_MAX_NBLKPTR ||
@@ -2611,8 +2625,27 @@ receive_object(struct receive_writer_arg *rwa, struct drr_object *drro,
return (SET_ERROR(EINVAL));
}
- if (rwa->raw)
- VERIFY0(dmu_object_dirty_raw(rwa->os, drro->drr_object, tx));
+ if (rwa->raw) {
+ /*
+ * Convert the buffer associated with this range of dnodes
+ * to a raw buffer. This ensures that it will be written out
+ * as a raw buffer when we fill in the dnode object. Since we
+ * are committing this tx now, it is possible for the dnode
+ * block to end up on-disk with the incorrect MAC. Despite
+ * this, the dataset is marked as inconsistent so no other
+ * code paths (apart from scrubs) will attempt to read this
+ * data. Scrubs will not be effected by this either since
+ * scrubs only read raw data and do not attempt to check
+ * the MAC.
+ */
+ err = dmu_convert_mdn_block_to_raw(rwa->os, rwa->or_firstobj,
+ rwa->or_byteorder, rwa->or_salt, rwa->or_iv, rwa->or_mac,
+ tx);
+ if (err != 0) {
+ dmu_tx_commit(tx);
+ return (SET_ERROR(EINVAL));
+ }
+ }
dmu_object_set_checksum(rwa->os, drro->drr_object,
drro->drr_checksumtype, tx);
@@ -2984,12 +3017,6 @@ static int
receive_object_range(struct receive_writer_arg *rwa,
struct drr_object_range *drror)
{
- int ret;
- dmu_tx_t *tx;
- dnode_t *mdn = NULL;
- dmu_buf_t *db = NULL;
- uint64_t offset;
-
/*
* By default, we assume this block is in our native format
* (ZFS_HOST_BYTEORDER). We then take into account whether
@@ -3019,38 +3046,18 @@ receive_object_range(struct receive_writer_arg *rwa,
if (drror->drr_firstobj > rwa->max_object)
rwa->max_object = drror->drr_firstobj;
- offset = drror->drr_firstobj * sizeof (dnode_phys_t);
- mdn = DMU_META_DNODE(rwa->os);
-
- tx = dmu_tx_create(rwa->os);
- ret = dmu_tx_assign(tx, TXG_WAIT);
- if (ret != 0) {
- dmu_tx_abort(tx);
- return (ret);
- }
-
- ret = dmu_buf_hold_by_dnode(mdn, offset, FTAG, &db,
- DMU_READ_PREFETCH | DMU_READ_NO_DECRYPT);
- if (ret != 0) {
- dmu_tx_commit(tx);
- return (ret);
- }
-
/*
- * Convert the buffer associated with this range of dnodes to a
- * raw buffer. This ensures that it will be written out as a raw
- * buffer when we fill in the dnode objects in future records.
- * Since we are commiting this tx now, it is technically possible
- * for the dnode block to end up on-disk with the incorrect MAC.
- * Despite this, the dataset is marked as inconsistent so no other
- * code paths (apart from scrubs) will attempt to read this data.
- * Scrubs will not be effected by this either since scrubs only
- * read raw data and do not attempt to check the MAC.
+ * The DRR_OBJECT_RANGE handling must be deferred to receive_object()
+ * so that the encryption parameters are set with each object that is
+ * written into that block.
*/
- dmu_convert_to_raw(db, byteorder, drror->drr_salt, drror->drr_iv,
- drror->drr_mac, tx);
- dmu_buf_rele(db, FTAG);
- dmu_tx_commit(tx);
+ rwa->or_firstobj = drror->drr_firstobj;
+ rwa->or_numslots = drror->drr_numslots;
+ bcopy(drror->drr_salt, rwa->or_salt, ZIO_DATA_SALT_LEN);
+ bcopy(drror->drr_iv, rwa->or_iv, ZIO_DATA_IV_LEN);
+ bcopy(drror->drr_mac, rwa->or_mac, ZIO_DATA_MAC_LEN);
+ rwa->or_byteorder = byteorder;
+
return (0);
}