summaryrefslogtreecommitdiff
path: root/usr/src/cmd/zfs/zfs_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/zfs/zfs_main.c')
-rw-r--r--usr/src/cmd/zfs/zfs_main.c148
1 files changed, 114 insertions, 34 deletions
diff --git a/usr/src/cmd/zfs/zfs_main.c b/usr/src/cmd/zfs/zfs_main.c
index ca11041cba..993d98ea12 100644
--- a/usr/src/cmd/zfs/zfs_main.c
+++ b/usr/src/cmd/zfs/zfs_main.c
@@ -239,7 +239,7 @@ get_usage(zfs_help_t idx)
"<filesystem|volume>@<snap>[%<snap>][,...]\n"
"\tdestroy <filesystem|volume>#<bookmark>\n"));
case HELP_GET:
- return (gettext("\tget [-rHp] [-d max] "
+ return (gettext("\tget [-crHp] [-d max] "
"[-o \"all\" | field[,...]]\n"
"\t [-t type[,...]] [-s source[,...]]\n"
"\t <\"all\" | property[,...]> "
@@ -625,7 +625,7 @@ should_auto_mount(zfs_handle_t *zhp)
}
/*
- * zfs clone [-p] [-o prop=value] ... <snap> <fs | vol>
+ * zfs clone [-Fp] [-o prop=value] ... <snap> <fs | vol>
*
* Given an existing dataset, create a writable copy whose initial contents
* are the same as the source. The newly created dataset maintains a
@@ -633,12 +633,18 @@ should_auto_mount(zfs_handle_t *zhp)
* the clone exists.
*
* The '-p' flag creates all the non-existing ancestors of the target first.
+ *
+ * The '-F' flag retries the zfs_mount() operation as long as zfs_mount() is
+ * still returning EBUSY. Any callers which specify -F should be careful to
+ * ensure that no other process has a persistent hold on the mountpoint's
+ * directory.
*/
static int
zfs_do_clone(int argc, char **argv)
{
zfs_handle_t *zhp = NULL;
boolean_t parents = B_FALSE;
+ boolean_t keeptrying = B_FALSE;
nvlist_t *props;
int ret = 0;
int c;
@@ -647,8 +653,11 @@ zfs_do_clone(int argc, char **argv)
nomem();
/* check options */
- while ((c = getopt(argc, argv, "o:p")) != -1) {
+ while ((c = getopt(argc, argv, "Fo:p")) != -1) {
switch (c) {
+ case 'F':
+ keeptrying = B_TRUE;
+ break;
case 'o':
if (parseprop(props, optarg) != 0)
return (1);
@@ -715,11 +724,16 @@ zfs_do_clone(int argc, char **argv)
* step.
*/
if (should_auto_mount(clone)) {
- if ((ret = zfs_mount(clone, NULL, 0)) != 0) {
- (void) fprintf(stderr, gettext("clone "
- "successfully created, "
- "but not mounted\n"));
- } else if ((ret = zfs_share(clone)) != 0) {
+ while ((ret = zfs_mount(clone, NULL, 0)) != 0) {
+ if (!keeptrying || errno != EBUSY) {
+ (void) fprintf(stderr,
+ gettext("clone "
+ "successfully created, "
+ "but not mounted\n"));
+ break;
+ }
+ }
+ if (ret == 0 && (ret = zfs_share(clone)) != 0) {
(void) fprintf(stderr, gettext("clone "
"successfully created, "
"but not shared\n"));
@@ -945,12 +959,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,
@@ -960,6 +975,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;
@@ -1043,13 +1059,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 {
@@ -1064,13 +1085,10 @@ destroy_callback(zfs_handle_t *zhp, void *data)
*/
if (strchr(zfs_get_name(zhp), '/') == NULL &&
zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
- zfs_close(zhp);
- return (0);
- }
- if (cb->cb_dryrun) {
- zfs_close(zhp);
- return (0);
+ goto out;
}
+ if (cbp->cb_dryrun)
+ goto out;
/*
* We batch up all contiguous snapshots (even of different
@@ -1079,23 +1097,66 @@ destroy_callback(zfs_handle_t *zhp, void *data)
* because we must delete a clone before its origin.
*/
if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) {
- fnvlist_add_boolean(cb->cb_batchedsnaps, name);
- } else {
- int error = zfs_destroy_snaps_nvl(g_zfs,
- cb->cb_batchedsnaps, B_FALSE);
- fnvlist_free(cb->cb_batchedsnaps);
- cb->cb_batchedsnaps = fnvlist_alloc();
-
- if (error != 0 ||
- zfs_unmount(zhp, NULL, cb->cb_force ? MS_FORCE : 0) != 0 ||
- zfs_destroy(zhp, cb->cb_defer_destroy) != 0) {
- zfs_close(zhp);
- return (-1);
+ fnvlist_add_boolean(cbp->cb_batchedsnaps, name);
+ 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.
+ */
+ while ((err = zfs_destroy_snaps_nvl(g_zfs,
+ cbp->cb_batchedsnaps, B_FALSE)) != 0) {
+ if (cbp->cb_wait && libzfs_errno(g_zfs) == EZFS_BUSY) {
+ nanosleep(&ts, NULL);
+ continue;
}
+ (void) fprintf(stderr, "%s: %s\n",
+ libzfs_error_action(g_zfs),
+ libzfs_error_description(g_zfs));
+ break;
+ }
+
+ fnvlist_free(cbp->cb_batchedsnaps);
+ cbp->cb_batchedsnaps = fnvlist_alloc();
+
+ if (err != 0)
+ goto out;
+
+ while ((err = zfs_unmount(zhp, NULL,
+ cbp->cb_force ? MS_FORCE : 0)) != 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;
+ }
+
+ if (err != 0)
+ goto out;
+
+ 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;
}
+out:
+ libzfs_print_on_error(g_zfs, B_TRUE);
zfs_close(zhp);
- return (0);
+ return (err);
}
static int
@@ -1251,7 +1312,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;
@@ -1270,6 +1331,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;
@@ -1640,8 +1704,11 @@ zfs_do_get(int argc, char **argv)
cb.cb_type = ZFS_TYPE_DATASET;
/* check options */
- while ((c = getopt(argc, argv, ":d:o:s:rt:Hp")) != -1) {
+ while ((c = getopt(argc, argv, ":d:o:s:rt:Hcp")) != -1) {
switch (c) {
+ case 'c':
+ libzfs_set_cachedprops(g_zfs, B_TRUE);
+ break;
case 'p':
cb.cb_literal = B_TRUE;
break;
@@ -3079,6 +3146,7 @@ zfs_do_list(int argc, char **argv)
int types = ZFS_TYPE_DATASET;
boolean_t types_specified = B_FALSE;
char *fields = NULL;
+ zprop_list_t *pl;
list_cbdata_t cb = { 0 };
char *value;
int limit = 0;
@@ -3197,6 +3265,18 @@ zfs_do_list(int argc, char **argv)
!= 0)
usage(B_FALSE);
+ /*
+ * The default set of properties contains only properties which can be
+ * retrieved from the set of cached properties. If any user-specfied
+ * properties cannot be retrieved from that set, unset the cachedprops
+ * flags on the ZFS handle.
+ */
+ libzfs_set_cachedprops(g_zfs, B_TRUE);
+ for (pl = cb.cb_proplist; pl != NULL; pl = pl->pl_next) {
+ if (zfs_prop_cacheable(pl->pl_prop))
+ libzfs_set_cachedprops(g_zfs, B_FALSE);
+ }
+
cb.cb_first = B_TRUE;
ret = zfs_for_each(argc, argv, flags, types, sortcol, &cb.cb_proplist,