MFV r253782:
To quote Illumos issue #3888: When 'zfs recv -F' is used with an incremental recv it rolls back any changes made since the last snapshot in case new changes were made to the file system while the recv is in progress (without -F the recv would fail when it does it's final check to commit the recv-ed data as the recv-ed data conflicts with the newly written data). However, if there is a snapshot taken after the recv began rolling back to the 'latest' snapshot will not help and the recv will still fail. 'zfs recv -F' should be extended to destroy any snapshots created since the source snapshot when finishing the recv (effectively rolling back through all snapshots, instead of just to the latest snapshot). Illumos ZFS issues: 3888 zfs recv -F should destroy any snapshots created since the incremental source MFC after: 2 weeks
This commit is contained in:
parent
c5affee6a3
commit
6cd9d0f454
@ -683,6 +683,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
|
||||
@ -693,11 +694,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,
|
||||
@ -713,41 +709,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);
|
||||
@ -856,8 +858,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;
|
||||
@ -1601,6 +1609,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, drc->drc_owner, tx);
|
||||
if (error != 0) {
|
||||
@ -1635,6 +1669,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,
|
||||
|
@ -1593,16 +1593,16 @@ 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)
|
||||
{
|
||||
dsl_pool_t *dp = ds->ds_dir->dd_pool;
|
||||
|
||||
ASSERT(dsl_pool_config_held(dp));
|
||||
if (ds->ds_prev == NULL)
|
||||
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
|
||||
@ -1610,10 +1610,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);
|
||||
@ -2436,15 +2436,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 */
|
||||
@ -2452,7 +2451,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) */
|
||||
@ -2489,6 +2489,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);
|
||||
|
@ -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))
|
||||
|
@ -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.
|
||||
*/
|
||||
@ -206,7 +206,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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user