summaryrefslogtreecommitdiff
path: root/usr/src/cmd/zfs
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/zfs')
-rw-r--r--usr/src/cmd/zfs/zfs_main.c83
1 files changed, 58 insertions, 25 deletions
diff --git a/usr/src/cmd/zfs/zfs_main.c b/usr/src/cmd/zfs/zfs_main.c
index 909a97a99d..a72e915c17 100644
--- a/usr/src/cmd/zfs/zfs_main.c
+++ b/usr/src/cmd/zfs/zfs_main.c
@@ -22,6 +22,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2011 Joyent, Inc. All rights reserved.
* Copyright (c) 2011 by Delphix. All rights reserved.
*/
@@ -226,7 +227,7 @@ get_usage(zfs_help_t idx)
return (gettext("\tupgrade [-v]\n"
"\tupgrade [-r] [-V version] <-a | filesystem ...>\n"));
case HELP_LIST:
- return (gettext("\tlist [-rH][-d max] "
+ return (gettext("\tlist [-rHp][-d max] "
"[-o property[,...]] [-t type[,...]] [-s property] ...\n"
"\t [-S property] ... "
"[filesystem|volume|snapshot] ...\n"));
@@ -860,12 +861,13 @@ badusage:
}
/*
- * zfs destroy [-rRf] <fs, vol>
+ * zfs destroy [-rRfF] <fs, vol>
* zfs destroy [-rRd] <snap>
*
* -r Recursively destroy all children
* -R Recursively destroy all dependents, including clones
* -f Force unmounting of any dependents
+ * -F Continue retrying on seeing EBUSY
* -d If we can't destroy now, mark for deferred destruction
*
* Destroys the given dataset. By default, it will unmount any filesystems,
@@ -875,6 +877,7 @@ badusage:
typedef struct destroy_cbdata {
boolean_t cb_first;
boolean_t cb_force;
+ boolean_t cb_wait;
boolean_t cb_recurse;
boolean_t cb_error;
boolean_t cb_doclones;
@@ -956,13 +959,18 @@ out:
static int
destroy_callback(zfs_handle_t *zhp, void *data)
{
- destroy_cbdata_t *cb = data;
+ destroy_cbdata_t *cbp = data;
+ struct timespec ts;
+ int err = 0;
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = 500 * (NANOSEC / MILLISEC);
const char *name = zfs_get_name(zhp);
- if (cb->cb_verbose) {
- if (cb->cb_parsable) {
+ if (cbp->cb_verbose) {
+ if (cbp->cb_parsable) {
(void) printf("destroy\t%s\n", name);
- } else if (cb->cb_dryrun) {
+ } else if (cbp->cb_dryrun) {
(void) printf(gettext("would destroy %s\n"),
name);
} else {
@@ -976,21 +984,37 @@ destroy_callback(zfs_handle_t *zhp, void *data)
* here).
*/
if (strchr(zfs_get_name(zhp), '/') == NULL &&
- zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
- zfs_close(zhp);
- return (0);
- }
+ zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM)
+ goto out;
- if (!cb->cb_dryrun) {
- 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);
- }
- }
+ if ((err = zfs_unmount(zhp, NULL, cbp->cb_force ? MS_FORCE : 0)) != 0)
+ goto out;
+
+ if (cbp->cb_wait)
+ libzfs_print_on_error(g_zfs, B_FALSE);
+ /*
+ * Unless instructed to retry on EBUSY, bail out on the first error.
+ * When retrying, try every 500ms until either succeeding or seeing a
+ * non-EBUSY error code.
+ */
+ if (!cbp->cb_dryrun) {
+ while ((err = zfs_destroy(zhp, cbp->cb_defer_destroy)) != 0) {
+ if (cbp->cb_wait && libzfs_errno(g_zfs) == EZFS_BUSY) {
+ (void) nanosleep(&ts, NULL);
+ continue;
+ }
+ (void) fprintf(stderr, "%s: %s\n", libzfs_error_action(g_zfs),
+ libzfs_error_description(g_zfs));
+ break;
+ }
+ }
+
+ libzfs_print_on_error(g_zfs, B_TRUE);
+
+out:
zfs_close(zhp);
- return (0);
+ return (err);
}
static int
@@ -1142,7 +1166,7 @@ zfs_do_destroy(int argc, char **argv)
zfs_type_t type = ZFS_TYPE_DATASET;
/* check options */
- while ((c = getopt(argc, argv, "vpndfrR")) != -1) {
+ while ((c = getopt(argc, argv, "vpndfFrR")) != -1) {
switch (c) {
case 'v':
cb.cb_verbose = B_TRUE;
@@ -1161,6 +1185,9 @@ zfs_do_destroy(int argc, char **argv)
case 'f':
cb.cb_force = B_TRUE;
break;
+ case 'F':
+ cb.cb_wait = B_TRUE;
+ break;
case 'r':
cb.cb_recurse = B_TRUE;
break;
@@ -2725,11 +2752,12 @@ zfs_do_userspace(int argc, char **argv)
}
/*
- * list [-r][-d max] [-H] [-o property[,property]...] [-t type[,type]...]
+ * list [-r][-p][-d max] [-H] [-o property[,property]...] [-t type[,type]...]
* [-s property [-s property]...] [-S property [-S property]...]
* <dataset> ...
*
* -r Recurse over all children
+ * -p Display values in parsable (literal) format.
* -d Limit recursion by depth.
* -H Scripted mode; elide headers and separate columns by tabs
* -o Control which fields to display.
@@ -2744,6 +2772,7 @@ zfs_do_userspace(int argc, char **argv)
typedef struct list_cbdata {
boolean_t cb_first;
boolean_t cb_scripted;
+ boolean_t cb_literal;
zprop_list_t *cb_proplist;
} list_cbdata_t;
@@ -2793,7 +2822,8 @@ print_header(zprop_list_t *pl)
* to the described layout.
*/
static void
-print_dataset(zfs_handle_t *zhp, zprop_list_t *pl, boolean_t scripted)
+print_dataset(zfs_handle_t *zhp, zprop_list_t *pl, boolean_t scripted,
+ boolean_t literal)
{
boolean_t first = B_TRUE;
char property[ZFS_MAXPROPLEN];
@@ -2815,7 +2845,7 @@ print_dataset(zfs_handle_t *zhp, zprop_list_t *pl, boolean_t scripted)
if (pl->pl_prop != ZPROP_INVAL) {
if (zfs_prop_get(zhp, pl->pl_prop, property,
- sizeof (property), NULL, NULL, 0, B_FALSE) != 0)
+ sizeof (property), NULL, NULL, 0, literal) != 0)
propstr = "-";
else
propstr = property;
@@ -2823,7 +2853,7 @@ print_dataset(zfs_handle_t *zhp, zprop_list_t *pl, boolean_t scripted)
right_justify = zfs_prop_align_right(pl->pl_prop);
} else if (zfs_prop_userquota(pl->pl_user_prop)) {
if (zfs_prop_get_userquota(zhp, pl->pl_user_prop,
- property, sizeof (property), B_FALSE) != 0)
+ property, sizeof (property), literal) != 0)
propstr = "-";
else
propstr = property;
@@ -2877,7 +2907,7 @@ list_callback(zfs_handle_t *zhp, void *data)
cbp->cb_first = B_FALSE;
}
- print_dataset(zhp, cbp->cb_proplist, cbp->cb_scripted);
+ print_dataset(zhp, cbp->cb_proplist, cbp->cb_scripted, cbp->cb_literal);
return (0);
}
@@ -2900,8 +2930,11 @@ zfs_do_list(int argc, char **argv)
int flags = ZFS_ITER_PROP_LISTSNAPS | ZFS_ITER_ARGS_CAN_BE_PATHS;
/* check options */
- while ((c = getopt(argc, argv, ":d:o:rt:Hs:S:")) != -1) {
+ while ((c = getopt(argc, argv, ":pd:o:rt:Hs:S:")) != -1) {
switch (c) {
+ case 'p':
+ cb.cb_literal = B_TRUE;
+ break;
case 'o':
fields = optarg;
break;