mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-25 03:37:45 +03:00
Fix handling of maxblkid for raw sends
Currently, the receive code can create an unreadable dataset from a correct raw send stream. This is because it is currently impossible to set maxblkid to a lower value without freeing the associated object. This means truncating files on the send side to a non-0 size could result in corruption. This patch solves this issue by adding a new 'force' flag to dnode_new_blkid() which will allow the raw receive code to force the DMU to accept the provided maxblkid even if it is a lower value than the existing one. For testing purposes the send_encrypted_files.ksh test has been extended to include a variety of truncated files and multiple snapshots. It also now leverages the xattrtest command to help ensure raw receives correctly handle xattrs. Reviewed-by: Paul Dagnelie <pcd@delphix.com> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Matt Ahrens <mahrens@delphix.com> Signed-off-by: Tom Caputi <tcaputi@datto.com> Closes #8168 Closes #8487
This commit is contained in:
committed by
Brian Behlendorf
parent
146bdc414c
commit
369aa501d1
+21
-5
@@ -1828,7 +1828,8 @@ out:
|
||||
|
||||
/* read-holding callers must not rely on the lock being continuously held */
|
||||
void
|
||||
dnode_new_blkid(dnode_t *dn, uint64_t blkid, dmu_tx_t *tx, boolean_t have_read)
|
||||
dnode_new_blkid(dnode_t *dn, uint64_t blkid, dmu_tx_t *tx, boolean_t have_read,
|
||||
boolean_t force)
|
||||
{
|
||||
int epbs, new_nlevels;
|
||||
uint64_t sz;
|
||||
@@ -1853,14 +1854,25 @@ dnode_new_blkid(dnode_t *dn, uint64_t blkid, dmu_tx_t *tx, boolean_t have_read)
|
||||
}
|
||||
}
|
||||
|
||||
if (blkid <= dn->dn_maxblkid)
|
||||
/*
|
||||
* Raw sends (indicated by the force flag) require that we take the
|
||||
* given blkid even if the value is lower than the current value.
|
||||
*/
|
||||
if (!force && blkid <= dn->dn_maxblkid)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* We use the (otherwise unused) top bit of dn_next_maxblkid[txgoff]
|
||||
* to indicate that this field is set. This allows us to set the
|
||||
* maxblkid to 0 on an existing object in dnode_sync().
|
||||
*/
|
||||
dn->dn_maxblkid = blkid;
|
||||
dn->dn_next_maxblkid[tx->tx_txg & TXG_MASK] = blkid;
|
||||
dn->dn_next_maxblkid[tx->tx_txg & TXG_MASK] =
|
||||
blkid | DMU_NEXT_MAXBLKID_SET;
|
||||
|
||||
/*
|
||||
* Compute the number of levels necessary to support the new maxblkid.
|
||||
* Raw sends will ensure nlevels is set correctly for us.
|
||||
*/
|
||||
new_nlevels = 1;
|
||||
epbs = dn->dn_indblkshift - SPA_BLKPTRSHIFT;
|
||||
@@ -1870,8 +1882,12 @@ dnode_new_blkid(dnode_t *dn, uint64_t blkid, dmu_tx_t *tx, boolean_t have_read)
|
||||
|
||||
ASSERT3U(new_nlevels, <=, DN_MAX_LEVELS);
|
||||
|
||||
if (new_nlevels > dn->dn_nlevels)
|
||||
dnode_set_nlevels_impl(dn, new_nlevels, tx);
|
||||
if (!force) {
|
||||
if (new_nlevels > dn->dn_nlevels)
|
||||
dnode_set_nlevels_impl(dn, new_nlevels, tx);
|
||||
} else {
|
||||
ASSERT3U(dn->dn_nlevels, >=, new_nlevels);
|
||||
}
|
||||
|
||||
out:
|
||||
if (have_read)
|
||||
|
||||
Reference in New Issue
Block a user