From owner-svn-src-head@freebsd.org Sat Nov 3 03:10:07 2018 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 00AC910EB486; Sat, 3 Nov 2018 03:10:07 +0000 (UTC) (envelope-from mav@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id A7FA46EB16; Sat, 3 Nov 2018 03:10:06 +0000 (UTC) (envelope-from mav@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 899941421C; Sat, 3 Nov 2018 03:10:06 +0000 (UTC) (envelope-from mav@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id wA33A65j047961; Sat, 3 Nov 2018 03:10:06 GMT (envelope-from mav@FreeBSD.org) Received: (from mav@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id wA33A6Jl047960; Sat, 3 Nov 2018 03:10:06 GMT (envelope-from mav@FreeBSD.org) Message-Id: <201811030310.wA33A6Jl047960@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: mav set sender to mav@FreeBSD.org using -f From: Alexander Motin Date: Sat, 3 Nov 2018 03:10:06 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r340096 - head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs X-SVN-Group: head X-SVN-Commit-Author: mav X-SVN-Commit-Paths: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs X-SVN-Commit-Revision: 340096 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 03 Nov 2018 03:10:07 -0000 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);