Deny block cloning is dbuf size doesn't match BP size.

I don't know an easy way to shrink down dbuf size, so just deny block cloning
into dbufs that don't match our BP's size.

This fixes the following situation:
1. Create a small file, eg. 1kB of random bytes. Its dbuf will be 1kB.
2. Create a larger file, eg. 2kB of random bytes. Its dbuf will be 2kB.
3. Truncate the large file to 0. Its dbuf will remain 2kB.
4. Clone the small file into the large file. Small file's BP lsize is
   1kB, but the large file's dbuf is 2kB.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Pawel Jakub Dawidek <pawel@dawidek.net>
Closes #14825
This commit is contained in:
Pawel Jakub Dawidek 2023-05-02 14:24:43 -07:00 committed by Brian Behlendorf
parent 555ef90c5c
commit bd8c6bd66f
3 changed files with 32 additions and 7 deletions

View File

@ -1066,7 +1066,7 @@ int dmu_offset_next(objset_t *os, uint64_t object, boolean_t hole,
int dmu_read_l0_bps(objset_t *os, uint64_t object, uint64_t offset,
uint64_t length, dmu_tx_t *tx, struct blkptr *bps, size_t *nbpsp);
void dmu_brt_clone(objset_t *os, uint64_t object, uint64_t offset,
int dmu_brt_clone(objset_t *os, uint64_t object, uint64_t offset,
uint64_t length, dmu_tx_t *tx, const struct blkptr *bps, size_t nbps,
boolean_t replay);

View File

@ -2257,7 +2257,7 @@ dmu_read_l0_bps(objset_t *os, uint64_t object, uint64_t offset, uint64_t length,
return (error);
}
void
int
dmu_brt_clone(objset_t *os, uint64_t object, uint64_t offset, uint64_t length,
dmu_tx_t *tx, const blkptr_t *bps, size_t nbps, boolean_t replay)
{
@ -2267,7 +2267,7 @@ dmu_brt_clone(objset_t *os, uint64_t object, uint64_t offset, uint64_t length,
struct dirty_leaf *dl;
dbuf_dirty_record_t *dr;
const blkptr_t *bp;
int numbufs;
int error = 0, i, numbufs;
spa = os->os_spa;
@ -2275,7 +2275,26 @@ dmu_brt_clone(objset_t *os, uint64_t object, uint64_t offset, uint64_t length,
&numbufs, &dbp));
ASSERT3U(nbps, ==, numbufs);
for (int i = 0; i < numbufs; i++) {
/*
* Before we start cloning make sure that the dbufs sizes much new BPs
* sizes. If they don't, that's a no-go, as we are not able to shrink
* dbufs.
*/
for (i = 0; i < numbufs; i++) {
dbuf = dbp[i];
db = (dmu_buf_impl_t *)dbuf;
bp = &bps[i];
ASSERT0(db->db_level);
ASSERT(db->db_blkid != DMU_BONUS_BLKID);
if (!BP_IS_HOLE(bp) && BP_GET_LSIZE(bp) != dbuf->db_size) {
error = SET_ERROR(EXDEV);
goto out;
}
}
for (i = 0; i < numbufs; i++) {
dbuf = dbp[i];
db = (dmu_buf_impl_t *)dbuf;
bp = &bps[i];
@ -2319,8 +2338,10 @@ dmu_brt_clone(objset_t *os, uint64_t object, uint64_t offset, uint64_t length,
brt_pending_add(spa, bp, tx);
}
}
out:
dmu_buf_rele_array(dbp, numbufs, FTAG);
return (error);
}
void

View File

@ -1309,8 +1309,12 @@ zfs_clone_range(znode_t *inzp, uint64_t *inoffp, znode_t *outzp,
((len - 1) / inblksz + 1) * inblksz);
}
dmu_brt_clone(outos, outzp->z_id, outoff, size, tx, bps, nbps,
B_FALSE);
error = dmu_brt_clone(outos, outzp->z_id, outoff, size, tx,
bps, nbps, B_FALSE);
if (error != 0) {
dmu_tx_commit(tx);
break;
}
zfs_clear_setid_bits_if_necessary(outzfsvfs, outzp, cr,
&clear_setid_bits_txg, tx);