]> git.proxmox.com Git - mirror_zfs.git/commitdiff
Illumos #3888
authorMatthew Ahrens <mahrens@delphix.com>
Mon, 29 Jul 2013 18:55:16 +0000 (10:55 -0800)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Mon, 4 Nov 2013 19:18:14 +0000 (11:18 -0800)
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@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 <behlendorf1@llnl.gov>
Ported-by: Richard Yao <ryao@gentoo.org>
Issue #1775

include/sys/dsl_dataset.h
include/sys/dsl_destroy.h
module/zfs/dmu_send.c
module/zfs/dsl_dataset.c
module/zfs/dsl_destroy.c

index e8e26b9f949661b8cd2755eacaae7261a0b53ab0..866a8976c16183f4e98ba53faadfc6a2c2741338 100644 (file)
@@ -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);
 
index c5a70bb90e4cba9e42a041bcf8813b7a81eb3b75..3f638643b6f8cc520521d87877dd3543092438ec 100644 (file)
@@ -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
 }
index e80631b48756d2b64a9ae3ce84366462d18ebd90..c5eb2fb794e2f0d1b3426829d6c9c84ac713295d 100644 (file)
@@ -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,
index 16d5dad4913dba547ecde428ad30cf78ef84815b..b04ef69bb7bccdb204df815b7c861835ded7af40 100644 (file)
@@ -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);
index 1c4c3deec78282cd50d1a20f6c3c70e15f637f2e..0e741105d31c572242d7c85ca207f29eaa7b5cf0 100644 (file)
@@ -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))