diff options
| author | lling <none@none> | 2006-01-18 19:34:00 -0800 |
|---|---|---|
| committer | lling <none@none> | 2006-01-18 19:34:00 -0800 |
| commit | b12a1c38fc215cc54fa6014069fd2b8dbb496646 (patch) | |
| tree | cdb14f308885b43eb50e055196988d4f1ad7974d /usr/src | |
| parent | ba1637f8b78b432c41b36839c92aecf1f5f9fafb (diff) | |
| download | illumos-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.c | 108 | ||||
| -rw-r--r-- | usr/src/lib/libzfs/common/libzfs.h | 4 | ||||
| -rw-r--r-- | usr/src/lib/libzfs/common/libzfs_changelist.c | 83 | ||||
| -rw-r--r-- | usr/src/lib/libzfs/common/libzfs_dataset.c | 115 | ||||
| -rw-r--r-- | usr/src/lib/libzfs/common/libzfs_impl.h | 3 |
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 *); |
