summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorlling <none@none>2006-01-18 19:34:00 -0800
committerlling <none@none>2006-01-18 19:34:00 -0800
commitb12a1c38fc215cc54fa6014069fd2b8dbb496646 (patch)
treecdb14f308885b43eb50e055196988d4f1ad7974d /usr/src
parentba1637f8b78b432c41b36839c92aecf1f5f9fafb (diff)
downloadillumos-gate-b12a1c38fc215cc54fa6014069fd2b8dbb496646.tar.gz
6364129 need to unmount any child datasets before doing the rollback
6364126 cannot rollback when a child filesystem is created after a snapshot is taken 6350233 Cannot rename filesystem|volume while it has dependent clones
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/zfs/zfs_main.c108
-rw-r--r--usr/src/lib/libzfs/common/libzfs.h4
-rw-r--r--usr/src/lib/libzfs/common/libzfs_changelist.c83
-rw-r--r--usr/src/lib/libzfs/common/libzfs_dataset.c115
-rw-r--r--usr/src/lib/libzfs/common/libzfs_impl.h3
5 files changed, 199 insertions, 114 deletions
diff --git a/usr/src/cmd/zfs/zfs_main.c b/usr/src/cmd/zfs/zfs_main.c
index 856cf51526..7a9de1cf95 100644
--- a/usr/src/cmd/zfs/zfs_main.c
+++ b/usr/src/cmd/zfs/zfs_main.c
@@ -1284,7 +1284,6 @@ error:
typedef struct rollback_cbdata {
uint64_t cb_create;
int cb_first;
- int cb_force;
int cb_doclones;
char *cb_target;
int cb_error;
@@ -1308,6 +1307,7 @@ rollback_check(zfs_handle_t *zhp, void *data)
if (!cbp->cb_dependent) {
if (strcmp(zfs_get_name(zhp), cbp->cb_target) != 0 &&
+ zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
cbp->cb_create) {
@@ -1352,83 +1352,22 @@ rollback_check(zfs_handle_t *zhp, void *data)
return (0);
}
-/*
- * Unmount any filesystems or snapshots that will need to be destroyed as part
- * of the rollback process.
- */
-static int
-rollback_unmount(zfs_handle_t *zhp, void *data)
-{
- rollback_cbdata_t *cbp = data;
-
- if (!cbp->cb_dependent) {
- if (strcmp(zfs_get_name(zhp), cbp->cb_target) != 0 &&
- zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
- cbp->cb_create) {
-
- cbp->cb_dependent = TRUE;
- (void) zfs_iter_dependents(zhp, rollback_unmount, cbp);
- cbp->cb_dependent = FALSE;
-
- if (zfs_unmount(zhp, NULL,
- cbp->cb_force ? MS_FORCE: 0) != 0)
- cbp->cb_error = 1;
- }
- } else if (zfs_unmount(zhp, NULL, cbp->cb_force ? MS_FORCE : 0) != 0) {
- cbp->cb_error = 1;
- }
-
- zfs_close(zhp);
- return (0);
-}
-
-/*
- * Destroy any more recent snapshots. We invoke this callback on any dependents
- * of the snapshot first. If the 'cb_dependent' member is non-zero, then this
- * is a dependent and we should just destroy it without checking the transaction
- * group.
- */
-static int
-rollback_destroy(zfs_handle_t *zhp, void *data)
-{
- rollback_cbdata_t *cbp = data;
-
- if (!cbp->cb_dependent) {
- if (strcmp(zfs_get_name(zhp), cbp->cb_target) != 0 &&
- zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
- cbp->cb_create) {
-
- cbp->cb_dependent = TRUE;
- (void) zfs_iter_dependents(zhp, rollback_destroy, cbp);
- cbp->cb_dependent = FALSE;
-
- if (zfs_destroy(zhp) != 0)
- cbp->cb_error = 1;
- }
- } else if (zfs_destroy(zhp) != 0) {
- cbp->cb_error = 1;
- }
-
- zfs_close(zhp);
- return (0);
-}
-
static int
zfs_do_rollback(int argc, char **argv)
{
int ret;
int c;
rollback_cbdata_t cb = { 0 };
- int was_mounted;
zfs_handle_t *zhp, *snap;
char parentname[ZFS_MAXNAMELEN];
char *delim;
+ int force = 0;
/* check options */
while ((c = getopt(argc, argv, "rfR")) != -1) {
switch (c) {
case 'f':
- cb.cb_force = TRUE;
+ force = 1;
break;
case 'r':
cb.cb_recurse = 1;
@@ -1457,13 +1396,12 @@ zfs_do_rollback(int argc, char **argv)
usage(FALSE);
}
- cb.cb_target = argv[0];
-
/* open the snapshot */
- if ((snap = zfs_open(cb.cb_target, ZFS_TYPE_SNAPSHOT)) == NULL)
+ if ((snap = zfs_open(argv[0], ZFS_TYPE_SNAPSHOT)) == NULL)
return (1);
- (void) strlcpy(parentname, cb.cb_target, sizeof (parentname));
+ /* open the parent dataset */
+ (void) strlcpy(parentname, argv[0], sizeof (parentname));
verify((delim = strrchr(parentname, '@')) != NULL);
*delim = '\0';
if ((zhp = zfs_open(parentname, ZFS_TYPE_ANY)) == NULL) {
@@ -1471,15 +1409,12 @@ zfs_do_rollback(int argc, char **argv)
return (1);
}
- /* See if this dataset is mounted */
- was_mounted = zfs_is_mounted(zhp, NULL);
-
- cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
-
/*
* Check for more recent snapshots and/or clones based on the presence
* of '-r' and '-R'.
*/
+ cb.cb_target = argv[0];
+ cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
cb.cb_first = 1;
cb.cb_error = 0;
(void) zfs_iter_children(zhp, rollback_check, &cb);
@@ -1487,33 +1422,10 @@ zfs_do_rollback(int argc, char **argv)
if ((ret = cb.cb_error) != 0)
goto out;
- cb.cb_error = 0;
-
- /*
- * Unmount any snapshots as well as the dataset itself.
- */
- if ((ret = zfs_iter_children(zhp, rollback_unmount,
- &cb)) != 0 || (ret = zfs_unmount(zhp, NULL,
- cb.cb_force ? MS_FORCE : 0)) != 0)
- goto out;
-
- (void) zfs_iter_children(zhp, rollback_destroy, &cb);
-
- if ((ret = cb.cb_error) != 0)
- goto out;
-
- /*
- * Now that we have verified that the snapshot is the latest, rollback
- * to the given snapshot.
- */
- ret = zfs_rollback(zhp);
-
/*
- * We only want to re-mount the filesystem if it was mounted in the
- * first place.
+ * Rollback parent to the given snapshot.
*/
- if (was_mounted)
- (void) zfs_mount(zhp, NULL, 0);
+ ret = zfs_rollback(zhp, snap, force);
out:
zfs_close(snap);
diff --git a/usr/src/lib/libzfs/common/libzfs.h b/usr/src/lib/libzfs/common/libzfs.h
index 525baa2066..63e394c376 100644
--- a/usr/src/lib/libzfs/common/libzfs.h
+++ b/usr/src/lib/libzfs/common/libzfs.h
@@ -20,7 +20,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -204,7 +204,7 @@ extern int zfs_create(const char *, zfs_type_t, const char *, const char *);
extern int zfs_destroy(zfs_handle_t *);
extern int zfs_clone(zfs_handle_t *, const char *);
extern int zfs_snapshot(const char *);
-extern int zfs_rollback(zfs_handle_t *);
+extern int zfs_rollback(zfs_handle_t *, zfs_handle_t *, int);
extern int zfs_rename(zfs_handle_t *, const char *);
extern int zfs_backup(zfs_handle_t *, zfs_handle_t *);
extern int zfs_restore(const char *, int, int, int);
diff --git a/usr/src/lib/libzfs/common/libzfs_changelist.c b/usr/src/lib/libzfs/common/libzfs_changelist.c
index 497461e19f..57fcc1497c 100644
--- a/usr/src/lib/libzfs/common/libzfs_changelist.c
+++ b/usr/src/lib/libzfs/common/libzfs_changelist.c
@@ -20,7 +20,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -54,6 +54,7 @@
*
* Other interfaces:
*
+ * changelist_remove() - remove a node from a gathered list
* changelist_rename() - renames all datasets appropriately when doing a rename
* changelist_unshare() - unshares all the nodes in a given changelist
* changelist_haszonedchild() - check if there is any child exported to
@@ -74,6 +75,7 @@ struct prop_changelist {
uu_list_t *cl_list;
int cl_waslegacy;
int cl_allchildren;
+ int cl_alldependents;
int cl_flags;
int cl_haszonedchild;
};
@@ -195,6 +197,28 @@ changelist_postfix(prop_changelist_t *clp)
}
/*
+ * Is this "dataset" a child of "parent"?
+ */
+static int
+isa_child_of(char *dataset, const char *parent)
+{
+ int len;
+
+ /* snapshot does not have a child */
+ if (strchr(parent, '@'))
+ return (FALSE);
+
+ len = strlen(parent);
+
+ if (strncmp(dataset, parent, len) == 0 &&
+ (dataset[len] == '/' || dataset[len] == '\0'))
+ return (TRUE);
+ else
+ return (FALSE);
+
+}
+
+/*
* If we rename a filesystem, and child filesystem handles are no longer valid,
* since we identify datasets by their name in the ZFS namespace. So, we have
* to go through and fix up all the names appropriately. We could do this
@@ -210,6 +234,12 @@ changelist_rename(prop_changelist_t *clp, const char *src, const char *dst)
for (cn = uu_list_first(clp->cl_list); cn != NULL;
cn = uu_list_next(clp->cl_list, cn)) {
/*
+ * Do not rename a clone that's not in the source hierarchy.
+ */
+ if (!isa_child_of(cn->cn_handle->zfs_name, src))
+ continue;
+
+ /*
* Destroy the previous mountpoint if needed.
*/
remove_mountpoint(cn->cn_handle);
@@ -257,6 +287,26 @@ changelist_haszonedchild(prop_changelist_t *clp)
}
/*
+ * Remove a node from a gathered list.
+ */
+void
+changelist_remove(zfs_handle_t *zhp, prop_changelist_t *clp)
+{
+ prop_changenode_t *cn;
+
+ for (cn = uu_list_first(clp->cl_list); cn != NULL;
+ cn = uu_list_next(clp->cl_list, cn)) {
+
+ if (strcmp(cn->cn_handle->zfs_name, zhp->zfs_name) == 0) {
+ uu_list_remove(clp->cl_list, cn);
+ zfs_close(cn->cn_handle);
+ free(cn);
+ return;
+ }
+ }
+}
+
+/*
* Release any memory associated with a changelist.
*/
void
@@ -306,8 +356,8 @@ change_one(zfs_handle_t *zhp, void *data)
FALSE) != 0)
return (0);
- if (clp->cl_allchildren || sourcetype == ZFS_SRC_DEFAULT ||
- sourcetype == ZFS_SRC_INHERITED) {
+ if (clp->cl_alldependents || clp->cl_allchildren ||
+ sourcetype == ZFS_SRC_DEFAULT || sourcetype == ZFS_SRC_INHERITED) {
cn = zfs_malloc(sizeof (prop_changenode_t));
cn->cn_handle = zhp;
@@ -320,10 +370,16 @@ change_one(zfs_handle_t *zhp, void *data)
clp->cl_haszonedchild = TRUE;
uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool);
- verify(uu_list_insert_before(clp->cl_list,
- uu_list_first(clp->cl_list), cn) == 0);
- return (zfs_iter_children(zhp, change_one, data));
+ if (clp->cl_alldependents) {
+ verify(uu_list_insert_after(clp->cl_list,
+ uu_list_last(clp->cl_list), cn) == 0);
+ return (0);
+ } else {
+ verify(uu_list_insert_before(clp->cl_list,
+ uu_list_first(clp->cl_list), cn) == 0);
+ return (zfs_iter_children(zhp, change_one, data));
+ }
} else {
zfs_close(zhp);
}
@@ -361,8 +417,14 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags)
* If this is a rename or the 'zoned' property, we pretend we're
* changing the mountpoint and flag it so we can catch all children in
* change_one().
+ *
+ * Flag cl_alldependents to catch all children plus the
+ * dependents (clones) that are not in the hierarchy.
*/
- if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED) {
+ if (prop == ZFS_PROP_NAME) {
+ clp->cl_prop = ZFS_PROP_MOUNTPOINT;
+ clp->cl_alldependents = TRUE;
+ } else if (prop == ZFS_PROP_ZONED) {
clp->cl_prop = ZFS_PROP_MOUNTPOINT;
clp->cl_allchildren = TRUE;
} else {
@@ -374,7 +436,12 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags)
clp->cl_prop != ZFS_PROP_SHARENFS)
return (clp);
- if (zfs_iter_children(zhp, change_one, clp) != 0) {
+ if (clp->cl_alldependents) {
+ if (zfs_iter_dependents(zhp, change_one, clp) != 0) {
+ changelist_free(clp);
+ return (NULL);
+ }
+ } else if (zfs_iter_children(zhp, change_one, clp) != 0) {
changelist_free(clp);
return (NULL);
}
diff --git a/usr/src/lib/libzfs/common/libzfs_dataset.c b/usr/src/lib/libzfs/common/libzfs_dataset.c
index 95a1b6a38a..1299a42f3f 100644
--- a/usr/src/lib/libzfs/common/libzfs_dataset.c
+++ b/usr/src/lib/libzfs/common/libzfs_dataset.c
@@ -20,7 +20,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -39,6 +39,7 @@
#include <zone.h>
#include <sys/mntent.h>
#include <sys/mnttab.h>
+#include <sys/mount.h>
#include <sys/spa.h>
#include <sys/zio.h>
@@ -2589,11 +2590,55 @@ zfs_restore(const char *tosnap, int isprefix, int verbose, int dryrun)
}
/*
- * Rollback the given dataset to the previous snapshot. It is up to the caller
- * to verify that there is a previous snapshot available.
+ * Destroy any more recent snapshots. We invoke this callback on any dependents
+ * of the snapshot first. If the 'cb_dependent' member is non-zero, then this
+ * is a dependent and we should just destroy it without checking the transaction
+ * group.
*/
-int
-zfs_rollback(zfs_handle_t *zhp)
+typedef struct rollback_data {
+ const char *cb_target; /* the snapshot */
+ uint64_t cb_create; /* creation time reference */
+ prop_changelist_t *cb_clp; /* changelist pointer */
+ int cb_error;
+ int cb_dependent;
+} rollback_data_t;
+
+static int
+rollback_destroy(zfs_handle_t *zhp, void *data)
+{
+ rollback_data_t *cbp = data;
+
+ if (!cbp->cb_dependent) {
+ if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 &&
+ zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
+ zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
+ cbp->cb_create) {
+
+ cbp->cb_dependent = TRUE;
+ (void) zfs_iter_dependents(zhp, rollback_destroy, cbp);
+ cbp->cb_dependent = FALSE;
+
+ if (zfs_destroy(zhp) != 0)
+ cbp->cb_error = 1;
+ else
+ changelist_remove(zhp, cbp->cb_clp);
+ }
+ } else {
+ if (zfs_destroy(zhp) != 0)
+ cbp->cb_error = 1;
+ else
+ changelist_remove(zhp, cbp->cb_clp);
+ }
+
+ zfs_close(zhp);
+ return (0);
+}
+
+/*
+ * Rollback the dataset to its latest snapshot.
+ */
+static int
+do_rollback(zfs_handle_t *zhp)
{
int ret;
zfs_cmd_t zc = { 0 };
@@ -2677,6 +2722,66 @@ zfs_rollback(zfs_handle_t *zhp)
}
/*
+ * Given a dataset, rollback to a specific snapshot, discarding any
+ * data changes since then and making it the active dataset.
+ *
+ * Any snapshots more recent than the target are destroyed, along with
+ * their dependents.
+ */
+int
+zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, int flag)
+{
+ int ret;
+ rollback_data_t cb = { 0 };
+ prop_changelist_t *clp;
+
+ /*
+ * Unmount all dependendents of the dataset and the dataset itself.
+ * The list we need to gather is the same as for doing rename
+ */
+ clp = changelist_gather(zhp, ZFS_PROP_NAME, flag ? MS_FORCE: 0);
+ if (clp == NULL)
+ return (-1);
+
+ if ((ret = changelist_prefix(clp)) != 0)
+ goto out;
+
+ /*
+ * Destroy all recent snapshots and its dependends.
+ */
+ cb.cb_target = snap->zfs_name;
+ cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
+ cb.cb_clp = clp;
+ (void) zfs_iter_children(zhp, rollback_destroy, &cb);
+
+ if ((ret = cb.cb_error) != 0) {
+ (void) changelist_postfix(clp);
+ goto out;
+ }
+
+ /*
+ * Now that we have verified that the snapshot is the latest,
+ * rollback to the given snapshot.
+ */
+ ret = do_rollback(zhp);
+
+ if (ret != 0) {
+ (void) changelist_postfix(clp);
+ goto out;
+ }
+
+ /*
+ * We only want to re-mount the filesystem if it was mounted in the
+ * first place.
+ */
+ ret = changelist_postfix(clp);
+
+out:
+ changelist_free(clp);
+ return (ret);
+}
+
+/*
* Iterate over all dependents for a given dataset. This includes both
* hierarchical dependents (children) and data dependents (snapshots and
* clones). The bulk of the processing occurs in get_dependents() in
diff --git a/usr/src/lib/libzfs/common/libzfs_impl.h b/usr/src/lib/libzfs/common/libzfs_impl.h
index c9e51de33b..cb1400b1b0 100644
--- a/usr/src/lib/libzfs/common/libzfs_impl.h
+++ b/usr/src/lib/libzfs/common/libzfs_impl.h
@@ -20,7 +20,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -82,6 +82,7 @@ typedef struct prop_changelist prop_changelist_t;
int changelist_prefix(prop_changelist_t *);
int changelist_postfix(prop_changelist_t *);
void changelist_rename(prop_changelist_t *, const char *, const char *);
+void changelist_remove(zfs_handle_t *, prop_changelist_t *);
void changelist_free(prop_changelist_t *);
prop_changelist_t *changelist_gather(zfs_handle_t *, zfs_prop_t, int);
int changelist_unshare(prop_changelist_t *);