summaryrefslogtreecommitdiff
path: root/usr/src/cmd/zfs
diff options
context:
space:
mode:
authorRichard Lowe <richlowe@richlowe.net>2011-11-08 17:01:06 -0500
committerRichard Lowe <richlowe@richlowe.net>2011-11-08 17:01:06 -0500
commit8ac09fcebf848c31516b15ce92861de4b2f514e8 (patch)
tree15f875666889194a611f93f451d6996168ed6c67 /usr/src/cmd/zfs
parent36a00406f380da1f3fd86e1a6af2de4d9f64633c (diff)
downloadillumos-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.c457
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