svn commit: r340096 - head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs

Alexander Motin mav at FreeBSD.org
Sat Nov 3 03:10:07 UTC 2018


Author: mav
Date: Sat Nov  3 03:10:06 2018
New Revision: 340096
URL: https://svnweb.freebsd.org/changeset/base/340096

Log:
  9952 Block size change during zfs receive drops spill block
  
  Replication code in receive_object() falsely assumes that if received
  object block size is different from local, then it must be a new object
  and calls dmu_object_reclaim() to wipe it out. In most cases it is not a
  problem, since all dnode, bonus buffer and data block(s) are immediately
  rewritten any way, but the problem is that spill block (if used) is not.
  This means loss of ACLs, extended attributes, etc.
  
  This issue can be triggered in very simple way:
  1. create 4KB file with 10+ ACL entries;
  2. take snapshot and send it to different dataset;
  3. append another 4KB to the file;
  4. take another snapshot and send incrementally;
  5. witness ACL loss on receive side.
  
  PR:		198457
  Discussed with:	mahrens
  MFC after:	2 weeks
  Sponsored by:	iXsystems, Inc.

Modified:
  head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c

Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c	Sat Nov  3 01:53:26 2018	(r340095)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c	Sat Nov  3 03:10:06 2018	(r340096)
@@ -2143,6 +2143,7 @@ receive_object(struct receive_writer_arg *rwa, struct 
 {
 	dmu_object_info_t doi;
 	dmu_tx_t *tx;
+	dmu_buf_t *db;
 	uint64_t object;
 	int err;
 
@@ -2190,12 +2191,14 @@ receive_object(struct receive_writer_arg *rwa, struct 
 
 	tx = dmu_tx_create(rwa->os);
 	dmu_tx_hold_bonus(tx, object);
+	dmu_tx_hold_write(tx, object, 0, 0);
 	err = dmu_tx_assign(tx, TXG_WAIT);
 	if (err != 0) {
 		dmu_tx_abort(tx);
 		return (err);
 	}
 
+	db = NULL;
 	if (object == DMU_NEW_OBJECT) {
 		/* currently free, want to be allocated */
 		err = dmu_object_claim_dnsize(rwa->os, drro->drr_object,
@@ -2203,15 +2206,33 @@ receive_object(struct receive_writer_arg *rwa, struct 
 		    drro->drr_bonustype, drro->drr_bonuslen,
 		    drro->drr_dn_slots << DNODE_SHIFT, tx);
 	} else if (drro->drr_type != doi.doi_type ||
-	    drro->drr_blksz != doi.doi_data_block_size ||
-	    drro->drr_bonustype != doi.doi_bonus_type ||
-	    drro->drr_bonuslen != doi.doi_bonus_size) {
+	    (drro->drr_blksz != doi.doi_data_block_size &&
+	     doi.doi_max_offset > doi.doi_data_block_size)) {
 		/* currently allocated, but with different properties */
 		err = dmu_object_reclaim(rwa->os, drro->drr_object,
 		    drro->drr_type, drro->drr_blksz,
 		    drro->drr_bonustype, drro->drr_bonuslen, tx);
+	} else {
+		/*
+		 * Currently allocated, but with slightly different properties,
+		 * that may change live, like block size or bonus buffer.
+		 * Change those specifically to not loose the spill block, etc.
+		 */
+		if (drro->drr_bonustype != doi.doi_bonus_type ||
+		    drro->drr_bonuslen != doi.doi_bonus_size)
+			VERIFY0(dmu_bonus_hold(rwa->os, drro->drr_object, FTAG,
+			    &db));
+		if (drro->drr_bonustype != doi.doi_bonus_type)
+			VERIFY0(dmu_set_bonustype(db, drro->drr_bonustype, tx));
+		if (drro->drr_bonuslen != doi.doi_bonus_size)
+			VERIFY0(dmu_set_bonus(db, drro->drr_bonuslen, tx));
+		if (drro->drr_blksz != doi.doi_data_block_size)
+			err = dmu_object_set_blocksize(rwa->os, drro->drr_object,
+			    drro->drr_blksz, 0, tx);
 	}
 	if (err != 0) {
+		if (db != NULL)
+			dmu_buf_rele(db, FTAG);
 		dmu_tx_commit(tx);
 		return (SET_ERROR(EINVAL));
 	}
@@ -2222,9 +2243,9 @@ receive_object(struct receive_writer_arg *rwa, struct 
 	    drro->drr_compress, tx);
 
 	if (data != NULL) {
-		dmu_buf_t *db;
-
-		VERIFY0(dmu_bonus_hold(rwa->os, drro->drr_object, FTAG, &db));
+		if (db == NULL)
+			VERIFY0(dmu_bonus_hold(rwa->os, drro->drr_object, FTAG,
+			    &db));
 		dmu_buf_will_dirty(db, tx);
 
 		ASSERT3U(db->db_size, >=, drro->drr_bonuslen);
@@ -2235,8 +2256,9 @@ receive_object(struct receive_writer_arg *rwa, struct 
 			dmu_ot_byteswap[byteswap].ob_func(db->db_data,
 			    drro->drr_bonuslen);
 		}
-		dmu_buf_rele(db, FTAG);
 	}
+	if (db != NULL)
+		dmu_buf_rele(db, FTAG);
 	dmu_tx_commit(tx);
 
 	return (0);


More information about the svn-src-all mailing list