mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2024-12-25 02:49:32 +03:00
Illumos #3888
3888 zfs recv -F should destroy any snapshots created since
the incremental source
Reviewed by: George Wilson <george.wilson@delphix.com>
Reviewed by: Adam Leventhal <ahl@delphix.com>
Reviewed by: Peng Dai <peng.dai@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
https://www.illumos.org/issues/3888
illumos/illumos-gate@34f2f8cf94
Porting notes:
1. Commit 1fde1e3720
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 <behlendorf1@llnl.gov>
Ported-by: Richard Yao <ryao@gentoo.org>
Issue #1775
This commit is contained in:
parent
96c2e96193
commit
1958067629
@ -20,7 +20,7 @@
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
|
||||
* Copyright (c) 2013 Steven Hartland. All rights reserved.
|
||||
*/
|
||||
@ -204,7 +204,8 @@ void dsl_dataset_set_blkptr(dsl_dataset_t *ds, blkptr_t *bp, dmu_tx_t *tx);
|
||||
|
||||
spa_t *dsl_dataset_get_spa(dsl_dataset_t *ds);
|
||||
|
||||
boolean_t dsl_dataset_modified_since_lastsnap(dsl_dataset_t *ds);
|
||||
boolean_t dsl_dataset_modified_since_snap(dsl_dataset_t *ds,
|
||||
dsl_dataset_t *snap);
|
||||
|
||||
void dsl_dataset_sync(dsl_dataset_t *os, zio_t *zio, dmu_tx_t *tx);
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
@ -35,15 +35,16 @@ struct nvlist;
|
||||
struct dsl_dataset;
|
||||
struct dmu_tx;
|
||||
|
||||
int dsl_destroy_snapshots_nvl(struct nvlist *snaps, boolean_t defer,
|
||||
struct nvlist *errlist);
|
||||
int dsl_destroy_snapshot(const char *name, boolean_t defer);
|
||||
int dsl_destroy_head(const char *name);
|
||||
int dsl_destroy_head_check_impl(struct dsl_dataset *ds, int expected_holds);
|
||||
void dsl_destroy_head_sync_impl(struct dsl_dataset *ds, struct dmu_tx *tx);
|
||||
int dsl_destroy_inconsistent(const char *dsname, void *arg);
|
||||
void dsl_destroy_snapshot_sync_impl(struct dsl_dataset *ds,
|
||||
boolean_t defer, struct dmu_tx *tx);
|
||||
int dsl_destroy_snapshots_nvl(struct nvlist *, boolean_t,
|
||||
struct nvlist *);
|
||||
int dsl_destroy_snapshot(const char *, boolean_t);
|
||||
int dsl_destroy_head(const char *);
|
||||
int dsl_destroy_head_check_impl(struct dsl_dataset *, int);
|
||||
void dsl_destroy_head_sync_impl(struct dsl_dataset *, struct dmu_tx *);
|
||||
int dsl_destroy_inconsistent(const char *, void *);
|
||||
int dsl_destroy_snapshot_check_impl(struct dsl_dataset *, boolean_t);
|
||||
void dsl_destroy_snapshot_sync_impl(struct dsl_dataset *,
|
||||
boolean_t, struct dmu_tx *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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))
|
||||
|
Loading…
Reference in New Issue
Block a user