diff options
author | Matthew Ahrens <[email protected]> | 2013-07-29 10:55:16 -0800 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2013-11-04 11:18:14 -0800 |
commit | 19580676295b4e271da63dce145bb17c3731d069 (patch) | |
tree | 1e888ba2b2ec87583cf961586f21d9837ec14751 /module | |
parent | 96c2e961938d4018ddb393fa60e004d8a91a58e9 (diff) |
Illumos #3888
3888 zfs recv -F should destroy any snapshots created since
the incremental source
Reviewed by: George Wilson <[email protected]>
Reviewed by: Adam Leventhal <[email protected]>
Reviewed by: Peng Dai <[email protected]>
Approved by: Richard Lowe <[email protected]>
References:
https://www.illumos.org/issues/3888
illumos/illumos-gate@34f2f8cf94052481c81be2e134b94a00b501bf21
Porting notes:
1. Commit 1fde1e37208c2f56c72c70a06676676f04b65998 wrapped a
declaration in dsl_dataset_modified_since_lastsnap in ASSERTV().
The ASSERTV() and local variable have been removed to avoid an
unused variable warning.
Signed-off-by: Brian Behlendorf <[email protected]>
Ported-by: Richard Yao <[email protected]>
Issue #1775
Diffstat (limited to 'module')
-rw-r--r-- | module/zfs/dmu_send.c | 119 | ||||
-rw-r--r-- | module/zfs/dsl_dataset.c | 29 | ||||
-rw-r--r-- | module/zfs/dsl_destroy.c | 5 |
3 files changed, 102 insertions, 51 deletions
diff --git a/module/zfs/dmu_send.c b/module/zfs/dmu_send.c index e80631b48..c5eb2fb79 100644 --- a/module/zfs/dmu_send.c +++ b/module/zfs/dmu_send.c @@ -682,6 +682,7 @@ typedef struct dmu_recv_begin_arg { const char *drba_origin; dmu_recv_cookie_t *drba_cookie; cred_t *drba_cred; + uint64_t drba_snapobj; } dmu_recv_begin_arg_t; static int @@ -692,11 +693,6 @@ recv_begin_check_existing_impl(dmu_recv_begin_arg_t *drba, dsl_dataset_t *ds, int error; dsl_pool_t *dp = ds->ds_dir->dd_pool; - /* must not have any changes since most recent snapshot */ - if (!drba->drba_cookie->drc_force && - dsl_dataset_modified_since_lastsnap(ds)) - return (SET_ERROR(ETXTBSY)); - /* temporary clone name must not exist */ error = zap_lookup(dp->dp_meta_objset, ds->ds_dir->dd_phys->dd_child_dir_zapobj, recv_clone_name, @@ -712,41 +708,47 @@ recv_begin_check_existing_impl(dmu_recv_begin_arg_t *drba, dsl_dataset_t *ds, return (error == 0 ? EEXIST : error); if (fromguid != 0) { - /* if incremental, most recent snapshot must match fromguid */ - if (ds->ds_prev == NULL) + dsl_dataset_t *snap; + uint64_t obj = ds->ds_phys->ds_prev_snap_obj; + + /* Find snapshot in this dir that matches fromguid. */ + while (obj != 0) { + error = dsl_dataset_hold_obj(dp, obj, FTAG, + &snap); + if (error != 0) + return (SET_ERROR(ENODEV)); + if (snap->ds_dir != ds->ds_dir) { + dsl_dataset_rele(snap, FTAG); + return (SET_ERROR(ENODEV)); + } + if (snap->ds_phys->ds_guid == fromguid) + break; + obj = snap->ds_phys->ds_prev_snap_obj; + dsl_dataset_rele(snap, FTAG); + } + if (obj == 0) return (SET_ERROR(ENODEV)); - /* - * most recent snapshot must match fromguid, or there are no - * changes since the fromguid one - */ - if (ds->ds_prev->ds_phys->ds_guid != fromguid) { - uint64_t birth = ds->ds_prev->ds_phys->ds_bp.blk_birth; - uint64_t obj = ds->ds_prev->ds_phys->ds_prev_snap_obj; - while (obj != 0) { - dsl_dataset_t *snap; - error = dsl_dataset_hold_obj(dp, obj, FTAG, - &snap); - if (error != 0) - return (SET_ERROR(ENODEV)); - if (snap->ds_phys->ds_creation_txg < birth) { - dsl_dataset_rele(snap, FTAG); - return (SET_ERROR(ENODEV)); - } - if (snap->ds_phys->ds_guid == fromguid) { - dsl_dataset_rele(snap, FTAG); - break; /* it's ok */ - } - obj = snap->ds_phys->ds_prev_snap_obj; + if (drba->drba_cookie->drc_force) { + drba->drba_snapobj = obj; + } else { + /* + * If we are not forcing, there must be no + * changes since fromsnap. + */ + if (dsl_dataset_modified_since_snap(ds, snap)) { dsl_dataset_rele(snap, FTAG); + return (SET_ERROR(ETXTBSY)); } - if (obj == 0) - return (SET_ERROR(ENODEV)); + drba->drba_snapobj = ds->ds_prev->ds_object; } + + dsl_dataset_rele(snap, FTAG); } else { /* if full, most recent snapshot must be $ORIGIN */ if (ds->ds_phys->ds_prev_snap_txg >= TXG_INITIAL) return (SET_ERROR(ENODEV)); + drba->drba_snapobj = ds->ds_phys->ds_prev_snap_obj; } return (0); @@ -855,8 +857,14 @@ dmu_recv_begin_sync(void *arg, dmu_tx_t *tx) error = dsl_dataset_hold(dp, tofs, FTAG, &ds); if (error == 0) { /* create temporary clone */ + dsl_dataset_t *snap = NULL; + if (drba->drba_snapobj != 0) { + VERIFY0(dsl_dataset_hold_obj(dp, + drba->drba_snapobj, FTAG, &snap)); + } dsobj = dsl_dataset_create_sync(ds->ds_dir, recv_clone_name, - ds->ds_prev, crflags, drba->drba_cred, tx); + snap, crflags, drba->drba_cred, tx); + dsl_dataset_rele(snap, FTAG); dsl_dataset_rele(ds, FTAG); } else { dsl_dir_t *dd; @@ -1577,6 +1585,32 @@ dmu_recv_end_check(void *arg, dmu_tx_t *tx) error = dsl_dataset_hold(dp, drc->drc_tofs, FTAG, &origin_head); if (error != 0) return (error); + if (drc->drc_force) { + /* + * We will destroy any snapshots in tofs (i.e. before + * origin_head) that are after the origin (which is + * the snap before drc_ds, because drc_ds can not + * have any snaps of its own). + */ + uint64_t obj = origin_head->ds_phys->ds_prev_snap_obj; + while (obj != drc->drc_ds->ds_phys->ds_prev_snap_obj) { + dsl_dataset_t *snap; + error = dsl_dataset_hold_obj(dp, obj, FTAG, + &snap); + if (error != 0) + return (error); + if (snap->ds_dir != origin_head->ds_dir) + error = SET_ERROR(EINVAL); + if (error == 0) { + error = dsl_destroy_snapshot_check_impl( + snap, B_FALSE); + } + obj = snap->ds_phys->ds_prev_snap_obj; + dsl_dataset_rele(snap, FTAG); + if (error != 0) + return (error); + } + } error = dsl_dataset_clone_swap_check_impl(drc->drc_ds, origin_head, drc->drc_force); if (error != 0) { @@ -1611,6 +1645,27 @@ dmu_recv_end_sync(void *arg, dmu_tx_t *tx) VERIFY0(dsl_dataset_hold(dp, drc->drc_tofs, FTAG, &origin_head)); + + if (drc->drc_force) { + /* + * Destroy any snapshots of drc_tofs (origin_head) + * after the origin (the snap before drc_ds). + */ + uint64_t obj = origin_head->ds_phys->ds_prev_snap_obj; + while (obj != drc->drc_ds->ds_phys->ds_prev_snap_obj) { + dsl_dataset_t *snap; + VERIFY0(dsl_dataset_hold_obj(dp, obj, FTAG, + &snap)); + ASSERT3P(snap->ds_dir, ==, origin_head->ds_dir); + obj = snap->ds_phys->ds_prev_snap_obj; + dsl_destroy_snapshot_sync_impl(snap, + B_FALSE, tx); + dsl_dataset_rele(snap, FTAG); + } + } + VERIFY3P(drc->drc_ds->ds_prev, ==, + origin_head->ds_prev); + dsl_dataset_clone_swap_sync_impl(drc->drc_ds, origin_head, tx); dsl_dataset_snapshot_sync_impl(origin_head, diff --git a/module/zfs/dsl_dataset.c b/module/zfs/dsl_dataset.c index 16d5dad49..b04ef69bb 100644 --- a/module/zfs/dsl_dataset.c +++ b/module/zfs/dsl_dataset.c @@ -1516,16 +1516,14 @@ dsl_dataset_space(dsl_dataset_t *ds, } boolean_t -dsl_dataset_modified_since_lastsnap(dsl_dataset_t *ds) +dsl_dataset_modified_since_snap(dsl_dataset_t *ds, dsl_dataset_t *snap) { - ASSERTV(dsl_pool_t *dp = ds->ds_dir->dd_pool); - - ASSERT(dsl_pool_config_held(dp)); - if (ds->ds_prev == NULL) + ASSERT(dsl_pool_config_held(ds->ds_dir->dd_pool)); + if (snap == NULL) return (B_FALSE); if (ds->ds_phys->ds_bp.blk_birth > - ds->ds_prev->ds_phys->ds_creation_txg) { - objset_t *os, *os_prev; + snap->ds_phys->ds_creation_txg) { + objset_t *os, *os_snap; /* * It may be that only the ZIL differs, because it was * reset in the head. Don't count that as being @@ -1533,10 +1531,10 @@ dsl_dataset_modified_since_lastsnap(dsl_dataset_t *ds) */ if (dmu_objset_from_ds(ds, &os) != 0) return (B_TRUE); - if (dmu_objset_from_ds(ds->ds_prev, &os_prev) != 0) + if (dmu_objset_from_ds(snap, &os_snap) != 0) return (B_TRUE); return (bcmp(&os->os_phys->os_meta_dnode, - &os_prev->os_phys->os_meta_dnode, + &os_snap->os_phys->os_meta_dnode, sizeof (os->os_phys->os_meta_dnode)) != 0); } return (B_FALSE); @@ -2287,15 +2285,14 @@ dsl_dataset_clone_swap_check_impl(dsl_dataset_t *clone, dsl_dataset_is_snapshot(origin_head)) return (SET_ERROR(EINVAL)); - /* the branch point should be just before them */ - if (clone->ds_prev != origin_head->ds_prev) + /* if we are not forcing, the branch point should be just before them */ + if (!force && clone->ds_prev != origin_head->ds_prev) return (SET_ERROR(EINVAL)); /* clone should be the clone (unless they are unrelated) */ if (clone->ds_prev != NULL && clone->ds_prev != clone->ds_dir->dd_pool->dp_origin_snap && - origin_head->ds_object != - clone->ds_prev->ds_phys->ds_next_snap_obj) + origin_head->ds_dir != clone->ds_prev->ds_dir) return (SET_ERROR(EINVAL)); /* the clone should be a child of the origin */ @@ -2303,7 +2300,8 @@ dsl_dataset_clone_swap_check_impl(dsl_dataset_t *clone, return (SET_ERROR(EINVAL)); /* origin_head shouldn't be modified unless 'force' */ - if (!force && dsl_dataset_modified_since_lastsnap(origin_head)) + if (!force && + dsl_dataset_modified_since_snap(origin_head, origin_head->ds_prev)) return (SET_ERROR(ETXTBSY)); /* origin_head should have no long holds (e.g. is not mounted) */ @@ -2340,6 +2338,7 @@ dsl_dataset_clone_swap_sync_impl(dsl_dataset_t *clone, ASSERT(clone->ds_reserved == 0); ASSERT(origin_head->ds_quota == 0 || clone->ds_phys->ds_unique_bytes <= origin_head->ds_quota); + ASSERT3P(clone->ds_prev, ==, origin_head->ds_prev); dmu_buf_will_dirty(clone->ds_dbuf, tx); dmu_buf_will_dirty(origin_head->ds_dbuf, tx); @@ -2943,7 +2942,7 @@ EXPORT_SYMBOL(dsl_dataset_get_holds); EXPORT_SYMBOL(dsl_dataset_get_blkptr); EXPORT_SYMBOL(dsl_dataset_set_blkptr); EXPORT_SYMBOL(dsl_dataset_get_spa); -EXPORT_SYMBOL(dsl_dataset_modified_since_lastsnap); +EXPORT_SYMBOL(dsl_dataset_modified_since_snap); EXPORT_SYMBOL(dsl_dataset_space_written); EXPORT_SYMBOL(dsl_dataset_space_wouldfree); EXPORT_SYMBOL(dsl_dataset_sync); diff --git a/module/zfs/dsl_destroy.c b/module/zfs/dsl_destroy.c index 1c4c3deec..0e741105d 100644 --- a/module/zfs/dsl_destroy.c +++ b/module/zfs/dsl_destroy.c @@ -46,10 +46,7 @@ typedef struct dmu_snapshots_destroy_arg { nvlist_t *dsda_errlist; } dmu_snapshots_destroy_arg_t; -/* - * ds must be owned. - */ -static int +int dsl_destroy_snapshot_check_impl(dsl_dataset_t *ds, boolean_t defer) { if (!dsl_dataset_is_snapshot(ds)) |