diff options
Diffstat (limited to 'usr/src/cmd/zfs')
-rw-r--r-- | usr/src/cmd/zfs/zfs_main.c | 83 |
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; |