mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 02:27:36 +03:00
1644 add ZFS "clones" property
1645 add ZFS "written" and "written@..." properties
1646 "zfs send" should estimate size of stream
1647 "zfs destroy" should determine space reclaimed by
destroying multiple snapshots
1708 adjust size of zpool history data
References:
https://www.illumos.org/issues/1644
https://www.illumos.org/issues/1645
https://www.illumos.org/issues/1646
https://www.illumos.org/issues/1647
https://www.illumos.org/issues/1708
This commit modifies the user to kernel space ioctl ABI. Extra
care should be taken when updating to ensure both the kernel
modules and utilities are updated. This change has reordered
all of the new ioctl()s to the end of the list. This should
help minimize this issue in the future.
Reviewed by: Richard Lowe <richlowe@richlowe.net>
Reviewed by: George Wilson <gwilson@zfsmail.com>
Reviewed by: Albert Lee <trisk@opensolaris.org>
Approved by: Garrett D'Amore <garret@nexenta.com>
Ported by: Martin Matuska <martin@matuska.org>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #826
Closes #664
This commit is contained in:
committed by
Brian Behlendorf
parent
7eebaff409
commit
330d06f90d
+328
-141
@@ -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 (c) 2011 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
@@ -140,7 +141,7 @@ typedef enum {
|
||||
HELP_HOLD,
|
||||
HELP_HOLDS,
|
||||
HELP_RELEASE,
|
||||
HELP_DIFF
|
||||
HELP_DIFF,
|
||||
} zfs_help_t;
|
||||
|
||||
typedef struct zfs_command {
|
||||
@@ -212,8 +213,9 @@ get_usage(zfs_help_t idx)
|
||||
"\tcreate [-ps] [-b blocksize] [-o property=value] ... "
|
||||
"-V <size> <volume>\n"));
|
||||
case HELP_DESTROY:
|
||||
return (gettext("\tdestroy [-rRf] <filesystem|volume>\n"
|
||||
"\tdestroy [-rRd] <snapshot>\n"));
|
||||
return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n"
|
||||
"\tdestroy [-dnpRrv] "
|
||||
"<filesystem|volume>@<snap>[%<snap>][,...]\n"));
|
||||
case HELP_GET:
|
||||
return (gettext("\tget [-rHp] [-d max] "
|
||||
"[-o \"all\" | field[,...]] [-s source[,...]]\n"
|
||||
@@ -247,7 +249,8 @@ get_usage(zfs_help_t idx)
|
||||
case HELP_ROLLBACK:
|
||||
return (gettext("\trollback [-rRf] <snapshot>\n"));
|
||||
case HELP_SEND:
|
||||
return (gettext("\tsend [-vRDp] [-[iI] snapshot] <snapshot>\n"));
|
||||
return (gettext("\tsend [-DnPpRrv] [-[iI] snapshot] "
|
||||
"<snapshot>\n"));
|
||||
case HELP_SET:
|
||||
return (gettext("\tset <property=value> "
|
||||
"<filesystem|volume|snapshot> ...\n"));
|
||||
@@ -426,6 +429,8 @@ 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"));
|
||||
@@ -872,15 +877,23 @@ badusage:
|
||||
*/
|
||||
typedef struct destroy_cbdata {
|
||||
boolean_t cb_first;
|
||||
int cb_force;
|
||||
int cb_recurse;
|
||||
int cb_error;
|
||||
int cb_needforce;
|
||||
int cb_doclones;
|
||||
boolean_t cb_closezhp;
|
||||
boolean_t cb_force;
|
||||
boolean_t cb_recurse;
|
||||
boolean_t cb_error;
|
||||
boolean_t cb_doclones;
|
||||
zfs_handle_t *cb_target;
|
||||
char *cb_snapname;
|
||||
boolean_t cb_defer_destroy;
|
||||
boolean_t cb_verbose;
|
||||
boolean_t cb_parsable;
|
||||
boolean_t cb_dryrun;
|
||||
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;
|
||||
|
||||
/*
|
||||
@@ -910,7 +923,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 = 1;
|
||||
cbp->cb_error = B_TRUE;
|
||||
}
|
||||
|
||||
(void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
|
||||
@@ -931,7 +944,8 @@ 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 = 1;
|
||||
cbp->cb_error = B_TRUE;
|
||||
cbp->cb_dryrun = B_TRUE;
|
||||
}
|
||||
|
||||
(void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
|
||||
@@ -945,7 +959,20 @@ out:
|
||||
static int
|
||||
destroy_callback(zfs_handle_t *zhp, void *data)
|
||||
{
|
||||
destroy_cbdata_t *cbp = 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_dryrun) {
|
||||
(void) printf(gettext("would destroy %s\n"),
|
||||
name);
|
||||
} else {
|
||||
(void) printf(gettext("will destroy %s\n"),
|
||||
name);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ignore pools (which we've already flagged as an error before getting
|
||||
@@ -957,13 +984,12 @@ destroy_callback(zfs_handle_t *zhp, void *data)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
zfs_close(zhp);
|
||||
@@ -971,39 +997,142 @@ destroy_callback(zfs_handle_t *zhp, void *data)
|
||||
}
|
||||
|
||||
static int
|
||||
destroy_snap_clones(zfs_handle_t *zhp, void *arg)
|
||||
destroy_print_cb(zfs_handle_t *zhp, void *arg)
|
||||
{
|
||||
destroy_cbdata_t *cbp = arg;
|
||||
char thissnap[MAXPATHLEN];
|
||||
zfs_handle_t *szhp;
|
||||
boolean_t closezhp = cbp->cb_closezhp;
|
||||
int rv;
|
||||
destroy_cbdata_t *cb = arg;
|
||||
const char *name = zfs_get_name(zhp);
|
||||
int err = 0;
|
||||
|
||||
(void) snprintf(thissnap, sizeof (thissnap),
|
||||
"%s@%s", zfs_get_name(zhp), cbp->cb_snapname);
|
||||
|
||||
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);
|
||||
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_dryrun) {
|
||||
(void) printf(gettext("would destroy %s\n"),
|
||||
name);
|
||||
} else {
|
||||
(void) printf(gettext("will destroy %s\n"),
|
||||
name);
|
||||
}
|
||||
}
|
||||
zfs_close(szhp);
|
||||
} 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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
cbp->cb_closezhp = B_TRUE;
|
||||
rv = zfs_iter_filesystems(zhp, destroy_snap_clones, arg);
|
||||
if (closezhp)
|
||||
zfs_close(zhp);
|
||||
return (rv);
|
||||
if (err == 0) {
|
||||
if (nvlist_add_boolean(cb->cb_nvl, zfs_get_name(zhp)))
|
||||
nomem();
|
||||
}
|
||||
zfs_close(zhp);
|
||||
return (err);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -1012,25 +1141,35 @@ zfs_do_destroy(int argc, char **argv)
|
||||
destroy_cbdata_t cb = { 0 };
|
||||
int c;
|
||||
zfs_handle_t *zhp;
|
||||
char *cp;
|
||||
char *at;
|
||||
zfs_type_t type = ZFS_TYPE_DATASET;
|
||||
|
||||
/* check options */
|
||||
while ((c = getopt(argc, argv, "dfrR")) != -1) {
|
||||
while ((c = getopt(argc, argv, "vpndfrR")) != -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_dryrun = B_TRUE;
|
||||
break;
|
||||
case 'd':
|
||||
cb.cb_defer_destroy = B_TRUE;
|
||||
type = ZFS_TYPE_SNAPSHOT;
|
||||
break;
|
||||
case 'f':
|
||||
cb.cb_force = 1;
|
||||
cb.cb_force = B_TRUE;
|
||||
break;
|
||||
case 'r':
|
||||
cb.cb_recurse = 1;
|
||||
cb.cb_recurse = B_TRUE;
|
||||
break;
|
||||
case 'R':
|
||||
cb.cb_recurse = 1;
|
||||
cb.cb_doclones = 1;
|
||||
cb.cb_recurse = B_TRUE;
|
||||
cb.cb_doclones = B_TRUE;
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
@@ -1045,7 +1184,7 @@ zfs_do_destroy(int argc, char **argv)
|
||||
|
||||
/* check number of arguments */
|
||||
if (argc == 0) {
|
||||
(void) fprintf(stderr, gettext("missing path argument\n"));
|
||||
(void) fprintf(stderr, gettext("missing dataset argument\n"));
|
||||
usage(B_FALSE);
|
||||
}
|
||||
if (argc > 1) {
|
||||
@@ -1053,92 +1192,118 @@ zfs_do_destroy(int argc, char **argv)
|
||||
usage(B_FALSE);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 = 0;
|
||||
at = strchr(argv[0], '@');
|
||||
if (at != NULL) {
|
||||
int err = 0;
|
||||
|
||||
*cp = '\0';
|
||||
if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL)
|
||||
/* 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);
|
||||
*cp = '@';
|
||||
cp++;
|
||||
|
||||
if (cb.cb_doclones) {
|
||||
boolean_t defer = cb.cb_defer_destroy;
|
||||
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);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
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_verbose) {
|
||||
char buf[16];
|
||||
zfs_nicenum(cb.cb_snapused, buf, sizeof (buf));
|
||||
if (cb.cb_parsable) {
|
||||
(void) printf("reclaim\t%llu\n",
|
||||
(u_longlong_t)cb.cb_snapused);
|
||||
} else if (cb.cb_dryrun) {
|
||||
(void) printf(gettext("would reclaim %s\n"),
|
||||
buf);
|
||||
} else {
|
||||
(void) printf(gettext("will reclaim %s\n"),
|
||||
buf);
|
||||
}
|
||||
cb.cb_defer_destroy = defer;
|
||||
}
|
||||
|
||||
ret = zfs_destroy_snaps(zhp, cp, cb.cb_defer_destroy);
|
||||
zfs_close(zhp);
|
||||
if (ret) {
|
||||
(void) fprintf(stderr,
|
||||
gettext("no snapshots destroyed\n"));
|
||||
if (!cb.cb_dryrun) {
|
||||
if (cb.cb_doclones)
|
||||
err = destroy_clones(&cb);
|
||||
if (err == 0) {
|
||||
err = zfs_destroy_snaps_nvl(zhp, cb.cb_nvl,
|
||||
cb.cb_defer_destroy);
|
||||
}
|
||||
}
|
||||
return (ret != 0);
|
||||
}
|
||||
|
||||
/* Open the given dataset */
|
||||
if ((zhp = zfs_open(g_zfs, argv[0], type)) == NULL)
|
||||
return (1);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
if (cb.cb_error) {
|
||||
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.
|
||||
*/
|
||||
if (destroy_callback(zhp, &cb) != 0)
|
||||
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 || (!cb.cb_defer_destroy &&
|
||||
(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.
|
||||
*/
|
||||
|
||||
if (destroy_callback(zhp, &cb) != 0)
|
||||
return (1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
@@ -1237,6 +1402,17 @@ get_callback(zfs_handle_t *zhp, void *data)
|
||||
(void) strlcpy(buf, "-", sizeof (buf));
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -1783,8 +1959,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 File system "
|
||||
"unique identifier (FUID)\n"));
|
||||
(void) printf(gettext(" 3 Case insensitive and filesystem "
|
||||
"user identifier (FUID)\n"));
|
||||
(void) printf(gettext(" 4 userquota, groupquota "
|
||||
"properties\n"));
|
||||
(void) printf(gettext(" 5 System attributes\n"));
|
||||
@@ -2670,6 +2846,13 @@ 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)
|
||||
@@ -3289,9 +3472,6 @@ 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
|
||||
@@ -3303,11 +3483,11 @@ zfs_do_send(int argc, char **argv)
|
||||
zfs_handle_t *zhp;
|
||||
sendflags_t flags = { 0 };
|
||||
int c, err;
|
||||
nvlist_t *dbgnv;
|
||||
nvlist_t *dbgnv = NULL;
|
||||
boolean_t extraverbose = B_FALSE;
|
||||
|
||||
/* check options */
|
||||
while ((c = getopt(argc, argv, ":i:I:RDpv")) != -1) {
|
||||
while ((c = getopt(argc, argv, ":i:I:RDpvnP")) != -1) {
|
||||
switch (c) {
|
||||
case 'i':
|
||||
if (fromname)
|
||||
@@ -3326,6 +3506,10 @@ 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;
|
||||
@@ -3334,6 +3518,9 @@ zfs_do_send(int argc, char **argv)
|
||||
case 'D':
|
||||
flags.dedup = B_TRUE;
|
||||
break;
|
||||
case 'n':
|
||||
flags.dryrun = B_TRUE;
|
||||
break;
|
||||
case ':':
|
||||
(void) fprintf(stderr, gettext("missing argument for "
|
||||
"'%c' option\n"), optopt);
|
||||
@@ -3359,7 +3546,7 @@ zfs_do_send(int argc, char **argv)
|
||||
usage(B_FALSE);
|
||||
}
|
||||
|
||||
if (isatty(STDOUT_FILENO)) {
|
||||
if (!flags.dryrun && isatty(STDOUT_FILENO)) {
|
||||
(void) fprintf(stderr,
|
||||
gettext("Error: Stream can not be written to a terminal.\n"
|
||||
"You must redirect standard output.\n"));
|
||||
@@ -3413,10 +3600,10 @@ zfs_do_send(int argc, char **argv)
|
||||
if (flags.replicate && fromname == NULL)
|
||||
flags.doall = B_TRUE;
|
||||
|
||||
err = zfs_send(zhp, fromname, toname, flags, STDOUT_FILENO, NULL, 0,
|
||||
err = zfs_send(zhp, fromname, toname, &flags, STDOUT_FILENO, NULL, 0,
|
||||
extraverbose ? &dbgnv : NULL);
|
||||
|
||||
if (extraverbose) {
|
||||
if (extraverbose && dbgnv != NULL) {
|
||||
/*
|
||||
* dump_nvlist prints to stdout, but that's been
|
||||
* redirected to a file. Make it print to stderr
|
||||
@@ -3497,7 +3684,7 @@ zfs_do_receive(int argc, char **argv)
|
||||
return (1);
|
||||
}
|
||||
|
||||
err = zfs_receive(g_zfs, argv[0], flags, STDIN_FILENO, NULL);
|
||||
err = zfs_receive(g_zfs, argv[0], &flags, STDIN_FILENO, NULL);
|
||||
|
||||
return (err != 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user