Improve block sizes checks during cloning
- Fail if source block is smaller than destination. We can only grow blocks, not shrink them. - Fail if we do not have full znode range lock. In that case grow is not even called. We should improve zfs_rangelock_cb() somehow to know when cloning needs to grow the block size unlike write. - Fail of we tried to resize, but failed. There are many reasons for it to fail that we can not predict at this level, so be ready for them. Unlike write, that may proceed after growth failure, block cloning can't and must return error. This fixes assertion inside dmu_brt_clone() when it sees different number of blocks held in destination than it got block pointers. Builds without ZFS_DEBUG returned EXDEV, so are not affected much. Reviewed-by: Pawel Jakub Dawidek <pawel@dawidek.net> Reviewed-by: Brian Atkinson <batkinson@lanl.gov> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Alexander Motin <mav@FreeBSD.org> Sponsored by: iXsystems, Inc. Closes #15724 Closes #15735
This commit is contained in:
parent
7ecaa07580
commit
255741fc97
|
@ -1186,11 +1186,18 @@ zfs_clone_range(znode_t *inzp, uint64_t *inoffp, znode_t *outzp,
|
||||||
inblksz = inzp->z_blksz;
|
inblksz = inzp->z_blksz;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We cannot clone into files with different block size if we can't
|
* We cannot clone into a file with different block size if we can't
|
||||||
* grow it (block size is already bigger or more than one block).
|
* grow it (block size is already bigger, has more than one block, or
|
||||||
|
* not locked for growth). There are other possible reasons for the
|
||||||
|
* grow to fail, but we cover what we can before opening transaction
|
||||||
|
* and the rest detect after we try to do it.
|
||||||
*/
|
*/
|
||||||
|
if (inblksz < outzp->z_blksz) {
|
||||||
|
error = SET_ERROR(EINVAL);
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
if (inblksz != outzp->z_blksz && (outzp->z_size > outzp->z_blksz ||
|
if (inblksz != outzp->z_blksz && (outzp->z_size > outzp->z_blksz ||
|
||||||
outzp->z_size > inblksz)) {
|
outlr->lr_length != UINT64_MAX)) {
|
||||||
error = SET_ERROR(EINVAL);
|
error = SET_ERROR(EINVAL);
|
||||||
goto unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
@ -1309,12 +1316,24 @@ zfs_clone_range(znode_t *inzp, uint64_t *inoffp, znode_t *outzp,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy source znode's block size. This only happens on the
|
* Copy source znode's block size. This is done only if the
|
||||||
* first iteration since zfs_rangelock_reduce() will shrink down
|
* whole znode is locked (see zfs_rangelock_cb()) and only
|
||||||
* lr_len to the appropriate size.
|
* on the first iteration since zfs_rangelock_reduce() will
|
||||||
|
* shrink down lr_length to the appropriate size.
|
||||||
*/
|
*/
|
||||||
if (outlr->lr_length == UINT64_MAX) {
|
if (outlr->lr_length == UINT64_MAX) {
|
||||||
zfs_grow_blocksize(outzp, inblksz, tx);
|
zfs_grow_blocksize(outzp, inblksz, tx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Block growth may fail for many reasons we can not
|
||||||
|
* predict here. If it happen the cloning is doomed.
|
||||||
|
*/
|
||||||
|
if (inblksz != outzp->z_blksz) {
|
||||||
|
error = SET_ERROR(EINVAL);
|
||||||
|
dmu_tx_abort(tx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Round range lock up to the block boundary, so we
|
* Round range lock up to the block boundary, so we
|
||||||
* prevent appends until we are done.
|
* prevent appends until we are done.
|
||||||
|
|
Loading…
Reference in New Issue