From 0fe60623dc0a8afa2fd9432c8bfc14b2fc9d1514 Mon Sep 17 00:00:00 2001 From: Keith M Wesolowski Date: Wed, 3 Jul 2013 18:30:55 +0000 Subject: OS-2358 zfs should not allow snapshot of inconsistent dataset Reviewed by: Bill Pijewski --- usr/src/uts/common/fs/zfs/dmu_send.c | 4 ++-- usr/src/uts/common/fs/zfs/dsl_dataset.c | 25 ++++++++++++++++++++++--- usr/src/uts/common/fs/zfs/sys/dsl_dataset.h | 2 +- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/usr/src/uts/common/fs/zfs/dmu_send.c b/usr/src/uts/common/fs/zfs/dmu_send.c index cbbde2071b..843516325e 100644 --- a/usr/src/uts/common/fs/zfs/dmu_send.c +++ b/usr/src/uts/common/fs/zfs/dmu_send.c @@ -1553,7 +1553,7 @@ dmu_recv_end_check(void *arg, dmu_tx_t *tx) return (error); } error = dsl_dataset_snapshot_check_impl(origin_head, - drc->drc_tosnap, tx); + drc->drc_tosnap, tx, B_TRUE); dsl_dataset_rele(origin_head, FTAG); if (error != 0) return (error); @@ -1561,7 +1561,7 @@ dmu_recv_end_check(void *arg, dmu_tx_t *tx) error = dsl_destroy_head_check_impl(drc->drc_ds, 1); } else { error = dsl_dataset_snapshot_check_impl(drc->drc_ds, - drc->drc_tosnap, tx); + drc->drc_tosnap, tx, B_TRUE); } return (error); } diff --git a/usr/src/uts/common/fs/zfs/dsl_dataset.c b/usr/src/uts/common/fs/zfs/dsl_dataset.c index d59b6fa052..cb62fd7c7e 100644 --- a/usr/src/uts/common/fs/zfs/dsl_dataset.c +++ b/usr/src/uts/common/fs/zfs/dsl_dataset.c @@ -947,7 +947,7 @@ typedef struct dsl_dataset_snapshot_arg { int dsl_dataset_snapshot_check_impl(dsl_dataset_t *ds, const char *snapname, - dmu_tx_t *tx) + dmu_tx_t *tx, boolean_t recv) { int error; uint64_t value; @@ -973,6 +973,24 @@ dsl_dataset_snapshot_check_impl(dsl_dataset_t *ds, const char *snapname, if (error != ENOENT) return (error); + /* + * We don't allow taking snapshots of inconsistent datasets, such as + * those into which we are currently receiving. However, if we are + * creating this snapshot as part of a receive, this check will be + * executed atomically with respect to the completion of the receive + * itself but prior to the clearing of DS_FLAG_INCONSISTENT; in this + * case we ignore this, knowing it will be fixed up for us shortly in + * dmu_recv_end_sync(). + */ + if (!recv) { + mutex_enter(&ds->ds_lock); + if (DS_IS_INCONSISTENT(ds)) { + mutex_exit(&ds->ds_lock); + return (SET_ERROR(EBUSY)); + } + mutex_exit(&ds->ds_lock); + } + error = dsl_dataset_snapshot_reserve_space(ds, tx); if (error != 0) return (error); @@ -1009,7 +1027,7 @@ dsl_dataset_snapshot_check(void *arg, dmu_tx_t *tx) error = dsl_dataset_hold(dp, dsname, FTAG, &ds); if (error == 0) { error = dsl_dataset_snapshot_check_impl(ds, - atp + 1, tx); + atp + 2, tx, B_FALSE); dsl_dataset_rele(ds, FTAG); } @@ -1262,7 +1280,8 @@ dsl_dataset_snapshot_tmp_check(void *arg, dmu_tx_t *tx) if (error != 0) return (error); - error = dsl_dataset_snapshot_check_impl(ds, ddsta->ddsta_snapname, tx); + error = dsl_dataset_snapshot_check_impl(ds, ddsta->ddsta_snapname, + tx, B_FALSE); if (error != 0) { dsl_dataset_rele(ds, FTAG); return (error); diff --git a/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h b/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h index cbb6f86539..58e4c58286 100644 --- a/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h +++ b/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h @@ -251,7 +251,7 @@ int dsl_dataset_clone_swap_check_impl(dsl_dataset_t *clone, void dsl_dataset_clone_swap_sync_impl(dsl_dataset_t *clone, dsl_dataset_t *origin_head, dmu_tx_t *tx); int dsl_dataset_snapshot_check_impl(dsl_dataset_t *ds, const char *snapname, - dmu_tx_t *tx); + dmu_tx_t *tx, boolean_t recv); void dsl_dataset_snapshot_sync_impl(dsl_dataset_t *ds, const char *snapname, dmu_tx_t *tx); -- cgit v1.2.3