diff options
author | Richard Lowe <richlowe@richlowe.net> | 2011-11-08 17:01:06 -0500 |
---|---|---|
committer | Richard Lowe <richlowe@richlowe.net> | 2011-11-08 17:01:06 -0500 |
commit | 8ac09fcebf848c31516b15ce92861de4b2f514e8 (patch) | |
tree | 15f875666889194a611f93f451d6996168ed6c67 /usr/src/cmd/zfs | |
parent | 36a00406f380da1f3fd86e1a6af2de4d9f64633c (diff) | |
download | illumos-joyent-8ac09fcebf848c31516b15ce92861de4b2f514e8.tar.gz |
backout 1644/1645/1646/1647/1708: Breaks 'zfs snapshot', boot environments
Diffstat (limited to 'usr/src/cmd/zfs')
-rw-r--r-- | usr/src/cmd/zfs/zfs_main.c | 457 |
1 files changed, 135 insertions, 322 deletions
diff --git a/usr/src/cmd/zfs/zfs_main.c b/usr/src/cmd/zfs/zfs_main.c index a6302da0c2..94deec6077 100644 --- a/usr/src/cmd/zfs/zfs_main.c +++ b/usr/src/cmd/zfs/zfs_main.c @@ -22,7 +22,6 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2011 Nexenta Systems, Inc. All rights reserved. - * Copyright (c) 2011 by Delphix. All rights reserved. */ #include <assert.h> @@ -139,7 +138,7 @@ typedef enum { HELP_HOLD, HELP_HOLDS, HELP_RELEASE, - HELP_DIFF, + HELP_DIFF } zfs_help_t; typedef struct zfs_command { @@ -211,9 +210,8 @@ get_usage(zfs_help_t idx) "\tcreate [-ps] [-b blocksize] [-o property=value] ... " "-V <size> <volume>\n")); case HELP_DESTROY: - return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n" - "\tdestroy [-dnpRrv] " - "<filesystem|volume>@<snap>[%<snap>][,...]\n")); + return (gettext("\tdestroy [-rRf] <filesystem|volume>\n" + "\tdestroy [-rRd] <snapshot>\n")); case HELP_GET: return (gettext("\tget [-rHp] [-d max] " "[-o \"all\" | field[,...]] [-s source[,...]]\n" @@ -247,8 +245,7 @@ get_usage(zfs_help_t idx) case HELP_ROLLBACK: return (gettext("\trollback [-rRf] <snapshot>\n")); case HELP_SEND: - return (gettext("\tsend [-DnPpRrv] [-[iI] snapshot] " - "<snapshot>\n")); + return (gettext("\tsend [-RDp] [-[iI] snapshot] <snapshot>\n")); case HELP_SET: return (gettext("\tset <property=value> " "<filesystem|volume|snapshot> ...\n")); @@ -427,8 +424,6 @@ usage(boolean_t requested) (void) fprintf(fp, "YES NO <size> | none\n"); (void) fprintf(fp, "\t%-15s ", "groupquota@..."); (void) fprintf(fp, "YES NO <size> | none\n"); - (void) fprintf(fp, "\t%-15s ", "written@<snap>"); - (void) fprintf(fp, " NO NO <size>\n"); (void) fprintf(fp, gettext("\nSizes are specified in bytes " "with standard units such as K, M, G, etc.\n")); @@ -874,23 +869,15 @@ badusage: */ typedef struct destroy_cbdata { boolean_t cb_first; - boolean_t cb_force; - boolean_t cb_recurse; - boolean_t cb_error; - boolean_t cb_doclones; + int cb_force; + int cb_recurse; + int cb_error; + int cb_needforce; + int cb_doclones; + boolean_t cb_closezhp; zfs_handle_t *cb_target; + char *cb_snapname; boolean_t cb_defer_destroy; - boolean_t cb_verbose; - boolean_t cb_parsable; - boolean_t cb_noop; - nvlist_t *cb_nvl; - - /* first snap in contiguous run */ - zfs_handle_t *cb_firstsnap; - /* previous snap in contiguous run */ - zfs_handle_t *cb_prevsnap; - int64_t cb_snapused; - char *cb_snapspec; } destroy_cbdata_t; /* @@ -920,7 +907,7 @@ destroy_check_dependent(zfs_handle_t *zhp, void *data) (void) fprintf(stderr, gettext("use '-r' to destroy " "the following datasets:\n")); cbp->cb_first = B_FALSE; - cbp->cb_error = B_TRUE; + cbp->cb_error = 1; } (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); @@ -941,8 +928,7 @@ destroy_check_dependent(zfs_handle_t *zhp, void *data) (void) fprintf(stderr, gettext("use '-R' to destroy " "the following datasets:\n")); cbp->cb_first = B_FALSE; - cbp->cb_error = B_TRUE; - cbp->cb_noop = B_TRUE; + cbp->cb_error = 1; } (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); @@ -956,20 +942,7 @@ out: static int destroy_callback(zfs_handle_t *zhp, void *data) { - destroy_cbdata_t *cb = data; - const char *name = zfs_get_name(zhp); - - if (cb->cb_verbose) { - if (cb->cb_parsable) { - (void) printf("destroy\t%s\n", name); - } else if (cb->cb_noop) { - (void) printf(gettext("would destroy %s\n"), - name); - } else { - (void) printf(gettext("will destroy %s\n"), - name); - } - } + destroy_cbdata_t *cbp = data; /* * Ignore pools (which we've already flagged as an error before getting @@ -981,12 +954,13 @@ destroy_callback(zfs_handle_t *zhp, void *data) return (0); } - if (!cb->cb_noop) { - if (zfs_unmount(zhp, NULL, cb->cb_force ? MS_FORCE : 0) != 0 || - zfs_destroy(zhp, cb->cb_defer_destroy) != 0) { - zfs_close(zhp); - return (-1); - } + /* + * Bail out on the first error. + */ + if (zfs_unmount(zhp, NULL, cbp->cb_force ? MS_FORCE : 0) != 0 || + zfs_destroy(zhp, cbp->cb_defer_destroy) != 0) { + zfs_close(zhp); + return (-1); } zfs_close(zhp); @@ -994,142 +968,39 @@ destroy_callback(zfs_handle_t *zhp, void *data) } static int -destroy_print_cb(zfs_handle_t *zhp, void *arg) +destroy_snap_clones(zfs_handle_t *zhp, void *arg) { - destroy_cbdata_t *cb = arg; - const char *name = zfs_get_name(zhp); - int err = 0; - - if (nvlist_exists(cb->cb_nvl, name)) { - if (cb->cb_firstsnap == NULL) - cb->cb_firstsnap = zfs_handle_dup(zhp); - if (cb->cb_prevsnap != NULL) - zfs_close(cb->cb_prevsnap); - /* this snap continues the current range */ - cb->cb_prevsnap = zfs_handle_dup(zhp); - if (cb->cb_verbose) { - if (cb->cb_parsable) { - (void) printf("destroy\t%s\n", name); - } else if (cb->cb_noop) { - (void) printf(gettext("would destroy %s\n"), - name); - } else { - (void) printf(gettext("will destroy %s\n"), - name); - } - } - } else if (cb->cb_firstsnap != NULL) { - /* end of this range */ - uint64_t used = 0; - err = zfs_get_snapused_int(cb->cb_firstsnap, - cb->cb_prevsnap, &used); - cb->cb_snapused += used; - zfs_close(cb->cb_firstsnap); - cb->cb_firstsnap = NULL; - zfs_close(cb->cb_prevsnap); - cb->cb_prevsnap = NULL; - } - zfs_close(zhp); - return (err); -} + destroy_cbdata_t *cbp = arg; + char thissnap[MAXPATHLEN]; + zfs_handle_t *szhp; + boolean_t closezhp = cbp->cb_closezhp; + int rv; -static int -destroy_print_snapshots(zfs_handle_t *fs_zhp, destroy_cbdata_t *cb) -{ - int err; - assert(cb->cb_firstsnap == NULL); - assert(cb->cb_prevsnap == NULL); - err = zfs_iter_snapshots_sorted(fs_zhp, destroy_print_cb, cb); - if (cb->cb_firstsnap != NULL) { - uint64_t used = 0; - if (err == 0) { - err = zfs_get_snapused_int(cb->cb_firstsnap, - cb->cb_prevsnap, &used); - } - cb->cb_snapused += used; - zfs_close(cb->cb_firstsnap); - cb->cb_firstsnap = NULL; - zfs_close(cb->cb_prevsnap); - cb->cb_prevsnap = NULL; - } - return (err); -} - -static int -snapshot_to_nvl_cb(zfs_handle_t *zhp, void *arg) -{ - destroy_cbdata_t *cb = arg; - int err = 0; - - /* Check for clones. */ - if (!cb->cb_doclones) { - cb->cb_target = zhp; - cb->cb_first = B_TRUE; - err = zfs_iter_dependents(zhp, B_TRUE, - destroy_check_dependent, cb); - } - - if (err == 0) { - if (nvlist_add_boolean(cb->cb_nvl, zfs_get_name(zhp))) - nomem(); - } - zfs_close(zhp); - return (err); -} + (void) snprintf(thissnap, sizeof (thissnap), + "%s@%s", zfs_get_name(zhp), cbp->cb_snapname); -static int -gather_snapshots(zfs_handle_t *zhp, void *arg) -{ - destroy_cbdata_t *cb = arg; - int err = 0; - - err = zfs_iter_snapspec(zhp, cb->cb_snapspec, snapshot_to_nvl_cb, cb); - if (err == ENOENT) - err = 0; - if (err != 0) - goto out; - - if (cb->cb_verbose) { - err = destroy_print_snapshots(zhp, cb); - if (err != 0) - goto out; - } - - if (cb->cb_recurse) - err = zfs_iter_filesystems(zhp, gather_snapshots, cb); - -out: - zfs_close(zhp); - return (err); -} - -static int -destroy_clones(destroy_cbdata_t *cb) -{ - nvpair_t *pair; - for (pair = nvlist_next_nvpair(cb->cb_nvl, NULL); - pair != NULL; - pair = nvlist_next_nvpair(cb->cb_nvl, pair)) { - zfs_handle_t *zhp = zfs_open(g_zfs, nvpair_name(pair), - ZFS_TYPE_SNAPSHOT); - if (zhp != NULL) { - boolean_t defer = cb->cb_defer_destroy; - int err; - - /* - * We can't defer destroy non-snapshots, so set it to - * false while destroying the clones. - */ - cb->cb_defer_destroy = B_FALSE; - err = zfs_iter_dependents(zhp, B_FALSE, - destroy_callback, cb); - cb->cb_defer_destroy = defer; - zfs_close(zhp); - if (err != 0) - return (err); + libzfs_print_on_error(g_zfs, B_FALSE); + szhp = zfs_open(g_zfs, thissnap, ZFS_TYPE_SNAPSHOT); + libzfs_print_on_error(g_zfs, B_TRUE); + if (szhp) { + /* + * Destroy any clones of this snapshot + */ + if (zfs_iter_dependents(szhp, B_FALSE, destroy_callback, + cbp) != 0) { + zfs_close(szhp); + if (closezhp) + zfs_close(zhp); + return (-1); } + zfs_close(szhp); } - return (0); + + cbp->cb_closezhp = B_TRUE; + rv = zfs_iter_filesystems(zhp, destroy_snap_clones, arg); + if (closezhp) + zfs_close(zhp); + return (rv); } static int @@ -1138,35 +1009,25 @@ zfs_do_destroy(int argc, char **argv) destroy_cbdata_t cb = { 0 }; int c; zfs_handle_t *zhp; - char *at; + char *cp; zfs_type_t type = ZFS_TYPE_DATASET; /* check options */ - while ((c = getopt(argc, argv, "vpndfrR")) != -1) { + while ((c = getopt(argc, argv, "dfrR")) != -1) { switch (c) { - case 'v': - cb.cb_verbose = B_TRUE; - break; - case 'p': - cb.cb_verbose = B_TRUE; - cb.cb_parsable = B_TRUE; - break; - case 'n': - cb.cb_noop = B_TRUE; - break; case 'd': cb.cb_defer_destroy = B_TRUE; type = ZFS_TYPE_SNAPSHOT; break; case 'f': - cb.cb_force = B_TRUE; + cb.cb_force = 1; break; case 'r': - cb.cb_recurse = B_TRUE; + cb.cb_recurse = 1; break; case 'R': - cb.cb_recurse = B_TRUE; - cb.cb_doclones = B_TRUE; + cb.cb_recurse = 1; + cb.cb_doclones = 1; break; case '?': default: @@ -1181,7 +1042,7 @@ zfs_do_destroy(int argc, char **argv) /* check number of arguments */ if (argc == 0) { - (void) fprintf(stderr, gettext("missing dataset argument\n")); + (void) fprintf(stderr, gettext("missing path argument\n")); usage(B_FALSE); } if (argc > 1) { @@ -1189,117 +1050,91 @@ zfs_do_destroy(int argc, char **argv) usage(B_FALSE); } - at = strchr(argv[0], '@'); - if (at != NULL) { - int err; - - /* Build the list of snaps to destroy in cb_nvl. */ - if (nvlist_alloc(&cb.cb_nvl, NV_UNIQUE_NAME, 0) != 0) - nomem(); - - *at = '\0'; - zhp = zfs_open(g_zfs, argv[0], - ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); - if (zhp == NULL) - return (1); + /* + * If we are doing recursive destroy of a snapshot, then the + * named snapshot may not exist. Go straight to libzfs. + */ + if (cb.cb_recurse && (cp = strchr(argv[0], '@'))) { + int ret; - cb.cb_snapspec = at + 1; - if (gather_snapshots(zfs_handle_dup(zhp), &cb) != 0 || - cb.cb_error) { - zfs_close(zhp); - nvlist_free(cb.cb_nvl); + *cp = '\0'; + if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL) return (1); - } + *cp = '@'; + cp++; - if (nvlist_empty(cb.cb_nvl)) { - (void) fprintf(stderr, gettext("could not find any " - "snapshots to destroy; check snapshot names.\n")); - zfs_close(zhp); - nvlist_free(cb.cb_nvl); - return (1); - } + if (cb.cb_doclones) { + boolean_t defer = cb.cb_defer_destroy; - if (cb.cb_verbose) { - char buf[16]; - zfs_nicenum(cb.cb_snapused, buf, sizeof (buf)); - if (cb.cb_parsable) { - (void) printf("reclaim\t%llu\n", - cb.cb_snapused); - } else if (cb.cb_noop) { - (void) printf(gettext("would reclaim %s\n"), - buf); - } else { - (void) printf(gettext("will reclaim %s\n"), - buf); + /* + * Temporarily ignore the defer_destroy setting since + * it's not supported for clones. + */ + cb.cb_defer_destroy = B_FALSE; + cb.cb_snapname = cp; + if (destroy_snap_clones(zhp, &cb) != 0) { + zfs_close(zhp); + return (1); } + cb.cb_defer_destroy = defer; } - if (!cb.cb_noop) { - if (cb.cb_doclones) - err = destroy_clones(&cb); - if (err == 0) { - err = zfs_destroy_snaps_nvl(zhp, cb.cb_nvl, - cb.cb_defer_destroy); - } + ret = zfs_destroy_snaps(zhp, cp, cb.cb_defer_destroy); + zfs_close(zhp); + if (ret) { + (void) fprintf(stderr, + gettext("no snapshots destroyed\n")); } + return (ret != 0); + } - zfs_close(zhp); - nvlist_free(cb.cb_nvl); - if (err != 0) - return (1); - } else { - /* Open the given dataset */ - if ((zhp = zfs_open(g_zfs, argv[0], type)) == NULL) - return (1); + /* Open the given dataset */ + if ((zhp = zfs_open(g_zfs, argv[0], type)) == NULL) + return (1); - cb.cb_target = zhp; + cb.cb_target = zhp; - /* - * Perform an explicit check for pools before going any further. - */ - if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL && - zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { - (void) fprintf(stderr, gettext("cannot destroy '%s': " - "operation does not apply to pools\n"), - zfs_get_name(zhp)); - (void) fprintf(stderr, gettext("use 'zfs destroy -r " - "%s' to destroy all datasets in the pool\n"), - zfs_get_name(zhp)); - (void) fprintf(stderr, gettext("use 'zpool destroy %s' " - "to destroy the pool itself\n"), zfs_get_name(zhp)); - zfs_close(zhp); - return (1); - } + /* + * Perform an explicit check for pools before going any further. + */ + if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL && + zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { + (void) fprintf(stderr, gettext("cannot destroy '%s': " + "operation does not apply to pools\n"), + zfs_get_name(zhp)); + (void) fprintf(stderr, gettext("use 'zfs destroy -r " + "%s' to destroy all datasets in the pool\n"), + zfs_get_name(zhp)); + (void) fprintf(stderr, gettext("use 'zpool destroy %s' " + "to destroy the pool itself\n"), zfs_get_name(zhp)); + zfs_close(zhp); + return (1); + } - /* - * Check for any dependents and/or clones. - */ - cb.cb_first = B_TRUE; - if (!cb.cb_doclones && - zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent, - &cb) != 0) { - zfs_close(zhp); - return (1); - } + /* + * Check for any dependents and/or clones. + */ + cb.cb_first = B_TRUE; + if (!cb.cb_doclones && !cb.cb_defer_destroy && + zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent, + &cb) != 0) { + zfs_close(zhp); + return (1); + } - if (cb.cb_error) { - zfs_close(zhp); - return (1); - } + if (cb.cb_error || (!cb.cb_defer_destroy && + (zfs_iter_dependents(zhp, B_FALSE, destroy_callback, &cb) != 0))) { + zfs_close(zhp); + return (1); + } - if (zfs_iter_dependents(zhp, B_FALSE, destroy_callback, - &cb) != 0) { - zfs_close(zhp); - return (1); - } + /* + * Do the real thing. The callback will close the handle regardless of + * whether it succeeds or not. + */ - /* - * Do the real thing. The callback will close the - * handle regardless of whether it succeeds or not. - */ - if (destroy_callback(zhp, &cb) != 0) - return (1); - } + if (destroy_callback(zhp, &cb) != 0) + return (1); return (0); } @@ -1401,17 +1236,6 @@ get_callback(zfs_handle_t *zhp, void *data) zprop_print_one_property(zfs_get_name(zhp), cbp, pl->pl_user_prop, buf, sourcetype, source, NULL); - } else if (zfs_prop_written(pl->pl_user_prop)) { - sourcetype = ZPROP_SRC_LOCAL; - - if (zfs_prop_get_written(zhp, pl->pl_user_prop, - buf, sizeof (buf), cbp->cb_literal) != 0) { - sourcetype = ZPROP_SRC_NONE; - (void) strlcpy(buf, "-", sizeof (buf)); - } - - zprop_print_one_property(zfs_get_name(zhp), cbp, - pl->pl_user_prop, buf, sourcetype, source, NULL); } else { if (nvlist_lookup_nvlist(user_props, pl->pl_user_prop, &propval) != 0) { @@ -1956,8 +1780,8 @@ zfs_do_upgrade(int argc, char **argv) "---------------\n"); (void) printf(gettext(" 1 Initial ZFS filesystem version\n")); (void) printf(gettext(" 2 Enhanced directory entries\n")); - (void) printf(gettext(" 3 Case insensitive and filesystem " - "user identifier (FUID)\n")); + (void) printf(gettext(" 3 Case insensitive and File system " + "unique identifier (FUID)\n")); (void) printf(gettext(" 4 userquota, groupquota " "properties\n")); (void) printf(gettext(" 5 System attributes\n")); @@ -2828,13 +2652,6 @@ print_dataset(zfs_handle_t *zhp, zprop_list_t *pl, boolean_t scripted) else propstr = property; right_justify = B_TRUE; - } else if (zfs_prop_written(pl->pl_user_prop)) { - if (zfs_prop_get_written(zhp, pl->pl_user_prop, - property, sizeof (property), B_FALSE) != 0) - propstr = "-"; - else - propstr = property; - right_justify = B_TRUE; } else { if (nvlist_lookup_nvlist(userprops, pl->pl_user_prop, &propval) != 0) @@ -3446,6 +3263,9 @@ usage: } /* + * zfs send [-vDp] -R [-i|-I <@snap>] <fs@snap> + * zfs send [-vDp] [-i|-I <@snap>] <fs@snap> + * * Send a backup stream to stdout. */ static int @@ -3457,11 +3277,11 @@ zfs_do_send(int argc, char **argv) zfs_handle_t *zhp; sendflags_t flags = { 0 }; int c, err; - nvlist_t *dbgnv = NULL; + nvlist_t *dbgnv; boolean_t extraverbose = B_FALSE; /* check options */ - while ((c = getopt(argc, argv, ":i:I:RDpvnP")) != -1) { + while ((c = getopt(argc, argv, ":i:I:RDpv")) != -1) { switch (c) { case 'i': if (fromname) @@ -3480,10 +3300,6 @@ zfs_do_send(int argc, char **argv) case 'p': flags.props = B_TRUE; break; - case 'P': - flags.parsable = B_TRUE; - flags.verbose = B_TRUE; - break; case 'v': if (flags.verbose) extraverbose = B_TRUE; @@ -3492,9 +3308,6 @@ zfs_do_send(int argc, char **argv) case 'D': flags.dedup = B_TRUE; break; - case 'n': - flags.noop = B_TRUE; - break; case ':': (void) fprintf(stderr, gettext("missing argument for " "'%c' option\n"), optopt); @@ -3520,7 +3333,7 @@ zfs_do_send(int argc, char **argv) usage(B_FALSE); } - if (!flags.noop && isatty(STDOUT_FILENO)) { + if (isatty(STDOUT_FILENO)) { (void) fprintf(stderr, gettext("Error: Stream can not be written to a terminal.\n" "You must redirect standard output.\n")); @@ -3577,7 +3390,7 @@ zfs_do_send(int argc, char **argv) err = zfs_send(zhp, fromname, toname, flags, STDOUT_FILENO, NULL, 0, extraverbose ? &dbgnv : NULL); - if (extraverbose && dbgnv != NULL) { + if (extraverbose) { /* * dump_nvlist prints to stdout, but that's been * redirected to a file. Make it print to stderr |