Free objects when receiving full stream as clone

All objects after the last written or freed object are not supposed to
exist after receiving the stream.  Free them accordingly, as if a
freeobjects record for them had been included in the stream.

Reviewed by: Paul Dagnelie <pcd@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Closes #5699
Closes #6507
Closes #6616
This commit is contained in:
Fabian Grünbichler 2017-09-29 12:00:29 +02:00 committed by Brian Behlendorf
parent aee1dd4d98
commit 48fbb9ddbf
2 changed files with 55 additions and 1 deletions

View File

@ -62,6 +62,7 @@ typedef struct dmu_recv_cookie {
boolean_t drc_force; boolean_t drc_force;
boolean_t drc_resumable; boolean_t drc_resumable;
boolean_t drc_raw; boolean_t drc_raw;
boolean_t drc_clone;
struct avl_tree *drc_guid_to_ds_map; struct avl_tree *drc_guid_to_ds_map;
zio_cksum_t drc_cksum; zio_cksum_t drc_cksum;
uint64_t drc_newsnapobj; uint64_t drc_newsnapobj;

View File

@ -2072,6 +2072,7 @@ dmu_recv_begin(char *tofs, char *tosnap, dmu_replay_record_t *drr_begin,
drc->drc_force = force; drc->drc_force = force;
drc->drc_resumable = resumable; drc->drc_resumable = resumable;
drc->drc_cred = CRED(); drc->drc_cred = CRED();
drc->drc_clone = (origin != NULL);
if (drc->drc_drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) { if (drc->drc_drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) {
drc->drc_byteswap = B_TRUE; drc->drc_byteswap = B_TRUE;
@ -2133,7 +2134,9 @@ struct receive_writer_arg {
avl_tree_t *guid_to_ds_map; avl_tree_t *guid_to_ds_map;
boolean_t resumable; boolean_t resumable;
boolean_t raw; boolean_t raw;
uint64_t last_object, last_offset; uint64_t last_object;
uint64_t last_offset;
uint64_t max_object; /* highest object ID referenced in stream */
uint64_t bytes_read; /* bytes read when current record created */ uint64_t bytes_read; /* bytes read when current record created */
}; };
@ -2435,6 +2438,9 @@ receive_object(struct receive_writer_arg *rwa, struct drr_object *drro,
return (SET_ERROR(EINVAL)); return (SET_ERROR(EINVAL));
object = err == 0 ? drro->drr_object : DMU_NEW_OBJECT; object = err == 0 ? drro->drr_object : DMU_NEW_OBJECT;
if (drro->drr_object > rwa->max_object)
rwa->max_object = drro->drr_object;
/* /*
* If we are losing blkptrs or changing the block size this must * If we are losing blkptrs or changing the block size this must
* be a new file instance. We must clear out the previous file * be a new file instance. We must clear out the previous file
@ -2571,6 +2577,9 @@ receive_freeobjects(struct receive_writer_arg *rwa,
err = dmu_free_long_object(rwa->os, obj); err = dmu_free_long_object(rwa->os, obj);
if (err != 0) if (err != 0)
return (err); return (err);
if (obj > rwa->max_object)
rwa->max_object = obj;
} }
if (next_err != ESRCH) if (next_err != ESRCH)
return (next_err); return (next_err);
@ -2601,6 +2610,9 @@ receive_write(struct receive_writer_arg *rwa, struct drr_write *drrw,
rwa->last_object = drrw->drr_object; rwa->last_object = drrw->drr_object;
rwa->last_offset = drrw->drr_offset; rwa->last_offset = drrw->drr_offset;
if (rwa->last_object > rwa->max_object)
rwa->max_object = rwa->last_object;
if (dmu_object_info(rwa->os, drrw->drr_object, NULL) != 0) if (dmu_object_info(rwa->os, drrw->drr_object, NULL) != 0)
return (SET_ERROR(EINVAL)); return (SET_ERROR(EINVAL));
@ -2682,6 +2694,9 @@ receive_write_byref(struct receive_writer_arg *rwa,
ref_os = rwa->os; ref_os = rwa->os;
} }
if (drrwbr->drr_object > rwa->max_object)
rwa->max_object = drrwbr->drr_object;
if (rwa->raw) if (rwa->raw)
flags |= DMU_READ_NO_DECRYPT; flags |= DMU_READ_NO_DECRYPT;
@ -2735,6 +2750,9 @@ receive_write_embedded(struct receive_writer_arg *rwa,
if (drrwe->drr_compression >= ZIO_COMPRESS_FUNCTIONS) if (drrwe->drr_compression >= ZIO_COMPRESS_FUNCTIONS)
return (SET_ERROR(EINVAL)); return (SET_ERROR(EINVAL));
if (drrwe->drr_object > rwa->max_object)
rwa->max_object = drrwe->drr_object;
tx = dmu_tx_create(rwa->os); tx = dmu_tx_create(rwa->os);
dmu_tx_hold_write(tx, drrwe->drr_object, dmu_tx_hold_write(tx, drrwe->drr_object,
@ -2778,6 +2796,9 @@ receive_spill(struct receive_writer_arg *rwa, struct drr_spill *drrs,
if (dmu_object_info(rwa->os, drrs->drr_object, NULL) != 0) if (dmu_object_info(rwa->os, drrs->drr_object, NULL) != 0)
return (SET_ERROR(EINVAL)); return (SET_ERROR(EINVAL));
if (drrs->drr_object > rwa->max_object)
rwa->max_object = drrs->drr_object;
VERIFY0(dmu_bonus_hold(rwa->os, drrs->drr_object, FTAG, &db)); VERIFY0(dmu_bonus_hold(rwa->os, drrs->drr_object, FTAG, &db));
if ((err = dmu_spill_hold_by_bonus(db, FTAG, &db_spill)) != 0) { if ((err = dmu_spill_hold_by_bonus(db, FTAG, &db_spill)) != 0) {
dmu_buf_rele(db, FTAG); dmu_buf_rele(db, FTAG);
@ -2824,6 +2845,9 @@ receive_free(struct receive_writer_arg *rwa, struct drr_free *drrf)
if (dmu_object_info(rwa->os, drrf->drr_object, NULL) != 0) if (dmu_object_info(rwa->os, drrf->drr_object, NULL) != 0)
return (SET_ERROR(EINVAL)); return (SET_ERROR(EINVAL));
if (drrf->drr_object > rwa->max_object)
rwa->max_object = drrf->drr_object;
err = dmu_free_long_range(rwa->os, drrf->drr_object, err = dmu_free_long_range(rwa->os, drrf->drr_object,
drrf->drr_offset, drrf->drr_length); drrf->drr_offset, drrf->drr_length);
@ -2866,6 +2890,9 @@ receive_object_range(struct receive_writer_arg *rwa,
!rwa->raw) !rwa->raw)
return (SET_ERROR(EINVAL)); return (SET_ERROR(EINVAL));
if (drror->drr_firstobj > rwa->max_object)
rwa->max_object = drror->drr_firstobj;
offset = drror->drr_firstobj * sizeof (dnode_phys_t); offset = drror->drr_firstobj * sizeof (dnode_phys_t);
mdn = DMU_META_DNODE(rwa->os); mdn = DMU_META_DNODE(rwa->os);
@ -3704,6 +3731,32 @@ dmu_recv_stream(dmu_recv_cookie_t *drc, vnode_t *vp, offset_t *voffp,
} }
mutex_exit(&rwa->mutex); mutex_exit(&rwa->mutex);
/*
* If we are receiving a full stream as a clone, all object IDs which
* are greater than the maximum ID referenced in the stream are
* by definition unused and must be freed.
*/
if (drc->drc_clone && drc->drc_drrb->drr_fromguid == 0) {
uint64_t obj = rwa->max_object + 1;
int free_err = 0;
int next_err = 0;
while (next_err == 0) {
free_err = dmu_free_long_object(rwa->os, obj);
if (free_err != 0 && free_err != ENOENT)
break;
next_err = dmu_object_next(rwa->os, &obj, FALSE, 0);
}
if (err == 0) {
if (free_err != 0 && free_err != ENOENT)
err = free_err;
else if (next_err != ESRCH)
err = next_err;
}
}
cv_destroy(&rwa->cv); cv_destroy(&rwa->cv);
mutex_destroy(&rwa->mutex); mutex_destroy(&rwa->mutex);
bqueue_destroy(&rwa->q); bqueue_destroy(&rwa->q);