aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Matuska <mm@FreeBSD.org>2012-07-18 10:19:51 +0000
committerMartin Matuska <mm@FreeBSD.org>2012-07-18 10:19:51 +0000
commit51ab6c0afd5ddf66818a95fe2d4246f650e9035b (patch)
treec1a1d8708d01362339691da82569576a94fa5fa0
parentcb34095d8e1a1332b2757534f521866cf353a3d6 (diff)
downloadsrc-51ab6c0afd5ddf66818a95fe2d4246f650e9035b.tar.gz
src-51ab6c0afd5ddf66818a95fe2d4246f650e9035b.zip
Update vendor/illumos/dist to pre libzfs_core state (zfs part)
illumos-gate revision 13742:b6bbdd77139c Obtained from: ssh://anonhg@hg.illumos.org/illumos-gate
Notes
Notes: svn path=/vendor/illumos/dist/; revision=238583
-rw-r--r--cmd/zdb/zdb.c91
-rw-r--r--cmd/zfs/zfs_main.c2808
-rw-r--r--cmd/zpool/zpool_main.c604
-rw-r--r--cmd/ztest/ztest.c1154
-rw-r--r--lib/libnvpair/libnvpair.c5
-rw-r--r--lib/libuutil/common/uu_list.c7
-rw-r--r--lib/libzfs/common/libzfs.h92
-rw-r--r--lib/libzfs/common/libzfs_config.c35
-rw-r--r--lib/libzfs/common/libzfs_dataset.c663
-rw-r--r--lib/libzfs/common/libzfs_graph.c653
-rw-r--r--lib/libzfs/common/libzfs_impl.h4
-rw-r--r--lib/libzfs/common/libzfs_import.c18
-rw-r--r--lib/libzfs/common/libzfs_iter.c462
-rw-r--r--lib/libzfs/common/libzfs_pool.c299
-rw-r--r--lib/libzfs/common/libzfs_sendrecv.c629
-rw-r--r--lib/libzfs/common/libzfs_status.c18
-rw-r--r--lib/libzfs/common/libzfs_util.c18
-rw-r--r--lib/libzpool/common/kernel.c6
-rw-r--r--lib/libzpool/common/sys/zfs_context.h20
-rw-r--r--lib/libzpool/common/taskq.c103
-rw-r--r--man/man1m/zdb.1m509
-rw-r--r--man/man1m/zfs.1m612
-rw-r--r--man/man1m/zpool.1m349
-rw-r--r--man/man1m/zstreamdump.1m15
24 files changed, 6842 insertions, 2332 deletions
diff --git a/cmd/zdb/zdb.c b/cmd/zdb/zdb.c
index c6e219df9e1d..ea211bf1f349 100644
--- a/cmd/zdb/zdb.c
+++ b/cmd/zdb/zdb.c
@@ -18,8 +18,10 @@
*
* CDDL HEADER END
*/
+
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
*/
#include <stdio.h>
@@ -54,6 +56,7 @@
#include <sys/zfs_fuid.h>
#include <sys/arc.h>
#include <sys/ddt.h>
+#include <sys/zfeature.h>
#undef ZFS_MAXNAMELEN
#undef verify
#include <libzfs.h>
@@ -63,7 +66,8 @@
#define ZDB_CHECKSUM_NAME(idx) ((idx) < ZIO_CHECKSUM_FUNCTIONS ? \
zio_checksum_table[(idx)].ci_name : "UNKNOWN")
#define ZDB_OT_NAME(idx) ((idx) < DMU_OT_NUMTYPES ? \
- dmu_ot[(idx)].ot_name : "UNKNOWN")
+ dmu_ot[(idx)].ot_name : DMU_OT_IS_VALID(idx) ? \
+ dmu_ot_byteswap[DMU_OT_BYTESWAP(idx)].ob_name : "UNKNOWN")
#define ZDB_OT_TYPE(idx) ((idx) < DMU_OT_NUMTYPES ? (idx) : DMU_OT_NUMTYPES)
#ifndef lint
@@ -102,13 +106,16 @@ static void
usage(void)
{
(void) fprintf(stderr,
- "Usage: %s [-CumdibcsDvhL] poolname [object...]\n"
- " %s [-div] dataset [object...]\n"
- " %s -m [-L] poolname [vdev [metaslab...]]\n"
- " %s -R poolname vdev:offset:size[:flags]\n"
- " %s -S poolname\n"
- " %s -l [-u] device\n"
- " %s -C\n\n",
+ "Usage: %s [-CumdibcsDvhLXFPA] [-t txg] [-e [-p path...]] "
+ "poolname [object...]\n"
+ " %s [-divPA] [-e -p path...] dataset [object...]\n"
+ " %s -m [-LXFPA] [-t txg] [-e [-p path...]] "
+ "poolname [vdev [metaslab...]]\n"
+ " %s -R [-A] [-e [-p path...]] poolname "
+ "vdev:offset:size[:flags]\n"
+ " %s -S [-PA] [-e [-p path...]] poolname\n"
+ " %s -l [-uA] device\n"
+ " %s -C [-A] [-U config]\n\n",
cmdname, cmdname, cmdname, cmdname, cmdname, cmdname, cmdname);
(void) fprintf(stderr, " Dataset name must include at least one "
@@ -150,7 +157,7 @@ usage(void)
"has altroot/not in a cachefile\n");
(void) fprintf(stderr, " -p <path> -- use one or more with "
"-e to specify path to vdev dir\n");
- (void) fprintf(stderr, " -P print numbers parsable\n");
+ (void) fprintf(stderr, " -P print numbers in parseable form\n");
(void) fprintf(stderr, " -t <txg> -- highest txg to use when "
"searching for uberblocks\n");
(void) fprintf(stderr, "Specify an option more than once (e.g. -bb) "
@@ -1085,7 +1092,7 @@ dump_dsl_dataset(objset_t *os, uint64_t object, void *data, size_t size)
ASSERT(size == sizeof (*ds));
crtime = ds->ds_creation_time;
- zdb_nicenum(ds->ds_used_bytes, used);
+ zdb_nicenum(ds->ds_referenced_bytes, used);
zdb_nicenum(ds->ds_compressed_bytes, compressed);
zdb_nicenum(ds->ds_uncompressed_bytes, uncompressed);
zdb_nicenum(ds->ds_unique_bytes, unique);
@@ -1129,6 +1136,44 @@ dump_dsl_dataset(objset_t *os, uint64_t object, void *data, size_t size)
/* ARGSUSED */
static int
+dump_bptree_cb(void *arg, const blkptr_t *bp, dmu_tx_t *tx)
+{
+ char blkbuf[BP_SPRINTF_LEN];
+
+ if (bp->blk_birth != 0) {
+ sprintf_blkptr(blkbuf, bp);
+ (void) printf("\t%s\n", blkbuf);
+ }
+ return (0);
+}
+
+static void
+dump_bptree(objset_t *os, uint64_t obj, char *name)
+{
+ char bytes[32];
+ bptree_phys_t *bt;
+ dmu_buf_t *db;
+
+ if (dump_opt['d'] < 3)
+ return;
+
+ VERIFY3U(0, ==, dmu_bonus_hold(os, obj, FTAG, &db));
+ bt = db->db_data;
+ zdb_nicenum(bt->bt_bytes, bytes);
+ (void) printf("\n %s: %llu datasets, %s\n",
+ name, (unsigned long long)(bt->bt_end - bt->bt_begin), bytes);
+ dmu_buf_rele(db, FTAG);
+
+ if (dump_opt['d'] < 5)
+ return;
+
+ (void) printf("\n");
+
+ (void) bptree_iterate(os, obj, B_FALSE, dump_bptree_cb, NULL, NULL);
+}
+
+/* ARGSUSED */
+static int
dump_bpobj_cb(void *arg, const blkptr_t *bp, dmu_tx_t *tx)
{
char blkbuf[BP_SPRINTF_LEN];
@@ -1880,11 +1925,13 @@ typedef struct zdb_blkstats {
*/
#define ZDB_OT_DEFERRED (DMU_OT_NUMTYPES + 0)
#define ZDB_OT_DITTO (DMU_OT_NUMTYPES + 1)
-#define ZDB_OT_TOTAL (DMU_OT_NUMTYPES + 2)
+#define ZDB_OT_OTHER (DMU_OT_NUMTYPES + 2)
+#define ZDB_OT_TOTAL (DMU_OT_NUMTYPES + 3)
static char *zdb_ot_extname[] = {
"deferred free",
"dedup ditto",
+ "other",
"Total",
};
@@ -1965,9 +2012,10 @@ zdb_blkptr_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, arc_buf_t *pbuf,
type = BP_GET_TYPE(bp);
- zdb_count_block(zcb, zilog, bp, type);
+ zdb_count_block(zcb, zilog, bp,
+ (type & DMU_OT_NEWTYPE) ? ZDB_OT_OTHER : type);
- is_metadata = (BP_GET_LEVEL(bp) != 0 || dmu_ot[type].ot_metadata);
+ is_metadata = (BP_GET_LEVEL(bp) != 0 || DMU_OT_IS_METADATA(type));
if (dump_opt['c'] > 1 || (dump_opt['c'] && is_metadata)) {
int ioerr;
@@ -2192,6 +2240,12 @@ dump_block_stats(spa_t *spa)
count_block_cb, &zcb, NULL);
(void) bpobj_iterate_nofree(&spa->spa_dsl_pool->dp_free_bpobj,
count_block_cb, &zcb, NULL);
+ if (spa_feature_is_active(spa,
+ &spa_feature_table[SPA_FEATURE_ASYNC_DESTROY])) {
+ VERIFY3U(0, ==, bptree_iterate(spa->spa_meta_objset,
+ spa->spa_dsl_pool->dp_bptree_obj, B_FALSE, count_block_cb,
+ &zcb, NULL));
+ }
if (dump_opt['c'] > 1)
flags |= TRAVERSE_PREFETCH_DATA;
@@ -2368,7 +2422,7 @@ zdb_ddt_add_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
}
if (BP_IS_HOLE(bp) || BP_GET_CHECKSUM(bp) == ZIO_CHECKSUM_OFF ||
- BP_GET_LEVEL(bp) > 0 || dmu_ot[BP_GET_TYPE(bp)].ot_metadata)
+ BP_GET_LEVEL(bp) > 0 || DMU_OT_IS_METADATA(BP_GET_TYPE(bp)))
return (0);
ddt_key_fill(&zdde_search.zdde_key, bp);
@@ -2473,7 +2527,14 @@ dump_zpool(spa_t *spa)
dump_bpobj(&spa->spa_deferred_bpobj, "Deferred frees");
if (spa_version(spa) >= SPA_VERSION_DEADLISTS) {
dump_bpobj(&spa->spa_dsl_pool->dp_free_bpobj,
- "Pool frees");
+ "Pool snapshot frees");
+ }
+
+ if (spa_feature_is_active(spa,
+ &spa_feature_table[SPA_FEATURE_ASYNC_DESTROY])) {
+ dump_bptree(spa->spa_meta_objset,
+ spa->spa_dsl_pool->dp_bptree_obj,
+ "Pool dataset frees");
}
dump_dtl(spa->spa_root_vdev, 0);
}
diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c
index 9516697390ef..b64905f11997 100644
--- a/cmd/zfs/zfs_main.c
+++ b/cmd/zfs/zfs_main.c
@@ -21,6 +21,10 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright 2012 Milan Jurik. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
*/
#include <assert.h>
@@ -41,6 +45,7 @@
#include <grp.h>
#include <pwd.h>
#include <signal.h>
+#include <sys/list.h>
#include <sys/mkdev.h>
#include <sys/mntent.h>
#include <sys/mnttab.h>
@@ -51,7 +56,11 @@
#include <time.h>
#include <libzfs.h>
+#include <zfs_prop.h>
+#include <zfs_deleg.h>
#include <libuutil.h>
+#include <aclutils.h>
+#include <directory.h>
#include "zfs_iter.h"
#include "zfs_util.h"
@@ -61,7 +70,6 @@ libzfs_handle_t *g_zfs;
static FILE *mnttab_file;
static char history_str[HIS_MAX_RECORD_LEN];
-const char *pypath = "/usr/lib/zfs/pyzfs.py";
static int zfs_do_clone(int argc, char **argv);
static int zfs_do_create(int argc, char **argv);
@@ -82,8 +90,10 @@ static int zfs_do_send(int argc, char **argv);
static int zfs_do_receive(int argc, char **argv);
static int zfs_do_promote(int argc, char **argv);
static int zfs_do_userspace(int argc, char **argv);
-static int zfs_do_python(int argc, char **argv);
+static int zfs_do_allow(int argc, char **argv);
+static int zfs_do_unallow(int argc, char **argv);
static int zfs_do_hold(int argc, char **argv);
+static int zfs_do_holds(int argc, char **argv);
static int zfs_do_release(int argc, char **argv);
static int zfs_do_diff(int argc, char **argv);
@@ -131,7 +141,7 @@ typedef enum {
HELP_HOLD,
HELP_HOLDS,
HELP_RELEASE,
- HELP_DIFF
+ HELP_DIFF,
} zfs_help_t;
typedef struct zfs_command {
@@ -176,12 +186,12 @@ static zfs_command_t command_table[] = {
{ "send", zfs_do_send, HELP_SEND },
{ "receive", zfs_do_receive, HELP_RECEIVE },
{ NULL },
- { "allow", zfs_do_python, HELP_ALLOW },
+ { "allow", zfs_do_allow, HELP_ALLOW },
{ NULL },
- { "unallow", zfs_do_python, HELP_UNALLOW },
+ { "unallow", zfs_do_unallow, HELP_UNALLOW },
{ NULL },
{ "hold", zfs_do_hold, HELP_HOLD },
- { "holds", zfs_do_python, HELP_HOLDS },
+ { "holds", zfs_do_holds, HELP_HOLDS },
{ "release", zfs_do_release, HELP_RELEASE },
{ "diff", zfs_do_diff, HELP_DIFF },
};
@@ -203,11 +213,13 @@ 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"
+ "[-o \"all\" | field[,...]] [-t type[,...]] "
+ "[-s source[,...]]\n"
"\t <\"all\" | property[,...]> "
"[filesystem|volume|snapshot] ...\n"));
case HELP_INHERIT:
@@ -231,14 +243,15 @@ get_usage(zfs_help_t idx)
"snapshot>\n"
"\treceive [-vnFu] [-d | -e] <filesystem>\n"));
case HELP_RENAME:
- return (gettext("\trename <filesystem|volume|snapshot> "
+ return (gettext("\trename [-f] <filesystem|volume|snapshot> "
"<filesystem|volume|snapshot>\n"
- "\trename -p <filesystem|volume> <filesystem|volume>\n"
+ "\trename [-f] -p <filesystem|volume> <filesystem|volume>\n"
"\trename -r <snapshot> <snapshot>"));
case HELP_ROLLBACK:
return (gettext("\trollback [-rRf] <snapshot>\n"));
case HELP_SEND:
- return (gettext("\tsend [-RDp] [-[iI] snapshot] <snapshot>\n"));
+ return (gettext("\tsend [-DnPpRrv] [-[iI] snapshot] "
+ "<snapshot>\n"));
case HELP_SET:
return (gettext("\tset <property=value> "
"<filesystem|volume|snapshot> ...\n"));
@@ -417,6 +430,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"));
@@ -562,7 +577,7 @@ zfs_do_clone(int argc, char **argv)
zfs_handle_t *zhp = NULL;
boolean_t parents = B_FALSE;
nvlist_t *props;
- int ret;
+ int ret = 0;
int c;
if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
@@ -729,7 +744,6 @@ zfs_do_create(int argc, char **argv)
(void) fprintf(stderr, gettext("missing size "
"argument\n"));
goto badusage;
- break;
case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
optopt);
@@ -862,15 +876,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;
/*
@@ -900,7 +922,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));
@@ -921,7 +943,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));
@@ -935,7 +958,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
@@ -947,13 +983,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);
@@ -961,39 +996,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;
-
- (void) snprintf(thissnap, sizeof (thissnap),
- "%s@%s", zfs_get_name(zhp), cbp->cb_snapname);
+ 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_dryrun) {
+ (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);
+}
- 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);
+static int
+destroy_print_snapshots(zfs_handle_t *fs_zhp, destroy_cbdata_t *cb)
+{
+ int err = 0;
+ 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);
}
- zfs_close(szhp);
+ cb->cb_snapused += used;
+ zfs_close(cb->cb_firstsnap);
+ cb->cb_firstsnap = NULL;
+ zfs_close(cb->cb_prevsnap);
+ cb->cb_prevsnap = NULL;
}
+ return (err);
+}
- cbp->cb_closezhp = B_TRUE;
- rv = zfs_iter_filesystems(zhp, destroy_snap_clones, arg);
- if (closezhp)
- zfs_close(zhp);
- return (rv);
+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);
+}
+
+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 = 0;
+
+ /*
+ * 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
@@ -1002,25 +1140,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:
@@ -1035,7 +1183,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) {
@@ -1043,91 +1191,117 @@ 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;
+ 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",
+ 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);
+ 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);
- 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 && !cb.cb_defer_destroy &&
- 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 &&
+ 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);
- }
+ if (cb.cb_error) {
+ zfs_close(zhp);
+ return (1);
+ }
- /*
- * Do the real thing. The callback will close the handle regardless of
- * whether it succeeds or not.
- */
+ if (zfs_iter_dependents(zhp, B_FALSE, destroy_callback,
+ &cb) != 0) {
+ zfs_close(zhp);
+ return (1);
+ }
- if (destroy_callback(zhp, &cb) != 0)
- 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);
}
@@ -1229,6 +1403,17 @@ 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) {
@@ -1273,9 +1458,10 @@ static int
zfs_do_get(int argc, char **argv)
{
zprop_get_cbdata_t cb = { 0 };
- int i, c, flags = 0;
+ int i, c, flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
+ int types = ZFS_TYPE_DATASET;
char *value, *fields;
- int ret;
+ int ret = 0;
int limit = 0;
zprop_list_t fake_name = { 0 };
@@ -1290,7 +1476,7 @@ zfs_do_get(int argc, char **argv)
cb.cb_type = ZFS_TYPE_DATASET;
/* check options */
- while ((c = getopt(argc, argv, ":d:o:s:rHp")) != -1) {
+ while ((c = getopt(argc, argv, ":d:o:s:rt:Hp")) != -1) {
switch (c) {
case 'p':
cb.cb_literal = B_TRUE;
@@ -1408,6 +1594,37 @@ zfs_do_get(int argc, char **argv)
}
break;
+ case 't':
+ types = 0;
+ flags &= ~ZFS_ITER_PROP_LISTSNAPS;
+ while (*optarg != '\0') {
+ static char *type_subopts[] = { "filesystem",
+ "volume", "snapshot", "all", NULL };
+
+ switch (getsubopt(&optarg, type_subopts,
+ &value)) {
+ case 0:
+ types |= ZFS_TYPE_FILESYSTEM;
+ break;
+ case 1:
+ types |= ZFS_TYPE_VOLUME;
+ break;
+ case 2:
+ types |= ZFS_TYPE_SNAPSHOT;
+ break;
+ case 3:
+ types = ZFS_TYPE_DATASET;
+ break;
+
+ default:
+ (void) fprintf(stderr,
+ gettext("invalid type '%s'\n"),
+ value);
+ usage(B_FALSE);
+ }
+ }
+ break;
+
case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
optopt);
@@ -1451,7 +1668,7 @@ zfs_do_get(int argc, char **argv)
cb.cb_first = B_TRUE;
/* run for each object */
- ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET, NULL,
+ ret = zfs_for_each(argc, argv, flags, types, NULL,
&cb.cb_proplist, limit, get_callback, &cb);
if (cb.cb_proplist == &fake_name)
@@ -1512,7 +1729,7 @@ zfs_do_inherit(int argc, char **argv)
zfs_prop_t prop;
inherit_cbdata_t cb = { 0 };
char *propname;
- int ret;
+ int ret = 0;
int flags = 0;
boolean_t received = B_FALSE;
@@ -1718,7 +1935,7 @@ zfs_do_upgrade(int argc, char **argv)
{
boolean_t all = B_FALSE;
boolean_t showversions = B_FALSE;
- int ret;
+ int ret = 0;
upgrade_cbdata_t cb = { 0 };
char c;
int flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
@@ -1773,8 +1990,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"));
@@ -1823,81 +2040,721 @@ zfs_do_upgrade(int argc, char **argv)
return (ret);
}
+#define USTYPE_USR_BIT (0)
+#define USTYPE_GRP_BIT (1)
+#define USTYPE_PSX_BIT (2)
+#define USTYPE_SMB_BIT (3)
+
+#define USTYPE_USR (1 << USTYPE_USR_BIT)
+#define USTYPE_GRP (1 << USTYPE_GRP_BIT)
+
+#define USTYPE_PSX (1 << USTYPE_PSX_BIT)
+#define USTYPE_SMB (1 << USTYPE_SMB_BIT)
+
+#define USTYPE_PSX_USR (USTYPE_PSX | USTYPE_USR)
+#define USTYPE_SMB_USR (USTYPE_SMB | USTYPE_USR)
+#define USTYPE_PSX_GRP (USTYPE_PSX | USTYPE_GRP)
+#define USTYPE_SMB_GRP (USTYPE_SMB | USTYPE_GRP)
+#define USTYPE_ALL (USTYPE_PSX_USR | USTYPE_SMB_USR \
+ | USTYPE_PSX_GRP | USTYPE_SMB_GRP)
+
+
+#define USPROP_USED_BIT (0)
+#define USPROP_QUOTA_BIT (1)
+
+#define USPROP_USED (1 << USPROP_USED_BIT)
+#define USPROP_QUOTA (1 << USPROP_QUOTA_BIT)
+
+typedef struct us_node {
+ nvlist_t *usn_nvl;
+ uu_avl_node_t usn_avlnode;
+ uu_list_node_t usn_listnode;
+} us_node_t;
+
+typedef struct us_cbdata {
+ nvlist_t **cb_nvlp;
+ uu_avl_pool_t *cb_avl_pool;
+ uu_avl_t *cb_avl;
+ boolean_t cb_numname;
+ boolean_t cb_nicenum;
+ boolean_t cb_sid2posix;
+ zfs_userquota_prop_t cb_prop;
+ zfs_sort_column_t *cb_sortcol;
+ size_t cb_max_typelen;
+ size_t cb_max_namelen;
+ size_t cb_max_usedlen;
+ size_t cb_max_quotalen;
+} us_cbdata_t;
+
+typedef struct {
+ zfs_sort_column_t *si_sortcol;
+ boolean_t si_num_name;
+ boolean_t si_parsable;
+} us_sort_info_t;
+
+static int
+us_compare(const void *larg, const void *rarg, void *unused)
+{
+ const us_node_t *l = larg;
+ const us_node_t *r = rarg;
+ int rc = 0;
+ us_sort_info_t *si = (us_sort_info_t *)unused;
+ zfs_sort_column_t *sortcol = si->si_sortcol;
+ boolean_t num_name = si->si_num_name;
+ nvlist_t *lnvl = l->usn_nvl;
+ nvlist_t *rnvl = r->usn_nvl;
+
+ for (; sortcol != NULL; sortcol = sortcol->sc_next) {
+ char *lvstr = "";
+ char *rvstr = "";
+ uint32_t lv32 = 0;
+ uint32_t rv32 = 0;
+ uint64_t lv64 = 0;
+ uint64_t rv64 = 0;
+ zfs_prop_t prop = sortcol->sc_prop;
+ const char *propname = NULL;
+ boolean_t reverse = sortcol->sc_reverse;
+
+ switch (prop) {
+ case ZFS_PROP_TYPE:
+ propname = "type";
+ (void) nvlist_lookup_uint32(lnvl, propname, &lv32);
+ (void) nvlist_lookup_uint32(rnvl, propname, &rv32);
+ if (rv32 != lv32)
+ rc = (rv32 > lv32) ? 1 : -1;
+ break;
+ case ZFS_PROP_NAME:
+ propname = "name";
+ if (num_name) {
+ (void) nvlist_lookup_uint32(lnvl, propname,
+ &lv32);
+ (void) nvlist_lookup_uint32(rnvl, propname,
+ &rv32);
+ if (rv32 != lv32)
+ rc = (rv32 > lv32) ? 1 : -1;
+ } else {
+ (void) nvlist_lookup_string(lnvl, propname,
+ &lvstr);
+ (void) nvlist_lookup_string(rnvl, propname,
+ &rvstr);
+ rc = strcmp(lvstr, rvstr);
+ }
+ break;
+
+ case ZFS_PROP_USED:
+ case ZFS_PROP_QUOTA:
+ if (ZFS_PROP_USED == prop)
+ propname = "used";
+ else
+ propname = "quota";
+ (void) nvlist_lookup_uint64(lnvl, propname, &lv64);
+ (void) nvlist_lookup_uint64(rnvl, propname, &rv64);
+ if (rv64 != lv64)
+ rc = (rv64 > lv64) ? 1 : -1;
+ }
+
+ if (rc)
+ if (rc < 0)
+ return (reverse ? 1 : -1);
+ else
+ return (reverse ? -1 : 1);
+ }
+
+ return (rc);
+}
+
+static inline const char *
+us_type2str(unsigned field_type)
+{
+ switch (field_type) {
+ case USTYPE_PSX_USR:
+ return ("POSIX User");
+ case USTYPE_PSX_GRP:
+ return ("POSIX Group");
+ case USTYPE_SMB_USR:
+ return ("SMB User");
+ case USTYPE_SMB_GRP:
+ return ("SMB Group");
+ default:
+ return ("Undefined");
+ }
+}
+
/*
* zfs userspace
*/
static int
userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space)
{
- zfs_userquota_prop_t *typep = arg;
- zfs_userquota_prop_t p = *typep;
+ us_cbdata_t *cb = (us_cbdata_t *)arg;
+ zfs_userquota_prop_t prop = cb->cb_prop;
char *name = NULL;
- char *ug, *propname;
+ char *propname;
char namebuf[32];
char sizebuf[32];
+ us_node_t *node;
+ uu_avl_pool_t *avl_pool = cb->cb_avl_pool;
+ uu_avl_t *avl = cb->cb_avl;
+ uu_avl_index_t idx;
+ nvlist_t *props;
+ us_node_t *n;
+ zfs_sort_column_t *sortcol = cb->cb_sortcol;
+ unsigned type;
+ const char *typestr;
+ size_t namelen;
+ size_t typelen;
+ size_t sizelen;
+ us_sort_info_t sortinfo = { sortcol, cb->cb_numname };
if (domain == NULL || domain[0] == '\0') {
- if (p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA) {
+ /* POSIX */
+ if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) {
+ type = USTYPE_PSX_GRP;
struct group *g = getgrgid(rid);
if (g)
name = g->gr_name;
} else {
+ type = USTYPE_PSX_USR;
struct passwd *p = getpwuid(rid);
if (p)
name = p->pw_name;
}
+ } else {
+ char sid[ZFS_MAXNAMELEN+32];
+ uid_t id;
+ uint64_t classes;
+ int err = 0;
+ directory_error_t e;
+
+ (void) snprintf(sid, sizeof (sid), "%s-%u", domain, rid);
+ /* SMB */
+ if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) {
+ type = USTYPE_SMB_GRP;
+ err = sid_to_id(sid, B_FALSE, &id);
+ } else {
+ type = USTYPE_SMB_USR;
+ err = sid_to_id(sid, B_TRUE, &id);
+ }
+
+ if (err == 0) {
+ rid = id;
+
+ e = directory_name_from_sid(NULL, sid, &name, &classes);
+ if (e != NULL) {
+ directory_error_free(e);
+ return (NULL);
+ }
+
+ if (name == NULL)
+ name = sid;
+ }
}
- if (p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA)
- ug = "group";
- else
- ug = "user";
+/*
+ * if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA)
+ * ug = "group";
+ * else
+ * ug = "user";
+ */
- if (p == ZFS_PROP_USERUSED || p == ZFS_PROP_GROUPUSED)
+ if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED)
propname = "used";
else
propname = "quota";
- if (name == NULL) {
- (void) snprintf(namebuf, sizeof (namebuf),
- "%llu", (longlong_t)rid);
+ (void) snprintf(namebuf, sizeof (namebuf), "%u", rid);
+ if (name == NULL)
name = namebuf;
+
+ if (cb->cb_nicenum)
+ zfs_nicenum(space, sizebuf, sizeof (sizebuf));
+ else
+ (void) sprintf(sizebuf, "%llu", space);
+
+ node = safe_malloc(sizeof (us_node_t));
+ uu_avl_node_init(node, &node->usn_avlnode, avl_pool);
+
+ if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) {
+ free(node);
+ return (-1);
+ }
+
+ if (nvlist_add_uint32(props, "type", type) != 0)
+ nomem();
+
+ if (cb->cb_numname) {
+ if (nvlist_add_uint32(props, "name", rid) != 0)
+ nomem();
+ namelen = strlen(namebuf);
+ } else {
+ if (nvlist_add_string(props, "name", name) != 0)
+ nomem();
+ namelen = strlen(name);
}
- zfs_nicenum(space, sizebuf, sizeof (sizebuf));
- (void) printf("%s %s %s%c%s %s\n", propname, ug, domain,
- domain[0] ? '-' : ' ', name, sizebuf);
+ typestr = us_type2str(type);
+ typelen = strlen(gettext(typestr));
+ if (typelen > cb->cb_max_typelen)
+ cb->cb_max_typelen = typelen;
+
+ if (namelen > cb->cb_max_namelen)
+ cb->cb_max_namelen = namelen;
+
+ sizelen = strlen(sizebuf);
+ if (0 == strcmp(propname, "used")) {
+ if (sizelen > cb->cb_max_usedlen)
+ cb->cb_max_usedlen = sizelen;
+ } else {
+ if (sizelen > cb->cb_max_quotalen)
+ cb->cb_max_quotalen = sizelen;
+ }
+
+ node->usn_nvl = props;
+
+ n = uu_avl_find(avl, node, &sortinfo, &idx);
+ if (n == NULL)
+ uu_avl_insert(avl, node, idx);
+ else {
+ nvlist_free(props);
+ free(node);
+ node = n;
+ props = node->usn_nvl;
+ }
+
+ if (nvlist_add_uint64(props, propname, space) != 0)
+ nomem();
return (0);
}
+static inline boolean_t
+usprop_check(zfs_userquota_prop_t p, unsigned types, unsigned props)
+{
+ unsigned type;
+ unsigned prop;
+
+ switch (p) {
+ case ZFS_PROP_USERUSED:
+ type = USTYPE_USR;
+ prop = USPROP_USED;
+ break;
+ case ZFS_PROP_USERQUOTA:
+ type = USTYPE_USR;
+ prop = USPROP_QUOTA;
+ break;
+ case ZFS_PROP_GROUPUSED:
+ type = USTYPE_GRP;
+ prop = USPROP_USED;
+ break;
+ case ZFS_PROP_GROUPQUOTA:
+ type = USTYPE_GRP;
+ prop = USPROP_QUOTA;
+ break;
+ default: /* ALL */
+ return (B_TRUE);
+ };
+
+ return (type & types && prop & props);
+}
+
+#define USFIELD_TYPE (1 << 0)
+#define USFIELD_NAME (1 << 1)
+#define USFIELD_USED (1 << 2)
+#define USFIELD_QUOTA (1 << 3)
+#define USFIELD_ALL (USFIELD_TYPE | USFIELD_NAME | USFIELD_USED | USFIELD_QUOTA)
+
+static int
+parsefields(unsigned *fieldsp, char **names, unsigned *bits, size_t len)
+{
+ char *field = optarg;
+ char *delim;
+
+ do {
+ int i;
+ boolean_t found = B_FALSE;
+ delim = strchr(field, ',');
+ if (delim != NULL)
+ *delim = '\0';
+
+ for (i = 0; i < len; i++)
+ if (0 == strcmp(field, names[i])) {
+ found = B_TRUE;
+ *fieldsp |= bits[i];
+ break;
+ }
+
+ if (!found) {
+ (void) fprintf(stderr, gettext("invalid type '%s'"
+ "for -t option\n"), field);
+ return (-1);
+ }
+
+ field = delim + 1;
+ } while (delim);
+
+ return (0);
+}
+
+
+static char *type_names[] = { "posixuser", "smbuser", "posixgroup", "smbgroup",
+ "all" };
+static unsigned type_bits[] = {
+ USTYPE_PSX_USR,
+ USTYPE_SMB_USR,
+ USTYPE_PSX_GRP,
+ USTYPE_SMB_GRP,
+ USTYPE_ALL
+};
+
+static char *us_field_names[] = { "type", "name", "used", "quota" };
+static unsigned us_field_bits[] = {
+ USFIELD_TYPE,
+ USFIELD_NAME,
+ USFIELD_USED,
+ USFIELD_QUOTA
+};
+
+static void
+print_us_node(boolean_t scripted, boolean_t parseable, unsigned fields,
+ size_t type_width, size_t name_width, size_t used_width,
+ size_t quota_width, us_node_t *node)
+{
+ nvlist_t *nvl = node->usn_nvl;
+ nvpair_t *nvp = NULL;
+ char valstr[ZFS_MAXNAMELEN];
+ boolean_t first = B_TRUE;
+ boolean_t quota_found = B_FALSE;
+
+ if (fields & USFIELD_QUOTA && !nvlist_exists(nvl, "quota"))
+ if (nvlist_add_string(nvl, "quota", "none") != 0)
+ nomem();
+
+ while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
+ char *pname = nvpair_name(nvp);
+ data_type_t type = nvpair_type(nvp);
+ uint32_t val32 = 0;
+ uint64_t val64 = 0;
+ char *strval = NULL;
+ unsigned field = 0;
+ unsigned width = 0;
+ int i;
+ for (i = 0; i < 4; i++) {
+ if (0 == strcmp(pname, us_field_names[i])) {
+ field = us_field_bits[i];
+ break;
+ }
+ }
+
+ if (!(field & fields))
+ continue;
+
+ switch (type) {
+ case DATA_TYPE_UINT32:
+ (void) nvpair_value_uint32(nvp, &val32);
+ break;
+ case DATA_TYPE_UINT64:
+ (void) nvpair_value_uint64(nvp, &val64);
+ break;
+ case DATA_TYPE_STRING:
+ (void) nvpair_value_string(nvp, &strval);
+ break;
+ default:
+ (void) fprintf(stderr, "Invalid data type\n");
+ }
+
+ if (!first)
+ if (scripted)
+ (void) printf("\t");
+ else
+ (void) printf(" ");
+
+ switch (field) {
+ case USFIELD_TYPE:
+ strval = (char *)us_type2str(val32);
+ width = type_width;
+ break;
+ case USFIELD_NAME:
+ if (type == DATA_TYPE_UINT64) {
+ (void) sprintf(valstr, "%llu", val64);
+ strval = valstr;
+ }
+ width = name_width;
+ break;
+ case USFIELD_USED:
+ case USFIELD_QUOTA:
+ if (type == DATA_TYPE_UINT64) {
+ (void) nvpair_value_uint64(nvp, &val64);
+ if (parseable)
+ (void) sprintf(valstr, "%llu", val64);
+ else
+ zfs_nicenum(val64, valstr,
+ sizeof (valstr));
+ strval = valstr;
+ }
+
+ if (field == USFIELD_USED)
+ width = used_width;
+ else {
+ quota_found = B_FALSE;
+ width = quota_width;
+ }
+
+ break;
+ }
+
+ if (field == USFIELD_QUOTA && !quota_found)
+ (void) printf("%*s", width, strval);
+ else {
+ if (type == DATA_TYPE_STRING)
+ (void) printf("%-*s", width, strval);
+ else
+ (void) printf("%*s", width, strval);
+ }
+
+ first = B_FALSE;
+
+ }
+
+ (void) printf("\n");
+}
+
+static void
+print_us(boolean_t scripted, boolean_t parsable, unsigned fields,
+ unsigned type_width, unsigned name_width, unsigned used_width,
+ unsigned quota_width, boolean_t rmnode, uu_avl_t *avl)
+{
+ static char *us_field_hdr[] = { "TYPE", "NAME", "USED", "QUOTA" };
+ us_node_t *node;
+ const char *col;
+ int i;
+ size_t width[4] = { type_width, name_width, used_width, quota_width };
+
+ if (!scripted) {
+ boolean_t first = B_TRUE;
+ for (i = 0; i < 4; i++) {
+ unsigned field = us_field_bits[i];
+ if (!(field & fields))
+ continue;
+
+ col = gettext(us_field_hdr[i]);
+ if (field == USFIELD_TYPE || field == USFIELD_NAME)
+ (void) printf(first?"%-*s":" %-*s", width[i],
+ col);
+ else
+ (void) printf(first?"%*s":" %*s", width[i],
+ col);
+ first = B_FALSE;
+ }
+ (void) printf("\n");
+ }
+
+ for (node = uu_avl_first(avl); node != NULL;
+ node = uu_avl_next(avl, node)) {
+ print_us_node(scripted, parsable, fields, type_width,
+ name_width, used_width, used_width, node);
+ if (rmnode)
+ nvlist_free(node->usn_nvl);
+ }
+}
+
static int
zfs_do_userspace(int argc, char **argv)
{
zfs_handle_t *zhp;
zfs_userquota_prop_t p;
- int error;
+ uu_avl_pool_t *avl_pool;
+ uu_avl_t *avl_tree;
+ uu_avl_walk_t *walk;
- /*
- * Try the python version. If the execv fails, we'll continue
- * and do a simplistic implementation.
- */
- (void) execv(pypath, argv-1);
+ char *cmd;
+ boolean_t scripted = B_FALSE;
+ boolean_t prtnum = B_FALSE;
+ boolean_t parseable = B_FALSE;
+ boolean_t sid2posix = B_FALSE;
+ int error = 0;
+ int c;
+ zfs_sort_column_t *default_sortcol = NULL;
+ zfs_sort_column_t *sortcol = NULL;
+ unsigned types = USTYPE_PSX_USR | USTYPE_SMB_USR;
+ unsigned fields = 0;
+ unsigned props = USPROP_USED | USPROP_QUOTA;
+ us_cbdata_t cb;
+ us_node_t *node;
+ boolean_t resort_avl = B_FALSE;
+
+ if (argc < 2)
+ usage(B_FALSE);
- (void) printf("internal error: %s not found\n"
- "falling back on built-in implementation, "
- "some features will not work\n", pypath);
+ cmd = argv[0];
+ if (0 == strcmp(cmd, "groupspace"))
+ /* toggle default group types */
+ types = USTYPE_PSX_GRP | USTYPE_SMB_GRP;
+
+ /* check options */
+ while ((c = getopt(argc, argv, "nHpo:s:S:t:i")) != -1) {
+ switch (c) {
+ case 'n':
+ prtnum = B_TRUE;
+ break;
+ case 'H':
+ scripted = B_TRUE;
+ break;
+ case 'p':
+ parseable = B_TRUE;
+ break;
+ case 'o':
+ if (parsefields(&fields, us_field_names, us_field_bits,
+ 4) != 0)
+ return (1);
+ break;
+ case 's':
+ if (zfs_add_sort_column(&sortcol, optarg,
+ B_FALSE) != 0) {
+ (void) fprintf(stderr,
+ gettext("invalid property '%s'\n"), optarg);
+ usage(B_FALSE);
+ }
+ break;
+ case 'S':
+ if (zfs_add_sort_column(&sortcol, optarg,
+ B_TRUE) != 0) {
+ (void) fprintf(stderr,
+ gettext("invalid property '%s'\n"), optarg);
+ usage(B_FALSE);
+ }
+ break;
+ case 't':
+ if (parsefields(&types, type_names, type_bits, 5))
+ return (1);
+ break;
+ case 'i':
+ sid2posix = B_TRUE;
+ break;
+ case ':':
+ (void) fprintf(stderr, gettext("missing argument for "
+ "'%c' option\n"), optopt);
+ usage(B_FALSE);
+ break;
+ case '?':
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ usage(B_FALSE);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* ok, now we have sorted by default colums (type,name) avl tree */
+ if (sortcol) {
+ zfs_sort_column_t *sc;
+ for (sc = sortcol; sc; sc = sc->sc_next) {
+ if (sc->sc_prop == ZFS_PROP_QUOTA) {
+ resort_avl = B_TRUE;
+ break;
+ }
+ }
+ }
+
+ if (!fields)
+ fields = USFIELD_ALL;
if ((zhp = zfs_open(g_zfs, argv[argc-1], ZFS_TYPE_DATASET)) == NULL)
return (1);
- (void) printf("PROP TYPE NAME VALUE\n");
+ if ((avl_pool = uu_avl_pool_create("us_avl_pool", sizeof (us_node_t),
+ offsetof(us_node_t, usn_avlnode),
+ us_compare, UU_DEFAULT)) == NULL)
+ nomem();
+ if ((avl_tree = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL)
+ nomem();
+
+ if (sortcol && !resort_avl)
+ cb.cb_sortcol = sortcol;
+ else {
+ (void) zfs_add_sort_column(&default_sortcol, "type", B_FALSE);
+ (void) zfs_add_sort_column(&default_sortcol, "name", B_FALSE);
+ cb.cb_sortcol = default_sortcol;
+ }
+ cb.cb_numname = prtnum;
+ cb.cb_nicenum = !parseable;
+ cb.cb_avl_pool = avl_pool;
+ cb.cb_avl = avl_tree;
+ cb.cb_sid2posix = sid2posix;
+ cb.cb_max_typelen = strlen(gettext("TYPE"));
+ cb.cb_max_namelen = strlen(gettext("NAME"));
+ cb.cb_max_usedlen = strlen(gettext("USED"));
+ cb.cb_max_quotalen = strlen(gettext("QUOTA"));
for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) {
- error = zfs_userspace(zhp, p, userspace_cb, &p);
+ if (!usprop_check(p, types, props))
+ continue;
+
+ cb.cb_prop = p;
+ error = zfs_userspace(zhp, p, userspace_cb, &cb);
if (error)
break;
}
+
+
+ if (resort_avl) {
+ us_node_t *node;
+ us_node_t *rmnode;
+ uu_list_pool_t *listpool;
+ uu_list_t *list;
+ uu_avl_index_t idx = 0;
+ uu_list_index_t idx2 = 0;
+ listpool = uu_list_pool_create("tmplist", sizeof (us_node_t),
+ offsetof(us_node_t, usn_listnode), NULL,
+ UU_DEFAULT);
+ list = uu_list_create(listpool, NULL, UU_DEFAULT);
+
+ node = uu_avl_first(avl_tree);
+ uu_list_node_init(node, &node->usn_listnode, listpool);
+ while (node != NULL) {
+ rmnode = node;
+ node = uu_avl_next(avl_tree, node);
+ uu_avl_remove(avl_tree, rmnode);
+ if (uu_list_find(list, rmnode, NULL, &idx2) == NULL) {
+ uu_list_insert(list, rmnode, idx2);
+ }
+ }
+
+ for (node = uu_list_first(list); node != NULL;
+ node = uu_list_next(list, node)) {
+ us_sort_info_t sortinfo = { sortcol, cb.cb_numname };
+ if (uu_avl_find(avl_tree, node, &sortinfo, &idx) ==
+ NULL)
+ uu_avl_insert(avl_tree, node, idx);
+ }
+
+ uu_list_destroy(list);
+ }
+
+ /* print & free node`s nvlist memory */
+ print_us(scripted, parseable, fields, cb.cb_max_typelen,
+ cb.cb_max_namelen, cb.cb_max_usedlen,
+ cb.cb_max_quotalen, B_TRUE, cb.cb_avl);
+
+ if (sortcol)
+ zfs_free_sort_columns(sortcol);
+ zfs_free_sort_columns(default_sortcol);
+
+ /*
+ * Finally, clean up the AVL tree.
+ */
+ if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL)
+ nomem();
+
+ while ((node = uu_avl_walk_next(walk)) != NULL) {
+ uu_avl_remove(cb.cb_avl, node);
+ free(node);
+ }
+
+ uu_avl_walk_end(walk);
+ uu_avl_destroy(avl_tree);
+ uu_avl_pool_destroy(avl_pool);
+
return (error);
}
@@ -2005,6 +2862,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)
@@ -2065,7 +2929,7 @@ zfs_do_list(int argc, char **argv)
list_cbdata_t cb = { 0 };
char *value;
int limit = 0;
- int ret;
+ int ret = 0;
zfs_sort_column_t *sortcol = NULL;
int flags = ZFS_ITER_PROP_LISTSNAPS | ZFS_ITER_ARGS_CAN_BE_PATHS;
@@ -2180,8 +3044,8 @@ zfs_do_list(int argc, char **argv)
}
/*
- * zfs rename <fs | snap | vol> <fs | snap | vol>
- * zfs rename -p <fs | vol> <fs | vol>
+ * zfs rename [-f] <fs | snap | vol> <fs | snap | vol>
+ * zfs rename [-f] -p <fs | vol> <fs | vol>
* zfs rename -r <snap> <snap>
*
* Renames the given dataset to another of the same type.
@@ -2194,12 +3058,13 @@ zfs_do_rename(int argc, char **argv)
{
zfs_handle_t *zhp;
int c;
- int ret;
+ int ret = 0;
boolean_t recurse = B_FALSE;
boolean_t parents = B_FALSE;
+ boolean_t force_unmount = B_FALSE;
/* check options */
- while ((c = getopt(argc, argv, "pr")) != -1) {
+ while ((c = getopt(argc, argv, "prf")) != -1) {
switch (c) {
case 'p':
parents = B_TRUE;
@@ -2207,6 +3072,9 @@ zfs_do_rename(int argc, char **argv)
case 'r':
recurse = B_TRUE;
break;
+ case 'f':
+ force_unmount = B_TRUE;
+ break;
case '?':
default:
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
@@ -2257,7 +3125,7 @@ zfs_do_rename(int argc, char **argv)
return (1);
}
- ret = (zfs_rename(zhp, argv[1], recurse) != 0);
+ ret = (zfs_rename(zhp, argv[1], recurse, force_unmount) != 0);
zfs_close(zhp);
return (ret);
@@ -2273,7 +3141,7 @@ static int
zfs_do_promote(int argc, char **argv)
{
zfs_handle_t *zhp;
- int ret;
+ int ret = 0;
/* check options */
if (argc > 1 && argv[1][0] == '-') {
@@ -2394,7 +3262,7 @@ rollback_check(zfs_handle_t *zhp, void *data)
static int
zfs_do_rollback(int argc, char **argv)
{
- int ret;
+ int ret = 0;
int c;
boolean_t force = B_FALSE;
rollback_cbdata_t cb = { 0 };
@@ -2512,7 +3380,7 @@ static int
zfs_do_set(int argc, char **argv)
{
set_cbdata_t cb;
- int ret;
+ int ret = 0;
/* check for options */
if (argc > 1 && argv[1][0] == '-') {
@@ -2566,7 +3434,7 @@ static int
zfs_do_snapshot(int argc, char **argv)
{
boolean_t recursive = B_FALSE;
- int ret;
+ int ret = 0;
char c;
nvlist_t *props;
@@ -2616,9 +3484,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
@@ -2630,11 +3495,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)
@@ -2653,14 +3518,22 @@ 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;
flags.verbose = B_TRUE;
+ flags.progress = B_TRUE;
break;
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);
@@ -2686,7 +3559,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"));
@@ -2740,10 +3613,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
@@ -2824,11 +3697,1357 @@ 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);
}
+/*
+ * allow/unallow stuff
+ */
+/* copied from zfs/sys/dsl_deleg.h */
+#define ZFS_DELEG_PERM_CREATE "create"
+#define ZFS_DELEG_PERM_DESTROY "destroy"
+#define ZFS_DELEG_PERM_SNAPSHOT "snapshot"
+#define ZFS_DELEG_PERM_ROLLBACK "rollback"
+#define ZFS_DELEG_PERM_CLONE "clone"
+#define ZFS_DELEG_PERM_PROMOTE "promote"
+#define ZFS_DELEG_PERM_RENAME "rename"
+#define ZFS_DELEG_PERM_MOUNT "mount"
+#define ZFS_DELEG_PERM_SHARE "share"
+#define ZFS_DELEG_PERM_SEND "send"
+#define ZFS_DELEG_PERM_RECEIVE "receive"
+#define ZFS_DELEG_PERM_ALLOW "allow"
+#define ZFS_DELEG_PERM_USERPROP "userprop"
+#define ZFS_DELEG_PERM_VSCAN "vscan" /* ??? */
+#define ZFS_DELEG_PERM_USERQUOTA "userquota"
+#define ZFS_DELEG_PERM_GROUPQUOTA "groupquota"
+#define ZFS_DELEG_PERM_USERUSED "userused"
+#define ZFS_DELEG_PERM_GROUPUSED "groupused"
+#define ZFS_DELEG_PERM_HOLD "hold"
+#define ZFS_DELEG_PERM_RELEASE "release"
+#define ZFS_DELEG_PERM_DIFF "diff"
+
+#define ZFS_NUM_DELEG_NOTES ZFS_DELEG_NOTE_NONE
+
+static zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = {
+ { ZFS_DELEG_PERM_ALLOW, ZFS_DELEG_NOTE_ALLOW },
+ { ZFS_DELEG_PERM_CLONE, ZFS_DELEG_NOTE_CLONE },
+ { ZFS_DELEG_PERM_CREATE, ZFS_DELEG_NOTE_CREATE },
+ { ZFS_DELEG_PERM_DESTROY, ZFS_DELEG_NOTE_DESTROY },
+ { ZFS_DELEG_PERM_DIFF, ZFS_DELEG_NOTE_DIFF},
+ { ZFS_DELEG_PERM_HOLD, ZFS_DELEG_NOTE_HOLD },
+ { ZFS_DELEG_PERM_MOUNT, ZFS_DELEG_NOTE_MOUNT },
+ { ZFS_DELEG_PERM_PROMOTE, ZFS_DELEG_NOTE_PROMOTE },
+ { ZFS_DELEG_PERM_RECEIVE, ZFS_DELEG_NOTE_RECEIVE },
+ { ZFS_DELEG_PERM_RELEASE, ZFS_DELEG_NOTE_RELEASE },
+ { ZFS_DELEG_PERM_RENAME, ZFS_DELEG_NOTE_RENAME },
+ { ZFS_DELEG_PERM_ROLLBACK, ZFS_DELEG_NOTE_ROLLBACK },
+ { ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_SEND },
+ { ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE },
+ { ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT },
+
+ { ZFS_DELEG_PERM_GROUPQUOTA, ZFS_DELEG_NOTE_GROUPQUOTA },
+ { ZFS_DELEG_PERM_GROUPUSED, ZFS_DELEG_NOTE_GROUPUSED },
+ { ZFS_DELEG_PERM_USERPROP, ZFS_DELEG_NOTE_USERPROP },
+ { ZFS_DELEG_PERM_USERQUOTA, ZFS_DELEG_NOTE_USERQUOTA },
+ { ZFS_DELEG_PERM_USERUSED, ZFS_DELEG_NOTE_USERUSED },
+ { NULL, ZFS_DELEG_NOTE_NONE }
+};
+
+/* permission structure */
+typedef struct deleg_perm {
+ zfs_deleg_who_type_t dp_who_type;
+ const char *dp_name;
+ boolean_t dp_local;
+ boolean_t dp_descend;
+} deleg_perm_t;
+
+/* */
+typedef struct deleg_perm_node {
+ deleg_perm_t dpn_perm;
+
+ uu_avl_node_t dpn_avl_node;
+} deleg_perm_node_t;
+
+typedef struct fs_perm fs_perm_t;
+
+/* permissions set */
+typedef struct who_perm {
+ zfs_deleg_who_type_t who_type;
+ const char *who_name; /* id */
+ char who_ug_name[256]; /* user/group name */
+ fs_perm_t *who_fsperm; /* uplink */
+
+ uu_avl_t *who_deleg_perm_avl; /* permissions */
+} who_perm_t;
+
+/* */
+typedef struct who_perm_node {
+ who_perm_t who_perm;
+ uu_avl_node_t who_avl_node;
+} who_perm_node_t;
+
+typedef struct fs_perm_set fs_perm_set_t;
+/* fs permissions */
+struct fs_perm {
+ const char *fsp_name;
+
+ uu_avl_t *fsp_sc_avl; /* sets,create */
+ uu_avl_t *fsp_uge_avl; /* user,group,everyone */
+
+ fs_perm_set_t *fsp_set; /* uplink */
+};
+
+/* */
+typedef struct fs_perm_node {
+ fs_perm_t fspn_fsperm;
+ uu_avl_t *fspn_avl;
+
+ uu_list_node_t fspn_list_node;
+} fs_perm_node_t;
+
+/* top level structure */
+struct fs_perm_set {
+ uu_list_pool_t *fsps_list_pool;
+ uu_list_t *fsps_list; /* list of fs_perms */
+
+ uu_avl_pool_t *fsps_named_set_avl_pool;
+ uu_avl_pool_t *fsps_who_perm_avl_pool;
+ uu_avl_pool_t *fsps_deleg_perm_avl_pool;
+};
+
+static inline const char *
+deleg_perm_type(zfs_deleg_note_t note)
+{
+ /* subcommands */
+ switch (note) {
+ /* SUBCOMMANDS */
+ /* OTHER */
+ case ZFS_DELEG_NOTE_GROUPQUOTA:
+ case ZFS_DELEG_NOTE_GROUPUSED:
+ case ZFS_DELEG_NOTE_USERPROP:
+ case ZFS_DELEG_NOTE_USERQUOTA:
+ case ZFS_DELEG_NOTE_USERUSED:
+ /* other */
+ return (gettext("other"));
+ default:
+ return (gettext("subcommand"));
+ }
+}
+
+static int inline
+who_type2weight(zfs_deleg_who_type_t who_type)
+{
+ int res;
+ switch (who_type) {
+ case ZFS_DELEG_NAMED_SET_SETS:
+ case ZFS_DELEG_NAMED_SET:
+ res = 0;
+ break;
+ case ZFS_DELEG_CREATE_SETS:
+ case ZFS_DELEG_CREATE:
+ res = 1;
+ break;
+ case ZFS_DELEG_USER_SETS:
+ case ZFS_DELEG_USER:
+ res = 2;
+ break;
+ case ZFS_DELEG_GROUP_SETS:
+ case ZFS_DELEG_GROUP:
+ res = 3;
+ break;
+ case ZFS_DELEG_EVERYONE_SETS:
+ case ZFS_DELEG_EVERYONE:
+ res = 4;
+ break;
+ default:
+ res = -1;
+ }
+
+ return (res);
+}
+
+/* ARGSUSED */
+static int
+who_perm_compare(const void *larg, const void *rarg, void *unused)
+{
+ const who_perm_node_t *l = larg;
+ const who_perm_node_t *r = rarg;
+ zfs_deleg_who_type_t ltype = l->who_perm.who_type;
+ zfs_deleg_who_type_t rtype = r->who_perm.who_type;
+ int lweight = who_type2weight(ltype);
+ int rweight = who_type2weight(rtype);
+ int res = lweight - rweight;
+ if (res == 0)
+ res = strncmp(l->who_perm.who_name, r->who_perm.who_name,
+ ZFS_MAX_DELEG_NAME-1);
+
+ if (res == 0)
+ return (0);
+ if (res > 0)
+ return (1);
+ else
+ return (-1);
+}
+
+/* ARGSUSED */
+static int
+deleg_perm_compare(const void *larg, const void *rarg, void *unused)
+{
+ const deleg_perm_node_t *l = larg;
+ const deleg_perm_node_t *r = rarg;
+ int res = strncmp(l->dpn_perm.dp_name, r->dpn_perm.dp_name,
+ ZFS_MAX_DELEG_NAME-1);
+
+ if (res == 0)
+ return (0);
+
+ if (res > 0)
+ return (1);
+ else
+ return (-1);
+}
+
+static inline void
+fs_perm_set_init(fs_perm_set_t *fspset)
+{
+ bzero(fspset, sizeof (fs_perm_set_t));
+
+ if ((fspset->fsps_list_pool = uu_list_pool_create("fsps_list_pool",
+ sizeof (fs_perm_node_t), offsetof(fs_perm_node_t, fspn_list_node),
+ NULL, UU_DEFAULT)) == NULL)
+ nomem();
+ if ((fspset->fsps_list = uu_list_create(fspset->fsps_list_pool, NULL,
+ UU_DEFAULT)) == NULL)
+ nomem();
+
+ if ((fspset->fsps_named_set_avl_pool = uu_avl_pool_create(
+ "named_set_avl_pool", sizeof (who_perm_node_t), offsetof(
+ who_perm_node_t, who_avl_node), who_perm_compare,
+ UU_DEFAULT)) == NULL)
+ nomem();
+
+ if ((fspset->fsps_who_perm_avl_pool = uu_avl_pool_create(
+ "who_perm_avl_pool", sizeof (who_perm_node_t), offsetof(
+ who_perm_node_t, who_avl_node), who_perm_compare,
+ UU_DEFAULT)) == NULL)
+ nomem();
+
+ if ((fspset->fsps_deleg_perm_avl_pool = uu_avl_pool_create(
+ "deleg_perm_avl_pool", sizeof (deleg_perm_node_t), offsetof(
+ deleg_perm_node_t, dpn_avl_node), deleg_perm_compare, UU_DEFAULT))
+ == NULL)
+ nomem();
+}
+
+static inline void fs_perm_fini(fs_perm_t *);
+static inline void who_perm_fini(who_perm_t *);
+
+static inline void
+fs_perm_set_fini(fs_perm_set_t *fspset)
+{
+ fs_perm_node_t *node = uu_list_first(fspset->fsps_list);
+
+ while (node != NULL) {
+ fs_perm_node_t *next_node =
+ uu_list_next(fspset->fsps_list, node);
+ fs_perm_t *fsperm = &node->fspn_fsperm;
+ fs_perm_fini(fsperm);
+ uu_list_remove(fspset->fsps_list, node);
+ free(node);
+ node = next_node;
+ }
+
+ uu_avl_pool_destroy(fspset->fsps_named_set_avl_pool);
+ uu_avl_pool_destroy(fspset->fsps_who_perm_avl_pool);
+ uu_avl_pool_destroy(fspset->fsps_deleg_perm_avl_pool);
+}
+
+static inline void
+deleg_perm_init(deleg_perm_t *deleg_perm, zfs_deleg_who_type_t type,
+ const char *name)
+{
+ deleg_perm->dp_who_type = type;
+ deleg_perm->dp_name = name;
+}
+
+static inline void
+who_perm_init(who_perm_t *who_perm, fs_perm_t *fsperm,
+ zfs_deleg_who_type_t type, const char *name)
+{
+ uu_avl_pool_t *pool;
+ pool = fsperm->fsp_set->fsps_deleg_perm_avl_pool;
+
+ bzero(who_perm, sizeof (who_perm_t));
+
+ if ((who_perm->who_deleg_perm_avl = uu_avl_create(pool, NULL,
+ UU_DEFAULT)) == NULL)
+ nomem();
+
+ who_perm->who_type = type;
+ who_perm->who_name = name;
+ who_perm->who_fsperm = fsperm;
+}
+
+static inline void
+who_perm_fini(who_perm_t *who_perm)
+{
+ deleg_perm_node_t *node = uu_avl_first(who_perm->who_deleg_perm_avl);
+
+ while (node != NULL) {
+ deleg_perm_node_t *next_node =
+ uu_avl_next(who_perm->who_deleg_perm_avl, node);
+
+ uu_avl_remove(who_perm->who_deleg_perm_avl, node);
+ free(node);
+ node = next_node;
+ }
+
+ uu_avl_destroy(who_perm->who_deleg_perm_avl);
+}
+
+static inline void
+fs_perm_init(fs_perm_t *fsperm, fs_perm_set_t *fspset, const char *fsname)
+{
+ uu_avl_pool_t *nset_pool = fspset->fsps_named_set_avl_pool;
+ uu_avl_pool_t *who_pool = fspset->fsps_who_perm_avl_pool;
+
+ bzero(fsperm, sizeof (fs_perm_t));
+
+ if ((fsperm->fsp_sc_avl = uu_avl_create(nset_pool, NULL, UU_DEFAULT))
+ == NULL)
+ nomem();
+
+ if ((fsperm->fsp_uge_avl = uu_avl_create(who_pool, NULL, UU_DEFAULT))
+ == NULL)
+ nomem();
+
+ fsperm->fsp_set = fspset;
+ fsperm->fsp_name = fsname;
+}
+
+static inline void
+fs_perm_fini(fs_perm_t *fsperm)
+{
+ who_perm_node_t *node = uu_avl_first(fsperm->fsp_sc_avl);
+ while (node != NULL) {
+ who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_sc_avl,
+ node);
+ who_perm_t *who_perm = &node->who_perm;
+ who_perm_fini(who_perm);
+ uu_avl_remove(fsperm->fsp_sc_avl, node);
+ free(node);
+ node = next_node;
+ }
+
+ node = uu_avl_first(fsperm->fsp_uge_avl);
+ while (node != NULL) {
+ who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_uge_avl,
+ node);
+ who_perm_t *who_perm = &node->who_perm;
+ who_perm_fini(who_perm);
+ uu_avl_remove(fsperm->fsp_uge_avl, node);
+ free(node);
+ node = next_node;
+ }
+
+ uu_avl_destroy(fsperm->fsp_sc_avl);
+ uu_avl_destroy(fsperm->fsp_uge_avl);
+}
+
+static void inline
+set_deleg_perm_node(uu_avl_t *avl, deleg_perm_node_t *node,
+ zfs_deleg_who_type_t who_type, const char *name, char locality)
+{
+ uu_avl_index_t idx = 0;
+
+ deleg_perm_node_t *found_node = NULL;
+ deleg_perm_t *deleg_perm = &node->dpn_perm;
+
+ deleg_perm_init(deleg_perm, who_type, name);
+
+ if ((found_node = uu_avl_find(avl, node, NULL, &idx))
+ == NULL)
+ uu_avl_insert(avl, node, idx);
+ else {
+ node = found_node;
+ deleg_perm = &node->dpn_perm;
+ }
+
+
+ switch (locality) {
+ case ZFS_DELEG_LOCAL:
+ deleg_perm->dp_local = B_TRUE;
+ break;
+ case ZFS_DELEG_DESCENDENT:
+ deleg_perm->dp_descend = B_TRUE;
+ break;
+ case ZFS_DELEG_NA:
+ break;
+ default:
+ assert(B_FALSE); /* invalid locality */
+ }
+}
+
+static inline int
+parse_who_perm(who_perm_t *who_perm, nvlist_t *nvl, char locality)
+{
+ nvpair_t *nvp = NULL;
+ fs_perm_set_t *fspset = who_perm->who_fsperm->fsp_set;
+ uu_avl_t *avl = who_perm->who_deleg_perm_avl;
+ zfs_deleg_who_type_t who_type = who_perm->who_type;
+
+ while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
+ const char *name = nvpair_name(nvp);
+ data_type_t type = nvpair_type(nvp);
+ uu_avl_pool_t *avl_pool = fspset->fsps_deleg_perm_avl_pool;
+ deleg_perm_node_t *node =
+ safe_malloc(sizeof (deleg_perm_node_t));
+
+ assert(type == DATA_TYPE_BOOLEAN);
+
+ uu_avl_node_init(node, &node->dpn_avl_node, avl_pool);
+ set_deleg_perm_node(avl, node, who_type, name, locality);
+ }
+
+ return (0);
+}
+
+static inline int
+parse_fs_perm(fs_perm_t *fsperm, nvlist_t *nvl)
+{
+ nvpair_t *nvp = NULL;
+ fs_perm_set_t *fspset = fsperm->fsp_set;
+
+ while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
+ nvlist_t *nvl2 = NULL;
+ const char *name = nvpair_name(nvp);
+ uu_avl_t *avl = NULL;
+ uu_avl_pool_t *avl_pool;
+ zfs_deleg_who_type_t perm_type = name[0];
+ char perm_locality = name[1];
+ const char *perm_name = name + 3;
+ boolean_t is_set = B_TRUE;
+ who_perm_t *who_perm = NULL;
+
+ assert('$' == name[2]);
+
+ if (nvpair_value_nvlist(nvp, &nvl2) != 0)
+ return (-1);
+
+ switch (perm_type) {
+ case ZFS_DELEG_CREATE:
+ case ZFS_DELEG_CREATE_SETS:
+ case ZFS_DELEG_NAMED_SET:
+ case ZFS_DELEG_NAMED_SET_SETS:
+ avl_pool = fspset->fsps_named_set_avl_pool;
+ avl = fsperm->fsp_sc_avl;
+ break;
+ case ZFS_DELEG_USER:
+ case ZFS_DELEG_USER_SETS:
+ case ZFS_DELEG_GROUP:
+ case ZFS_DELEG_GROUP_SETS:
+ case ZFS_DELEG_EVERYONE:
+ case ZFS_DELEG_EVERYONE_SETS:
+ avl_pool = fspset->fsps_who_perm_avl_pool;
+ avl = fsperm->fsp_uge_avl;
+ break;
+ }
+
+ if (is_set) {
+ who_perm_node_t *found_node = NULL;
+ who_perm_node_t *node = safe_malloc(
+ sizeof (who_perm_node_t));
+ who_perm = &node->who_perm;
+ uu_avl_index_t idx = 0;
+
+ uu_avl_node_init(node, &node->who_avl_node, avl_pool);
+ who_perm_init(who_perm, fsperm, perm_type, perm_name);
+
+ if ((found_node = uu_avl_find(avl, node, NULL, &idx))
+ == NULL) {
+ if (avl == fsperm->fsp_uge_avl) {
+ uid_t rid = 0;
+ struct passwd *p = NULL;
+ struct group *g = NULL;
+ const char *nice_name = NULL;
+
+ switch (perm_type) {
+ case ZFS_DELEG_USER_SETS:
+ case ZFS_DELEG_USER:
+ rid = atoi(perm_name);
+ p = getpwuid(rid);
+ if (p)
+ nice_name = p->pw_name;
+ break;
+ case ZFS_DELEG_GROUP_SETS:
+ case ZFS_DELEG_GROUP:
+ rid = atoi(perm_name);
+ g = getgrgid(rid);
+ if (g)
+ nice_name = g->gr_name;
+ break;
+ }
+
+ if (nice_name != NULL)
+ (void) strlcpy(
+ node->who_perm.who_ug_name,
+ nice_name, 256);
+ }
+
+ uu_avl_insert(avl, node, idx);
+ } else {
+ node = found_node;
+ who_perm = &node->who_perm;
+ }
+ }
+
+ (void) parse_who_perm(who_perm, nvl2, perm_locality);
+ }
+
+ return (0);
+}
+
+static inline int
+parse_fs_perm_set(fs_perm_set_t *fspset, nvlist_t *nvl)
+{
+ nvpair_t *nvp = NULL;
+ uu_avl_index_t idx = 0;
+
+ while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
+ nvlist_t *nvl2 = NULL;
+ const char *fsname = nvpair_name(nvp);
+ data_type_t type = nvpair_type(nvp);
+ fs_perm_t *fsperm = NULL;
+ fs_perm_node_t *node = safe_malloc(sizeof (fs_perm_node_t));
+ if (node == NULL)
+ nomem();
+
+ fsperm = &node->fspn_fsperm;
+
+ assert(DATA_TYPE_NVLIST == type);
+
+ uu_list_node_init(node, &node->fspn_list_node,
+ fspset->fsps_list_pool);
+
+ idx = uu_list_numnodes(fspset->fsps_list);
+ fs_perm_init(fsperm, fspset, fsname);
+
+ if (nvpair_value_nvlist(nvp, &nvl2) != 0)
+ return (-1);
+
+ (void) parse_fs_perm(fsperm, nvl2);
+
+ uu_list_insert(fspset->fsps_list, node, idx);
+ }
+
+ return (0);
+}
+
+static inline const char *
+deleg_perm_comment(zfs_deleg_note_t note)
+{
+ const char *str = "";
+
+ /* subcommands */
+ switch (note) {
+ /* SUBCOMMANDS */
+ case ZFS_DELEG_NOTE_ALLOW:
+ str = gettext("Must also have the permission that is being"
+ "\n\t\t\t\tallowed");
+ break;
+ case ZFS_DELEG_NOTE_CLONE:
+ str = gettext("Must also have the 'create' ability and 'mount'"
+ "\n\t\t\t\tability in the origin file system");
+ break;
+ case ZFS_DELEG_NOTE_CREATE:
+ str = gettext("Must also have the 'mount' ability");
+ break;
+ case ZFS_DELEG_NOTE_DESTROY:
+ str = gettext("Must also have the 'mount' ability");
+ break;
+ case ZFS_DELEG_NOTE_DIFF:
+ str = gettext("Allows lookup of paths within a dataset;"
+ "\n\t\t\t\tgiven an object number. Ordinary users need this"
+ "\n\t\t\t\tin order to use zfs diff");
+ break;
+ case ZFS_DELEG_NOTE_HOLD:
+ str = gettext("Allows adding a user hold to a snapshot");
+ break;
+ case ZFS_DELEG_NOTE_MOUNT:
+ str = gettext("Allows mount/umount of ZFS datasets");
+ break;
+ case ZFS_DELEG_NOTE_PROMOTE:
+ str = gettext("Must also have the 'mount'\n\t\t\t\tand"
+ " 'promote' ability in the origin file system");
+ break;
+ case ZFS_DELEG_NOTE_RECEIVE:
+ str = gettext("Must also have the 'mount' and 'create'"
+ " ability");
+ break;
+ case ZFS_DELEG_NOTE_RELEASE:
+ str = gettext("Allows releasing a user hold which\n\t\t\t\t"
+ "might destroy the snapshot");
+ break;
+ case ZFS_DELEG_NOTE_RENAME:
+ str = gettext("Must also have the 'mount' and 'create'"
+ "\n\t\t\t\tability in the new parent");
+ break;
+ case ZFS_DELEG_NOTE_ROLLBACK:
+ str = gettext("");
+ break;
+ case ZFS_DELEG_NOTE_SEND:
+ str = gettext("");
+ break;
+ case ZFS_DELEG_NOTE_SHARE:
+ str = gettext("Allows sharing file systems over NFS or SMB"
+ "\n\t\t\t\tprotocols");
+ break;
+ case ZFS_DELEG_NOTE_SNAPSHOT:
+ str = gettext("");
+ break;
+/*
+ * case ZFS_DELEG_NOTE_VSCAN:
+ * str = gettext("");
+ * break;
+ */
+ /* OTHER */
+ case ZFS_DELEG_NOTE_GROUPQUOTA:
+ str = gettext("Allows accessing any groupquota@... property");
+ break;
+ case ZFS_DELEG_NOTE_GROUPUSED:
+ str = gettext("Allows reading any groupused@... property");
+ break;
+ case ZFS_DELEG_NOTE_USERPROP:
+ str = gettext("Allows changing any user property");
+ break;
+ case ZFS_DELEG_NOTE_USERQUOTA:
+ str = gettext("Allows accessing any userquota@... property");
+ break;
+ case ZFS_DELEG_NOTE_USERUSED:
+ str = gettext("Allows reading any userused@... property");
+ break;
+ /* other */
+ default:
+ str = "";
+ }
+
+ return (str);
+}
+
+struct allow_opts {
+ boolean_t local;
+ boolean_t descend;
+ boolean_t user;
+ boolean_t group;
+ boolean_t everyone;
+ boolean_t create;
+ boolean_t set;
+ boolean_t recursive; /* unallow only */
+ boolean_t prt_usage;
+
+ boolean_t prt_perms;
+ char *who;
+ char *perms;
+ const char *dataset;
+};
+
+static inline int
+prop_cmp(const void *a, const void *b)
+{
+ const char *str1 = *(const char **)a;
+ const char *str2 = *(const char **)b;
+ return (strcmp(str1, str2));
+}
+
+static void
+allow_usage(boolean_t un, boolean_t requested, const char *msg)
+{
+ const char *opt_desc[] = {
+ "-h", gettext("show this help message and exit"),
+ "-l", gettext("set permission locally"),
+ "-d", gettext("set permission for descents"),
+ "-u", gettext("set permission for user"),
+ "-g", gettext("set permission for group"),
+ "-e", gettext("set permission for everyone"),
+ "-c", gettext("set create time permission"),
+ "-s", gettext("define permission set"),
+ /* unallow only */
+ "-r", gettext("remove permissions recursively"),
+ };
+ size_t unallow_size = sizeof (opt_desc) / sizeof (char *);
+ size_t allow_size = unallow_size - 2;
+ const char *props[ZFS_NUM_PROPS];
+ int i;
+ size_t count = 0;
+ FILE *fp = requested ? stdout : stderr;
+ zprop_desc_t *pdtbl = zfs_prop_get_table();
+ const char *fmt = gettext("%-16s %-14s\t%s\n");
+
+ (void) fprintf(fp, gettext("Usage: %s\n"), get_usage(un ? HELP_UNALLOW :
+ HELP_ALLOW));
+ (void) fprintf(fp, gettext("Options:\n"));
+ for (int i = 0; i < (un ? unallow_size : allow_size); i++) {
+ const char *opt = opt_desc[i++];
+ const char *optdsc = opt_desc[i];
+ (void) fprintf(fp, gettext(" %-10s %s\n"), opt, optdsc);
+ }
+
+ (void) fprintf(fp, gettext("\nThe following permissions are "
+ "supported:\n\n"));
+ (void) fprintf(fp, fmt, gettext("NAME"), gettext("TYPE"),
+ gettext("NOTES"));
+ for (i = 0; i < ZFS_NUM_DELEG_NOTES; i++) {
+ const char *perm_name = zfs_deleg_perm_tbl[i].z_perm;
+ zfs_deleg_note_t perm_note = zfs_deleg_perm_tbl[i].z_note;
+ const char *perm_type = deleg_perm_type(perm_note);
+ const char *perm_comment = deleg_perm_comment(perm_note);
+ (void) fprintf(fp, fmt, perm_name, perm_type, perm_comment);
+ }
+
+ for (i = 0; i < ZFS_NUM_PROPS; i++) {
+ zprop_desc_t *pd = &pdtbl[i];
+ if (pd->pd_visible != B_TRUE)
+ continue;
+
+ if (pd->pd_attr == PROP_READONLY)
+ continue;
+
+ props[count++] = pd->pd_name;
+ }
+ props[count] = NULL;
+
+ qsort(props, count, sizeof (char *), prop_cmp);
+
+ for (i = 0; i < count; i++)
+ (void) fprintf(fp, fmt, props[i], gettext("property"), "");
+
+ if (msg != NULL)
+ (void) fprintf(fp, gettext("\nzfs: error: %s"), msg);
+
+ exit(requested ? 0 : 2);
+}
+
+static inline const char *
+munge_args(int argc, char **argv, boolean_t un, size_t expected_argc,
+ char **permsp)
+{
+ if (un && argc == expected_argc - 1)
+ *permsp = NULL;
+ else if (argc == expected_argc)
+ *permsp = argv[argc - 2];
+ else
+ allow_usage(un, B_FALSE,
+ gettext("wrong number of parameters\n"));
+
+ return (argv[argc - 1]);
+}
+
+static void
+parse_allow_args(int argc, char **argv, boolean_t un, struct allow_opts *opts)
+{
+ int uge_sum = opts->user + opts->group + opts->everyone;
+ int csuge_sum = opts->create + opts->set + uge_sum;
+ int ldcsuge_sum = csuge_sum + opts->local + opts->descend;
+ int all_sum = un ? ldcsuge_sum + opts->recursive : ldcsuge_sum;
+
+ if (uge_sum > 1)
+ allow_usage(un, B_FALSE,
+ gettext("-u, -g, and -e are mutually exclusive\n"));
+
+ if (opts->prt_usage)
+ if (argc == 0 && all_sum == 0)
+ allow_usage(un, B_TRUE, NULL);
+ else
+ usage(B_FALSE);
+
+ if (opts->set) {
+ if (csuge_sum > 1)
+ allow_usage(un, B_FALSE,
+ gettext("invalid options combined with -s\n"));
+
+ opts->dataset = munge_args(argc, argv, un, 3, &opts->perms);
+ if (argv[0][0] != '@')
+ allow_usage(un, B_FALSE,
+ gettext("invalid set name: missing '@' prefix\n"));
+ opts->who = argv[0];
+ } else if (opts->create) {
+ if (ldcsuge_sum > 1)
+ allow_usage(un, B_FALSE,
+ gettext("invalid options combined with -c\n"));
+ opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
+ } else if (opts->everyone) {
+ if (csuge_sum > 1)
+ allow_usage(un, B_FALSE,
+ gettext("invalid options combined with -e\n"));
+ opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
+ } else if (uge_sum == 0 && argc > 0 && strcmp(argv[0], "everyone")
+ == 0) {
+ opts->everyone = B_TRUE;
+ argc--;
+ argv++;
+ opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
+ } else if (argc == 1 && !un) {
+ opts->prt_perms = B_TRUE;
+ opts->dataset = argv[argc-1];
+ } else {
+ opts->dataset = munge_args(argc, argv, un, 3, &opts->perms);
+ opts->who = argv[0];
+ }
+
+ if (!opts->local && !opts->descend) {
+ opts->local = B_TRUE;
+ opts->descend = B_TRUE;
+ }
+}
+
+static void
+store_allow_perm(zfs_deleg_who_type_t type, boolean_t local, boolean_t descend,
+ const char *who, char *perms, nvlist_t *top_nvl)
+{
+ int i;
+ char ld[2] = { '\0', '\0' };
+ char who_buf[ZFS_MAXNAMELEN+32];
+ char base_type;
+ char set_type;
+ nvlist_t *base_nvl = NULL;
+ nvlist_t *set_nvl = NULL;
+ nvlist_t *nvl;
+
+ if (nvlist_alloc(&base_nvl, NV_UNIQUE_NAME, 0) != 0)
+ nomem();
+ if (nvlist_alloc(&set_nvl, NV_UNIQUE_NAME, 0) != 0)
+ nomem();
+
+ switch (type) {
+ case ZFS_DELEG_NAMED_SET_SETS:
+ case ZFS_DELEG_NAMED_SET:
+ set_type = ZFS_DELEG_NAMED_SET_SETS;
+ base_type = ZFS_DELEG_NAMED_SET;
+ ld[0] = ZFS_DELEG_NA;
+ break;
+ case ZFS_DELEG_CREATE_SETS:
+ case ZFS_DELEG_CREATE:
+ set_type = ZFS_DELEG_CREATE_SETS;
+ base_type = ZFS_DELEG_CREATE;
+ ld[0] = ZFS_DELEG_NA;
+ break;
+ case ZFS_DELEG_USER_SETS:
+ case ZFS_DELEG_USER:
+ set_type = ZFS_DELEG_USER_SETS;
+ base_type = ZFS_DELEG_USER;
+ if (local)
+ ld[0] = ZFS_DELEG_LOCAL;
+ if (descend)
+ ld[1] = ZFS_DELEG_DESCENDENT;
+ break;
+ case ZFS_DELEG_GROUP_SETS:
+ case ZFS_DELEG_GROUP:
+ set_type = ZFS_DELEG_GROUP_SETS;
+ base_type = ZFS_DELEG_GROUP;
+ if (local)
+ ld[0] = ZFS_DELEG_LOCAL;
+ if (descend)
+ ld[1] = ZFS_DELEG_DESCENDENT;
+ break;
+ case ZFS_DELEG_EVERYONE_SETS:
+ case ZFS_DELEG_EVERYONE:
+ set_type = ZFS_DELEG_EVERYONE_SETS;
+ base_type = ZFS_DELEG_EVERYONE;
+ if (local)
+ ld[0] = ZFS_DELEG_LOCAL;
+ if (descend)
+ ld[1] = ZFS_DELEG_DESCENDENT;
+ }
+
+ if (perms != NULL) {
+ char *curr = perms;
+ char *end = curr + strlen(perms);
+
+ while (curr < end) {
+ char *delim = strchr(curr, ',');
+ if (delim == NULL)
+ delim = end;
+ else
+ *delim = '\0';
+
+ if (curr[0] == '@')
+ nvl = set_nvl;
+ else
+ nvl = base_nvl;
+
+ (void) nvlist_add_boolean(nvl, curr);
+ if (delim != end)
+ *delim = ',';
+ curr = delim + 1;
+ }
+
+ for (i = 0; i < 2; i++) {
+ char locality = ld[i];
+ if (locality == 0)
+ continue;
+
+ if (!nvlist_empty(base_nvl)) {
+ if (who != NULL)
+ (void) snprintf(who_buf,
+ sizeof (who_buf), "%c%c$%s",
+ base_type, locality, who);
+ else
+ (void) snprintf(who_buf,
+ sizeof (who_buf), "%c%c$",
+ base_type, locality);
+
+ (void) nvlist_add_nvlist(top_nvl, who_buf,
+ base_nvl);
+ }
+
+
+ if (!nvlist_empty(set_nvl)) {
+ if (who != NULL)
+ (void) snprintf(who_buf,
+ sizeof (who_buf), "%c%c$%s",
+ set_type, locality, who);
+ else
+ (void) snprintf(who_buf,
+ sizeof (who_buf), "%c%c$",
+ set_type, locality);
+
+ (void) nvlist_add_nvlist(top_nvl, who_buf,
+ set_nvl);
+ }
+ }
+ } else {
+ for (i = 0; i < 2; i++) {
+ char locality = ld[i];
+ if (locality == 0)
+ continue;
+
+ if (who != NULL)
+ (void) snprintf(who_buf, sizeof (who_buf),
+ "%c%c$%s", base_type, locality, who);
+ else
+ (void) snprintf(who_buf, sizeof (who_buf),
+ "%c%c$", base_type, locality);
+ (void) nvlist_add_boolean(top_nvl, who_buf);
+
+ if (who != NULL)
+ (void) snprintf(who_buf, sizeof (who_buf),
+ "%c%c$%s", set_type, locality, who);
+ else
+ (void) snprintf(who_buf, sizeof (who_buf),
+ "%c%c$", set_type, locality);
+ (void) nvlist_add_boolean(top_nvl, who_buf);
+ }
+ }
+}
+
+static int
+construct_fsacl_list(boolean_t un, struct allow_opts *opts, nvlist_t **nvlp)
+{
+ if (nvlist_alloc(nvlp, NV_UNIQUE_NAME, 0) != 0)
+ nomem();
+
+ if (opts->set) {
+ store_allow_perm(ZFS_DELEG_NAMED_SET, opts->local,
+ opts->descend, opts->who, opts->perms, *nvlp);
+ } else if (opts->create) {
+ store_allow_perm(ZFS_DELEG_CREATE, opts->local,
+ opts->descend, NULL, opts->perms, *nvlp);
+ } else if (opts->everyone) {
+ store_allow_perm(ZFS_DELEG_EVERYONE, opts->local,
+ opts->descend, NULL, opts->perms, *nvlp);
+ } else {
+ char *curr = opts->who;
+ char *end = curr + strlen(curr);
+
+ while (curr < end) {
+ const char *who;
+ zfs_deleg_who_type_t who_type;
+ char *endch;
+ char *delim = strchr(curr, ',');
+ char errbuf[256];
+ char id[64];
+ struct passwd *p = NULL;
+ struct group *g = NULL;
+
+ uid_t rid;
+ if (delim == NULL)
+ delim = end;
+ else
+ *delim = '\0';
+
+ rid = (uid_t)strtol(curr, &endch, 0);
+ if (opts->user) {
+ who_type = ZFS_DELEG_USER;
+ if (*endch != '\0')
+ p = getpwnam(curr);
+ else
+ p = getpwuid(rid);
+
+ if (p != NULL)
+ rid = p->pw_uid;
+ else {
+ (void) snprintf(errbuf, 256, gettext(
+ "invalid user %s"), curr);
+ allow_usage(un, B_TRUE, errbuf);
+ }
+ } else if (opts->group) {
+ who_type = ZFS_DELEG_GROUP;
+ if (*endch != '\0')
+ g = getgrnam(curr);
+ else
+ g = getgrgid(rid);
+
+ if (g != NULL)
+ rid = g->gr_gid;
+ else {
+ (void) snprintf(errbuf, 256, gettext(
+ "invalid group %s"), curr);
+ allow_usage(un, B_TRUE, errbuf);
+ }
+ } else {
+ if (*endch != '\0') {
+ p = getpwnam(curr);
+ } else {
+ p = getpwuid(rid);
+ }
+
+ if (p == NULL)
+ if (*endch != '\0') {
+ g = getgrnam(curr);
+ } else {
+ g = getgrgid(rid);
+ }
+
+ if (p != NULL) {
+ who_type = ZFS_DELEG_USER;
+ rid = p->pw_uid;
+ } else if (g != NULL) {
+ who_type = ZFS_DELEG_GROUP;
+ rid = g->gr_gid;
+ } else {
+ (void) snprintf(errbuf, 256, gettext(
+ "invalid user/group %s"), curr);
+ allow_usage(un, B_TRUE, errbuf);
+ }
+ }
+
+ (void) sprintf(id, "%u", rid);
+ who = id;
+
+ store_allow_perm(who_type, opts->local,
+ opts->descend, who, opts->perms, *nvlp);
+ curr = delim + 1;
+ }
+ }
+
+ return (0);
+}
+
+static void
+print_set_creat_perms(uu_avl_t *who_avl)
+{
+ const char *sc_title[] = {
+ gettext("Permission sets:\n"),
+ gettext("Create time permissions:\n"),
+ NULL
+ };
+ const char **title_ptr = sc_title;
+ who_perm_node_t *who_node = NULL;
+ int prev_weight = -1;
+
+ for (who_node = uu_avl_first(who_avl); who_node != NULL;
+ who_node = uu_avl_next(who_avl, who_node)) {
+ uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl;
+ zfs_deleg_who_type_t who_type = who_node->who_perm.who_type;
+ const char *who_name = who_node->who_perm.who_name;
+ int weight = who_type2weight(who_type);
+ boolean_t first = B_TRUE;
+ deleg_perm_node_t *deleg_node;
+
+ if (prev_weight != weight) {
+ (void) printf(*title_ptr++);
+ prev_weight = weight;
+ }
+
+ if (who_name == NULL || strnlen(who_name, 1) == 0)
+ (void) printf("\t");
+ else
+ (void) printf("\t%s ", who_name);
+
+ for (deleg_node = uu_avl_first(avl); deleg_node != NULL;
+ deleg_node = uu_avl_next(avl, deleg_node)) {
+ if (first) {
+ (void) printf("%s",
+ deleg_node->dpn_perm.dp_name);
+ first = B_FALSE;
+ } else
+ (void) printf(",%s",
+ deleg_node->dpn_perm.dp_name);
+ }
+
+ (void) printf("\n");
+ }
+}
+
+static void inline
+print_uge_deleg_perms(uu_avl_t *who_avl, boolean_t local, boolean_t descend,
+ const char *title)
+{
+ who_perm_node_t *who_node = NULL;
+ boolean_t prt_title = B_TRUE;
+ uu_avl_walk_t *walk;
+
+ if ((walk = uu_avl_walk_start(who_avl, UU_WALK_ROBUST)) == NULL)
+ nomem();
+
+ while ((who_node = uu_avl_walk_next(walk)) != NULL) {
+ const char *who_name = who_node->who_perm.who_name;
+ const char *nice_who_name = who_node->who_perm.who_ug_name;
+ uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl;
+ zfs_deleg_who_type_t who_type = who_node->who_perm.who_type;
+ char delim = ' ';
+ deleg_perm_node_t *deleg_node;
+ boolean_t prt_who = B_TRUE;
+
+ for (deleg_node = uu_avl_first(avl);
+ deleg_node != NULL;
+ deleg_node = uu_avl_next(avl, deleg_node)) {
+ if (local != deleg_node->dpn_perm.dp_local ||
+ descend != deleg_node->dpn_perm.dp_descend)
+ continue;
+
+ if (prt_who) {
+ const char *who = NULL;
+ if (prt_title) {
+ prt_title = B_FALSE;
+ (void) printf(title);
+ }
+
+ switch (who_type) {
+ case ZFS_DELEG_USER_SETS:
+ case ZFS_DELEG_USER:
+ who = gettext("user");
+ if (nice_who_name)
+ who_name = nice_who_name;
+ break;
+ case ZFS_DELEG_GROUP_SETS:
+ case ZFS_DELEG_GROUP:
+ who = gettext("group");
+ if (nice_who_name)
+ who_name = nice_who_name;
+ break;
+ case ZFS_DELEG_EVERYONE_SETS:
+ case ZFS_DELEG_EVERYONE:
+ who = gettext("everyone");
+ who_name = NULL;
+ }
+
+ prt_who = B_FALSE;
+ if (who_name == NULL)
+ (void) printf("\t%s", who);
+ else
+ (void) printf("\t%s %s", who, who_name);
+ }
+
+ (void) printf("%c%s", delim,
+ deleg_node->dpn_perm.dp_name);
+ delim = ',';
+ }
+
+ if (!prt_who)
+ (void) printf("\n");
+ }
+
+ uu_avl_walk_end(walk);
+}
+
+static void
+print_fs_perms(fs_perm_set_t *fspset)
+{
+ fs_perm_node_t *node = NULL;
+ char buf[ZFS_MAXNAMELEN+32];
+ const char *dsname = buf;
+
+ for (node = uu_list_first(fspset->fsps_list); node != NULL;
+ node = uu_list_next(fspset->fsps_list, node)) {
+ uu_avl_t *sc_avl = node->fspn_fsperm.fsp_sc_avl;
+ uu_avl_t *uge_avl = node->fspn_fsperm.fsp_uge_avl;
+ int left = 0;
+
+ (void) snprintf(buf, ZFS_MAXNAMELEN+32,
+ gettext("---- Permissions on %s "),
+ node->fspn_fsperm.fsp_name);
+ (void) printf(dsname);
+ left = 70 - strlen(buf);
+ while (left-- > 0)
+ (void) printf("-");
+ (void) printf("\n");
+
+ print_set_creat_perms(sc_avl);
+ print_uge_deleg_perms(uge_avl, B_TRUE, B_FALSE,
+ gettext("Local permissions:\n"));
+ print_uge_deleg_perms(uge_avl, B_FALSE, B_TRUE,
+ gettext("Descendent permissions:\n"));
+ print_uge_deleg_perms(uge_avl, B_TRUE, B_TRUE,
+ gettext("Local+Descendent permissions:\n"));
+ }
+}
+
+static fs_perm_set_t fs_perm_set = { NULL, NULL, NULL, NULL };
+
+struct deleg_perms {
+ boolean_t un;
+ nvlist_t *nvl;
+};
+
+static int
+set_deleg_perms(zfs_handle_t *zhp, void *data)
+{
+ struct deleg_perms *perms = (struct deleg_perms *)data;
+ zfs_type_t zfs_type = zfs_get_type(zhp);
+
+ if (zfs_type != ZFS_TYPE_FILESYSTEM && zfs_type != ZFS_TYPE_VOLUME)
+ return (0);
+
+ return (zfs_set_fsacl(zhp, perms->un, perms->nvl));
+}
+
+static int
+zfs_do_allow_unallow_impl(int argc, char **argv, boolean_t un)
+{
+ zfs_handle_t *zhp;
+ nvlist_t *perm_nvl = NULL;
+ nvlist_t *update_perm_nvl = NULL;
+ int error = 1;
+ int c;
+ struct allow_opts opts = { 0 };
+
+ const char *optstr = un ? "ldugecsrh" : "ldugecsh";
+
+ /* check opts */
+ while ((c = getopt(argc, argv, optstr)) != -1) {
+ switch (c) {
+ case 'l':
+ opts.local = B_TRUE;
+ break;
+ case 'd':
+ opts.descend = B_TRUE;
+ break;
+ case 'u':
+ opts.user = B_TRUE;
+ break;
+ case 'g':
+ opts.group = B_TRUE;
+ break;
+ case 'e':
+ opts.everyone = B_TRUE;
+ break;
+ case 's':
+ opts.set = B_TRUE;
+ break;
+ case 'c':
+ opts.create = B_TRUE;
+ break;
+ case 'r':
+ opts.recursive = B_TRUE;
+ break;
+ case ':':
+ (void) fprintf(stderr, gettext("missing argument for "
+ "'%c' option\n"), optopt);
+ usage(B_FALSE);
+ break;
+ case 'h':
+ opts.prt_usage = B_TRUE;
+ break;
+ case '?':
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ usage(B_FALSE);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* check arguments */
+ parse_allow_args(argc, argv, un, &opts);
+
+ /* try to open the dataset */
+ if ((zhp = zfs_open(g_zfs, opts.dataset, ZFS_TYPE_FILESYSTEM |
+ ZFS_TYPE_VOLUME)) == NULL) {
+ (void) fprintf(stderr, "Failed to open dataset: %s\n",
+ opts.dataset);
+ return (-1);
+ }
+
+ if (zfs_get_fsacl(zhp, &perm_nvl) != 0)
+ goto cleanup2;
+
+ fs_perm_set_init(&fs_perm_set);
+ if (parse_fs_perm_set(&fs_perm_set, perm_nvl) != 0) {
+ (void) fprintf(stderr, "Failed to parse fsacl permissions\n");
+ goto cleanup1;
+ }
+
+ if (opts.prt_perms)
+ print_fs_perms(&fs_perm_set);
+ else {
+ (void) construct_fsacl_list(un, &opts, &update_perm_nvl);
+ if (zfs_set_fsacl(zhp, un, update_perm_nvl) != 0)
+ goto cleanup0;
+
+ if (un && opts.recursive) {
+ struct deleg_perms data = { un, update_perm_nvl };
+ if (zfs_iter_filesystems(zhp, set_deleg_perms,
+ &data) != 0)
+ goto cleanup0;
+ }
+ }
+
+ error = 0;
+
+cleanup0:
+ nvlist_free(perm_nvl);
+ if (update_perm_nvl != NULL)
+ nvlist_free(update_perm_nvl);
+cleanup1:
+ fs_perm_set_fini(&fs_perm_set);
+cleanup2:
+ zfs_close(zhp);
+
+ return (error);
+}
+
+/*
+ * zfs allow [-r] [-t] <tag> <snap> ...
+ *
+ * -r Recursively hold
+ * -t Temporary hold (hidden option)
+ *
+ * Apply a user-hold with the given tag to the list of snapshots.
+ */
+static int
+zfs_do_allow(int argc, char **argv)
+{
+ return (zfs_do_allow_unallow_impl(argc, argv, B_FALSE));
+}
+
+/*
+ * zfs unallow [-r] [-t] <tag> <snap> ...
+ *
+ * -r Recursively hold
+ * -t Temporary hold (hidden option)
+ *
+ * Apply a user-hold with the given tag to the list of snapshots.
+ */
+static int
+zfs_do_unallow(int argc, char **argv)
+{
+ return (zfs_do_allow_unallow_impl(argc, argv, B_TRUE));
+}
+
static int
zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding)
{
@@ -2936,6 +5155,200 @@ zfs_do_release(int argc, char **argv)
return (zfs_do_hold_rele_impl(argc, argv, B_FALSE));
}
+typedef struct holds_cbdata {
+ boolean_t cb_recursive;
+ const char *cb_snapname;
+ nvlist_t **cb_nvlp;
+ size_t cb_max_namelen;
+ size_t cb_max_taglen;
+} holds_cbdata_t;
+
+#define STRFTIME_FMT_STR "%a %b %e %k:%M %Y"
+#define DATETIME_BUF_LEN (32)
+/*
+ *
+ */
+static void
+print_holds(boolean_t scripted, size_t nwidth, size_t tagwidth, nvlist_t *nvl)
+{
+ int i;
+ nvpair_t *nvp = NULL;
+ char *hdr_cols[] = { "NAME", "TAG", "TIMESTAMP" };
+ const char *col;
+
+ if (!scripted) {
+ for (i = 0; i < 3; i++) {
+ col = gettext(hdr_cols[i]);
+ if (i < 2)
+ (void) printf("%-*s ", i ? tagwidth : nwidth,
+ col);
+ else
+ (void) printf("%s\n", col);
+ }
+ }
+
+ while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
+ char *zname = nvpair_name(nvp);
+ nvlist_t *nvl2;
+ nvpair_t *nvp2 = NULL;
+ (void) nvpair_value_nvlist(nvp, &nvl2);
+ while ((nvp2 = nvlist_next_nvpair(nvl2, nvp2)) != NULL) {
+ char tsbuf[DATETIME_BUF_LEN];
+ char *tagname = nvpair_name(nvp2);
+ uint64_t val = 0;
+ time_t time;
+ struct tm t;
+ char sep = scripted ? '\t' : ' ';
+ size_t sepnum = scripted ? 1 : 2;
+
+ (void) nvpair_value_uint64(nvp2, &val);
+ time = (time_t)val;
+ (void) localtime_r(&time, &t);
+ (void) strftime(tsbuf, DATETIME_BUF_LEN,
+ gettext(STRFTIME_FMT_STR), &t);
+
+ (void) printf("%-*s%*c%-*s%*c%s\n", nwidth, zname,
+ sepnum, sep, tagwidth, tagname, sepnum, sep, tsbuf);
+ }
+ }
+}
+
+/*
+ * Generic callback function to list a dataset or snapshot.
+ */
+static int
+holds_callback(zfs_handle_t *zhp, void *data)
+{
+ holds_cbdata_t *cbp = data;
+ nvlist_t *top_nvl = *cbp->cb_nvlp;
+ nvlist_t *nvl = NULL;
+ nvpair_t *nvp = NULL;
+ const char *zname = zfs_get_name(zhp);
+ size_t znamelen = strnlen(zname, ZFS_MAXNAMELEN);
+
+ if (cbp->cb_recursive) {
+ const char *snapname;
+ char *delim = strchr(zname, '@');
+ if (delim == NULL)
+ return (0);
+
+ snapname = delim + 1;
+ if (strcmp(cbp->cb_snapname, snapname))
+ return (0);
+ }
+
+ if (zfs_get_holds(zhp, &nvl) != 0)
+ return (-1);
+
+ if (znamelen > cbp->cb_max_namelen)
+ cbp->cb_max_namelen = znamelen;
+
+ while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
+ const char *tag = nvpair_name(nvp);
+ size_t taglen = strnlen(tag, MAXNAMELEN);
+ if (taglen > cbp->cb_max_taglen)
+ cbp->cb_max_taglen = taglen;
+ }
+
+ return (nvlist_add_nvlist(top_nvl, zname, nvl));
+}
+
+/*
+ * zfs holds [-r] <snap> ...
+ *
+ * -r Recursively hold
+ */
+static int
+zfs_do_holds(int argc, char **argv)
+{
+ int errors = 0;
+ int c;
+ int i;
+ boolean_t scripted = B_FALSE;
+ boolean_t recursive = B_FALSE;
+ const char *opts = "rH";
+ nvlist_t *nvl;
+
+ int types = ZFS_TYPE_SNAPSHOT;
+ holds_cbdata_t cb = { 0 };
+
+ int limit = 0;
+ int ret = 0;
+ int flags = 0;
+
+ /* check options */
+ while ((c = getopt(argc, argv, opts)) != -1) {
+ switch (c) {
+ case 'r':
+ recursive = B_TRUE;
+ break;
+ case 'H':
+ scripted = B_TRUE;
+ break;
+ case '?':
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ usage(B_FALSE);
+ }
+ }
+
+ if (recursive) {
+ types |= ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME;
+ flags |= ZFS_ITER_RECURSE;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* check number of arguments */
+ if (argc < 1)
+ usage(B_FALSE);
+
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
+ nomem();
+
+ for (i = 0; i < argc; ++i) {
+ char *snapshot = argv[i];
+ const char *delim;
+ const char *snapname;
+
+ delim = strchr(snapshot, '@');
+ if (delim == NULL) {
+ (void) fprintf(stderr,
+ gettext("'%s' is not a snapshot\n"), snapshot);
+ ++errors;
+ continue;
+ }
+ snapname = delim + 1;
+ if (recursive)
+ snapshot[delim - snapshot] = '\0';
+
+ cb.cb_recursive = recursive;
+ cb.cb_snapname = snapname;
+ cb.cb_nvlp = &nvl;
+
+ /*
+ * 1. collect holds data, set format options
+ */
+ ret = zfs_for_each(argc, argv, flags, types, NULL, NULL, limit,
+ holds_callback, &cb);
+ if (ret != 0)
+ ++errors;
+ }
+
+ /*
+ * 2. print holds data
+ */
+ print_holds(scripted, cb.cb_max_namelen, cb.cb_max_taglen, nvl);
+
+ if (nvlist_empty(nvl))
+ (void) printf(gettext("no datasets available\n"));
+
+ nvlist_free(nvl);
+
+ return (0 != errors);
+}
+
#define CHECK_SPINNER 30
#define SPINNER_TIME 3 /* seconds */
#define MOUNT_TIME 5 /* seconds */
@@ -3437,7 +5850,7 @@ static int
unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)
{
zfs_handle_t *zhp;
- int ret;
+ int ret = 0;
struct stat64 statbuf;
struct extmnttab entry;
const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount";
@@ -3820,15 +6233,6 @@ zfs_do_unshare(int argc, char **argv)
return (unshare_unmount(OP_SHARE, argc, argv));
}
-/* ARGSUSED */
-static int
-zfs_do_python(int argc, char **argv)
-{
- (void) execv(pypath, argv-1);
- (void) printf("internal error: %s not found\n", pypath);
- return (-1);
-}
-
/*
* Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is
* 'legacy'. Otherwise, complain that use should be using 'zfs mount'.
@@ -3839,7 +6243,7 @@ manual_mount(int argc, char **argv)
zfs_handle_t *zhp;
char mountpoint[ZFS_MAXPROPLEN];
char mntopts[MNT_LINE_MAX] = { '\0' };
- int ret;
+ int ret = 0;
int c;
int flags = 0;
char *dataset, *path;
@@ -3989,7 +6393,7 @@ zfs_do_diff(int argc, char **argv)
char *tosnap = NULL;
char *fromsnap = NULL;
char *atp, *copy;
- int err;
+ int err = 0;
int c;
while ((c = getopt(argc, argv, "FHt")) != -1) {
@@ -4059,7 +6463,7 @@ zfs_do_diff(int argc, char **argv)
int
main(int argc, char **argv)
{
- int ret;
+ int ret = 0;
int i;
char *progname;
char *cmdname;
diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c
index 8aa985b1a552..bc300b3bd958 100644
--- a/cmd/zpool/zpool_main.c
+++ b/cmd/zpool/zpool_main.c
@@ -21,6 +21,9 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2012 by Frederik Wessels. All rights reserved.
*/
#include <assert.h>
@@ -40,6 +43,7 @@
#include <priv.h>
#include <pwd.h>
#include <zone.h>
+#include <zfs_prop.h>
#include <sys/fs/zfs.h>
#include <sys/stat.h>
@@ -47,6 +51,7 @@
#include "zpool_util.h"
#include "zfs_comutil.h"
+#include "zfeature_common.h"
#include "statcommon.h"
@@ -63,6 +68,9 @@ static int zpool_do_status(int, char **);
static int zpool_do_online(int, char **);
static int zpool_do_offline(int, char **);
static int zpool_do_clear(int, char **);
+static int zpool_do_reopen(int, char **);
+
+static int zpool_do_reguid(int, char **);
static int zpool_do_attach(int, char **);
static int zpool_do_detach(int, char **);
@@ -121,7 +129,9 @@ typedef enum {
HELP_UPGRADE,
HELP_GET,
HELP_SET,
- HELP_SPLIT
+ HELP_SPLIT,
+ HELP_REGUID,
+ HELP_REOPEN
} zpool_help_t;
@@ -154,6 +164,7 @@ static zpool_command_t command_table[] = {
{ "online", zpool_do_online, HELP_ONLINE },
{ "offline", zpool_do_offline, HELP_OFFLINE },
{ "clear", zpool_do_clear, HELP_CLEAR },
+ { "reopen", zpool_do_reopen, HELP_REOPEN },
{ NULL },
{ "attach", zpool_do_attach, HELP_ATTACH },
{ "detach", zpool_do_detach, HELP_DETACH },
@@ -165,6 +176,7 @@ static zpool_command_t command_table[] = {
{ "import", zpool_do_import, HELP_IMPORT },
{ "export", zpool_do_export, HELP_EXPORT },
{ "upgrade", zpool_do_upgrade, HELP_UPGRADE },
+ { "reguid", zpool_do_reguid, HELP_REGUID },
{ NULL },
{ "history", zpool_do_history, HELP_HISTORY },
{ "get", zpool_do_get, HELP_GET },
@@ -189,7 +201,7 @@ get_usage(zpool_help_t idx) {
case HELP_CLEAR:
return (gettext("\tclear [-nF] <pool> [device]\n"));
case HELP_CREATE:
- return (gettext("\tcreate [-fn] [-o property=value] ... \n"
+ return (gettext("\tcreate [-fnd] [-o property=value] ... \n"
"\t [-O file-system-property=value] ... \n"
"\t [-m mountpoint] [-R root] <pool> <vdev> ...\n"));
case HELP_DESTROY:
@@ -225,6 +237,8 @@ get_usage(zpool_help_t idx) {
"[new-device]\n"));
case HELP_REMOVE:
return (gettext("\tremove <pool> <device> ...\n"));
+ case HELP_REOPEN:
+ return (""); /* Undocumented command */
case HELP_SCRUB:
return (gettext("\tscrub [-s] <pool> ...\n"));
case HELP_STATUS:
@@ -243,6 +257,8 @@ get_usage(zpool_help_t idx) {
return (gettext("\tsplit [-n] [-R altroot] [-o mntopts]\n"
"\t [-o property=value] <pool> <newpool> "
"[<device> ...]\n"));
+ case HELP_REGUID:
+ return (gettext("\treguid <pool>\n"));
}
abort();
@@ -316,6 +332,12 @@ usage(boolean_t requested)
/* Iterate over all properties */
(void) zprop_iter(print_prop_cb, fp, B_FALSE, B_TRUE,
ZFS_TYPE_POOL);
+
+ (void) fprintf(fp, "\t%-15s ", "feature@...");
+ (void) fprintf(fp, "YES disabled | enabled | active\n");
+
+ (void) fprintf(fp, gettext("\nThe feature@ properties must be "
+ "appended with a feature name.\nSee zpool-features(5).\n"));
}
/*
@@ -382,12 +404,16 @@ add_prop_list(const char *propname, char *propval, nvlist_t **props,
proplist = *props;
if (poolprop) {
- if ((prop = zpool_name_to_prop(propname)) == ZPROP_INVAL) {
+ if ((prop = zpool_name_to_prop(propname)) == ZPROP_INVAL &&
+ !zpool_prop_feature(propname)) {
(void) fprintf(stderr, gettext("property '%s' is "
"not a valid pool property\n"), propname);
return (2);
}
- normnm = zpool_prop_to_name(prop);
+ if (zpool_prop_feature(propname))
+ normnm = propname;
+ else
+ normnm = zpool_prop_to_name(prop);
} else {
if ((fprop = zfs_name_to_prop(propname)) != ZPROP_INVAL) {
normnm = zfs_prop_to_name(fprop);
@@ -559,7 +585,7 @@ zpool_do_remove(int argc, char **argv)
}
/*
- * zpool create [-fn] [-o property=value] ...
+ * zpool create [-fnd] [-o property=value] ...
* [-O file-system-property=value] ...
* [-R root] [-m mountpoint] <pool> <dev> ...
*
@@ -568,8 +594,10 @@ zpool_do_remove(int argc, char **argv)
* were to be created.
* -R Create a pool under an alternate root
* -m Set default mountpoint for the root dataset. By default it's
- * '/<pool>'
+ * '/<pool>'
* -o Set property=value.
+ * -d Don't automatically enable all supported pool features
+ * (individual features can be enabled with -o).
* -O Set fsproperty=value in the pool's root file system
*
* Creates the named pool according to the given vdev specification. The
@@ -582,6 +610,7 @@ zpool_do_create(int argc, char **argv)
{
boolean_t force = B_FALSE;
boolean_t dryrun = B_FALSE;
+ boolean_t enable_all_pool_feat = B_TRUE;
int c;
nvlist_t *nvroot = NULL;
char *poolname;
@@ -593,7 +622,7 @@ zpool_do_create(int argc, char **argv)
char *propval;
/* check options */
- while ((c = getopt(argc, argv, ":fnR:m:o:O:")) != -1) {
+ while ((c = getopt(argc, argv, ":fndR:m:o:O:")) != -1) {
switch (c) {
case 'f':
force = B_TRUE;
@@ -601,6 +630,9 @@ zpool_do_create(int argc, char **argv)
case 'n':
dryrun = B_TRUE;
break;
+ case 'd':
+ enable_all_pool_feat = B_FALSE;
+ break;
case 'R':
altroot = optarg;
if (add_prop_list(zpool_prop_to_name(
@@ -628,6 +660,21 @@ zpool_do_create(int argc, char **argv)
if (add_prop_list(optarg, propval, &props, B_TRUE))
goto errout;
+
+ /*
+ * If the user is creating a pool that doesn't support
+ * feature flags, don't enable any features.
+ */
+ if (zpool_name_to_prop(optarg) == ZPOOL_PROP_VERSION) {
+ char *end;
+ u_longlong_t ver;
+
+ ver = strtoull(propval, &end, 10);
+ if (*end == '\0' &&
+ ver < SPA_VERSION_FEATURES) {
+ enable_all_pool_feat = B_FALSE;
+ }
+ }
break;
case 'O':
if ((propval = strchr(optarg, '=')) == NULL) {
@@ -693,7 +740,6 @@ zpool_do_create(int argc, char **argv)
goto errout;
}
-
if (altroot != NULL && altroot[0] != '/') {
(void) fprintf(stderr, gettext("invalid alternate root '%s': "
"must be an absolute path\n"), altroot);
@@ -775,6 +821,27 @@ zpool_do_create(int argc, char **argv)
/*
* Hand off to libzfs.
*/
+ if (enable_all_pool_feat) {
+ int i;
+ for (i = 0; i < SPA_FEATURES; i++) {
+ char propname[MAXPATHLEN];
+ zfeature_info_t *feat = &spa_feature_table[i];
+
+ (void) snprintf(propname, sizeof (propname),
+ "feature@%s", feat->fi_uname);
+
+ /*
+ * Skip feature if user specified it manually
+ * on the command line.
+ */
+ if (nvlist_exists(props, propname))
+ continue;
+
+ if (add_prop_list(propname, ZFS_FEATURE_ENABLED,
+ &props, B_TRUE) != 0)
+ goto errout;
+ }
+ }
if (zpool_create(g_zfs, poolname,
nvroot, props, fsprops) == 0) {
zfs_handle_t *pool = zfs_open(g_zfs, poolname,
@@ -1106,6 +1173,10 @@ print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
(void) printf(gettext("newer version"));
break;
+ case VDEV_AUX_UNSUP_FEAT:
+ (void) printf(gettext("unsupported feature(s)"));
+ break;
+
case VDEV_AUX_SPARED:
verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID,
&cb.cb_guid) == 0);
@@ -1223,6 +1294,10 @@ print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth)
(void) printf(gettext("newer version"));
break;
+ case VDEV_AUX_UNSUP_FEAT:
+ (void) printf(gettext("unsupported feature(s)"));
+ break;
+
case VDEV_AUX_ERR_EXCEEDED:
(void) printf(gettext("too many errors"));
break;
@@ -1326,6 +1401,7 @@ show_import(nvlist_t *config)
const char *health;
uint_t vsc;
int namewidth;
+ char *comment;
verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
&name) == 0);
@@ -1342,9 +1418,9 @@ show_import(nvlist_t *config)
reason = zpool_import_status(config, &msgid);
- (void) printf(gettext(" pool: %s\n"), name);
- (void) printf(gettext(" id: %llu\n"), (u_longlong_t)guid);
- (void) printf(gettext(" state: %s"), health);
+ (void) printf(gettext(" pool: %s\n"), name);
+ (void) printf(gettext(" id: %llu\n"), (u_longlong_t)guid);
+ (void) printf(gettext(" state: %s"), health);
if (pool_state == POOL_STATE_DESTROYED)
(void) printf(gettext(" (DESTROYED)"));
(void) printf("\n");
@@ -1353,58 +1429,73 @@ show_import(nvlist_t *config)
case ZPOOL_STATUS_MISSING_DEV_R:
case ZPOOL_STATUS_MISSING_DEV_NR:
case ZPOOL_STATUS_BAD_GUID_SUM:
- (void) printf(gettext("status: One or more devices are missing "
- "from the system.\n"));
+ (void) printf(gettext(" status: One or more devices are "
+ "missing from the system.\n"));
break;
case ZPOOL_STATUS_CORRUPT_LABEL_R:
case ZPOOL_STATUS_CORRUPT_LABEL_NR:
- (void) printf(gettext("status: One or more devices contains "
+ (void) printf(gettext(" status: One or more devices contains "
"corrupted data.\n"));
break;
case ZPOOL_STATUS_CORRUPT_DATA:
- (void) printf(gettext("status: The pool data is corrupted.\n"));
+ (void) printf(
+ gettext(" status: The pool data is corrupted.\n"));
break;
case ZPOOL_STATUS_OFFLINE_DEV:
- (void) printf(gettext("status: One or more devices "
+ (void) printf(gettext(" status: One or more devices "
"are offlined.\n"));
break;
case ZPOOL_STATUS_CORRUPT_POOL:
- (void) printf(gettext("status: The pool metadata is "
+ (void) printf(gettext(" status: The pool metadata is "
"corrupted.\n"));
break;
case ZPOOL_STATUS_VERSION_OLDER:
- (void) printf(gettext("status: The pool is formatted using an "
+ (void) printf(gettext(" status: The pool is formatted using an "
"older on-disk version.\n"));
break;
case ZPOOL_STATUS_VERSION_NEWER:
- (void) printf(gettext("status: The pool is formatted using an "
+ (void) printf(gettext(" status: The pool is formatted using an "
"incompatible version.\n"));
break;
+ case ZPOOL_STATUS_UNSUP_FEAT_READ:
+ (void) printf(gettext("status: The pool uses the following "
+ "feature(s) not supported on this sytem:\n"));
+ zpool_print_unsup_feat(config);
+ break;
+
+ case ZPOOL_STATUS_UNSUP_FEAT_WRITE:
+ (void) printf(gettext("status: The pool can only be accessed "
+ "in read-only mode on this system. It\n\tcannot be "
+ "accessed in read-write mode because it uses the "
+ "following\n\tfeature(s) not supported on this system:\n"));
+ zpool_print_unsup_feat(config);
+ break;
+
case ZPOOL_STATUS_HOSTID_MISMATCH:
- (void) printf(gettext("status: The pool was last accessed by "
+ (void) printf(gettext(" status: The pool was last accessed by "
"another system.\n"));
break;
case ZPOOL_STATUS_FAULTED_DEV_R:
case ZPOOL_STATUS_FAULTED_DEV_NR:
- (void) printf(gettext("status: One or more devices are "
+ (void) printf(gettext(" status: One or more devices are "
"faulted.\n"));
break;
case ZPOOL_STATUS_BAD_LOG:
- (void) printf(gettext("status: An intent log record cannot be "
+ (void) printf(gettext(" status: An intent log record cannot be "
"read.\n"));
break;
case ZPOOL_STATUS_RESILVERING:
- (void) printf(gettext("status: One or more devices were being "
+ (void) printf(gettext(" status: One or more devices were being "
"resilvered.\n"));
break;
@@ -1420,43 +1511,61 @@ show_import(nvlist_t *config)
*/
if (vs->vs_state == VDEV_STATE_HEALTHY) {
if (reason == ZPOOL_STATUS_VERSION_OLDER)
- (void) printf(gettext("action: The pool can be "
+ (void) printf(gettext(" action: The pool can be "
"imported using its name or numeric identifier, "
"though\n\tsome features will not be available "
"without an explicit 'zpool upgrade'.\n"));
else if (reason == ZPOOL_STATUS_HOSTID_MISMATCH)
- (void) printf(gettext("action: The pool can be "
+ (void) printf(gettext(" action: The pool can be "
"imported using its name or numeric "
"identifier and\n\tthe '-f' flag.\n"));
else
- (void) printf(gettext("action: The pool can be "
+ (void) printf(gettext(" action: The pool can be "
"imported using its name or numeric "
"identifier.\n"));
} else if (vs->vs_state == VDEV_STATE_DEGRADED) {
- (void) printf(gettext("action: The pool can be imported "
+ (void) printf(gettext(" action: The pool can be imported "
"despite missing or damaged devices. The\n\tfault "
"tolerance of the pool may be compromised if imported.\n"));
} else {
switch (reason) {
case ZPOOL_STATUS_VERSION_NEWER:
- (void) printf(gettext("action: The pool cannot be "
+ (void) printf(gettext(" action: The pool cannot be "
"imported. Access the pool on a system running "
"newer\n\tsoftware, or recreate the pool from "
"backup.\n"));
break;
+ case ZPOOL_STATUS_UNSUP_FEAT_READ:
+ (void) printf(gettext("action: The pool cannot be "
+ "imported. Access the pool on a system that "
+ "supports\n\tthe required feature(s), or recreate "
+ "the pool from backup.\n"));
+ break;
+ case ZPOOL_STATUS_UNSUP_FEAT_WRITE:
+ (void) printf(gettext("action: The pool cannot be "
+ "imported in read-write mode. Import the pool "
+ "with\n"
+ "\t\"-o readonly=on\", access the pool on a system "
+ "that supports the\n\trequired feature(s), or "
+ "recreate the pool from backup.\n"));
+ break;
case ZPOOL_STATUS_MISSING_DEV_R:
case ZPOOL_STATUS_MISSING_DEV_NR:
case ZPOOL_STATUS_BAD_GUID_SUM:
- (void) printf(gettext("action: The pool cannot be "
+ (void) printf(gettext(" action: The pool cannot be "
"imported. Attach the missing\n\tdevices and try "
"again.\n"));
break;
default:
- (void) printf(gettext("action: The pool cannot be "
+ (void) printf(gettext(" action: The pool cannot be "
"imported due to damaged devices or data.\n"));
}
}
+ /* Print the comment attached to the pool. */
+ if (nvlist_lookup_string(config, ZPOOL_CONFIG_COMMENT, &comment) == 0)
+ (void) printf(gettext("comment: %s\n"), comment);
+
/*
* If the state is "closed" or "can't open", and the aux state
* is "corrupt data":
@@ -1474,10 +1583,10 @@ show_import(nvlist_t *config)
}
if (msgid != NULL)
- (void) printf(gettext(" see: http://www.sun.com/msg/%s\n"),
+ (void) printf(gettext(" see: http://illumos.org/msg/%s\n"),
msgid);
- (void) printf(gettext("config:\n\n"));
+ (void) printf(gettext(" config:\n\n"));
namewidth = max_width(NULL, nvroot, 0, 0);
if (namewidth < 10)
@@ -1515,9 +1624,9 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
ZPOOL_CONFIG_POOL_STATE, &state) == 0);
verify(nvlist_lookup_uint64(config,
ZPOOL_CONFIG_VERSION, &version) == 0);
- if (version > SPA_VERSION) {
+ if (!SPA_VERSION_IS_SUPPORTED(version)) {
(void) fprintf(stderr, gettext("cannot import '%s': pool "
- "is formatted using a newer ZFS version\n"), name);
+ "is formatted using an unsupported ZFS version\n"), name);
return (1);
} else if (state != POOL_STATE_EXPORTED &&
!(flags & ZFS_IMPORT_ANY_HOST)) {
@@ -1966,10 +2075,10 @@ error:
}
typedef struct iostat_cbdata {
- zpool_list_t *cb_list;
- int cb_verbose;
- int cb_iteration;
+ boolean_t cb_verbose;
int cb_namewidth;
+ int cb_iteration;
+ zpool_list_t *cb_list;
} iostat_cbdata_t;
static void
@@ -2078,10 +2187,15 @@ print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv,
return;
for (c = 0; c < children; c++) {
- uint64_t ishole = B_FALSE;
+ uint64_t ishole = B_FALSE, islog = B_FALSE;
- if (nvlist_lookup_uint64(newchild[c],
- ZPOOL_CONFIG_IS_HOLE, &ishole) == 0 && ishole)
+ (void) nvlist_lookup_uint64(newchild[c], ZPOOL_CONFIG_IS_HOLE,
+ &ishole);
+
+ (void) nvlist_lookup_uint64(newchild[c], ZPOOL_CONFIG_IS_LOG,
+ &islog);
+
+ if (ishole || islog)
continue;
vname = zpool_vdev_name(g_zfs, zhp, newchild[c], B_FALSE);
@@ -2091,6 +2205,31 @@ print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv,
}
/*
+ * Log device section
+ */
+
+ if (num_logs(newnv) > 0) {
+ (void) printf("%-*s - - - - - "
+ "-\n", cb->cb_namewidth, "logs");
+
+ for (c = 0; c < children; c++) {
+ uint64_t islog = B_FALSE;
+ (void) nvlist_lookup_uint64(newchild[c],
+ ZPOOL_CONFIG_IS_LOG, &islog);
+
+ if (islog) {
+ vname = zpool_vdev_name(g_zfs, zhp, newchild[c],
+ B_FALSE);
+ print_vdev_stats(zhp, vname, oldnv ?
+ oldchild[c] : NULL, newchild[c],
+ cb, depth + 2);
+ free(vname);
+ }
+ }
+
+ }
+
+ /*
* Include level 2 ARC devices in iostat output
*/
if (nvlist_lookup_nvlist_array(newnv, ZPOOL_CONFIG_L2CACHE,
@@ -2179,7 +2318,8 @@ get_namewidth(zpool_handle_t *zhp, void *data)
if (!cb->cb_verbose)
cb->cb_namewidth = strlen(zpool_get_name(zhp));
else
- cb->cb_namewidth = max_width(zhp, nvroot, 0, 0);
+ cb->cb_namewidth = max_width(zhp, nvroot, 0,
+ cb->cb_namewidth);
}
/*
@@ -2408,8 +2548,9 @@ zpool_do_iostat(int argc, char **argv)
}
typedef struct list_cbdata {
+ boolean_t cb_verbose;
+ int cb_namewidth;
boolean_t cb_scripted;
- boolean_t cb_first;
zprop_list_t *cb_proplist;
} list_cbdata_t;
@@ -2417,30 +2558,50 @@ typedef struct list_cbdata {
* Given a list of columns to display, output appropriate headers for each one.
*/
static void
-print_header(zprop_list_t *pl)
+print_header(list_cbdata_t *cb)
{
+ zprop_list_t *pl = cb->cb_proplist;
+ char headerbuf[ZPOOL_MAXPROPLEN];
const char *header;
boolean_t first = B_TRUE;
boolean_t right_justify;
+ size_t width = 0;
for (; pl != NULL; pl = pl->pl_next) {
- if (pl->pl_prop == ZPROP_INVAL)
- continue;
+ width = pl->pl_width;
+ if (first && cb->cb_verbose) {
+ /*
+ * Reset the width to accommodate the verbose listing
+ * of devices.
+ */
+ width = cb->cb_namewidth;
+ }
if (!first)
(void) printf(" ");
else
first = B_FALSE;
- header = zpool_prop_column_name(pl->pl_prop);
- right_justify = zpool_prop_align_right(pl->pl_prop);
+ right_justify = B_FALSE;
+ if (pl->pl_prop != ZPROP_INVAL) {
+ header = zpool_prop_column_name(pl->pl_prop);
+ right_justify = zpool_prop_align_right(pl->pl_prop);
+ } else {
+ int i;
+
+ for (i = 0; pl->pl_user_prop[i] != '\0'; i++)
+ headerbuf[i] = toupper(pl->pl_user_prop[i]);
+ headerbuf[i] = '\0';
+ header = headerbuf;
+ }
if (pl->pl_next == NULL && !right_justify)
(void) printf("%s", header);
else if (right_justify)
- (void) printf("%*s", pl->pl_width, header);
+ (void) printf("%*s", width, header);
else
- (void) printf("%-*s", pl->pl_width, header);
+ (void) printf("%-*s", width, header);
+
}
(void) printf("\n");
@@ -2451,17 +2612,28 @@ print_header(zprop_list_t *pl)
* to the described layout.
*/
static void
-print_pool(zpool_handle_t *zhp, zprop_list_t *pl, int scripted)
+print_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
{
+ zprop_list_t *pl = cb->cb_proplist;
boolean_t first = B_TRUE;
char property[ZPOOL_MAXPROPLEN];
char *propstr;
boolean_t right_justify;
- int width;
+ size_t width;
for (; pl != NULL; pl = pl->pl_next) {
+
+ width = pl->pl_width;
+ if (first && cb->cb_verbose) {
+ /*
+ * Reset the width to accommodate the verbose listing
+ * of devices.
+ */
+ width = cb->cb_namewidth;
+ }
+
if (!first) {
- if (scripted)
+ if (cb->cb_scripted)
(void) printf("\t");
else
(void) printf(" ");
@@ -2471,25 +2643,32 @@ print_pool(zpool_handle_t *zhp, zprop_list_t *pl, int scripted)
right_justify = B_FALSE;
if (pl->pl_prop != ZPROP_INVAL) {
- if (zpool_get_prop(zhp, pl->pl_prop, property,
+ if (pl->pl_prop == ZPOOL_PROP_EXPANDSZ &&
+ zpool_get_prop_int(zhp, pl->pl_prop, NULL) == 0)
+ propstr = "-";
+ else if (zpool_get_prop(zhp, pl->pl_prop, property,
sizeof (property), NULL) != 0)
propstr = "-";
else
propstr = property;
right_justify = zpool_prop_align_right(pl->pl_prop);
+ } else if ((zpool_prop_feature(pl->pl_user_prop) ||
+ zpool_prop_unsupported(pl->pl_user_prop)) &&
+ zpool_prop_get_feature(zhp, pl->pl_user_prop, property,
+ sizeof (property)) == 0) {
+ propstr = property;
} else {
propstr = "-";
}
- width = pl->pl_width;
/*
* If this is being called in scripted mode, or if this is the
* last column and it is left-justified, don't include a width
* format specifier.
*/
- if (scripted || (pl->pl_next == NULL && !right_justify))
+ if (cb->cb_scripted || (pl->pl_next == NULL && !right_justify))
(void) printf("%s", propstr);
else if (right_justify)
(void) printf("%*s", width, propstr);
@@ -2500,6 +2679,101 @@ print_pool(zpool_handle_t *zhp, zprop_list_t *pl, int scripted)
(void) printf("\n");
}
+static void
+print_one_column(zpool_prop_t prop, uint64_t value, boolean_t scripted)
+{
+ char propval[64];
+ boolean_t fixed;
+ size_t width = zprop_width(prop, &fixed, ZFS_TYPE_POOL);
+
+ zfs_nicenum(value, propval, sizeof (propval));
+
+ if (prop == ZPOOL_PROP_EXPANDSZ && value == 0)
+ (void) strlcpy(propval, "-", sizeof (propval));
+
+ if (scripted)
+ (void) printf("\t%s", propval);
+ else
+ (void) printf(" %*s", width, propval);
+}
+
+void
+print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
+ list_cbdata_t *cb, int depth)
+{
+ nvlist_t **child;
+ vdev_stat_t *vs;
+ uint_t c, children;
+ char *vname;
+ boolean_t scripted = cb->cb_scripted;
+
+ verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_VDEV_STATS,
+ (uint64_t **)&vs, &c) == 0);
+
+ if (name != NULL) {
+ if (scripted)
+ (void) printf("\t%s", name);
+ else if (strlen(name) + depth > cb->cb_namewidth)
+ (void) printf("%*s%s", depth, "", name);
+ else
+ (void) printf("%*s%s%*s", depth, "", name,
+ (int)(cb->cb_namewidth - strlen(name) - depth), "");
+
+ /* only toplevel vdevs have capacity stats */
+ if (vs->vs_space == 0) {
+ if (scripted)
+ (void) printf("\t-\t-\t-");
+ else
+ (void) printf(" - - -");
+ } else {
+ print_one_column(ZPOOL_PROP_SIZE, vs->vs_space,
+ scripted);
+ print_one_column(ZPOOL_PROP_CAPACITY, vs->vs_alloc,
+ scripted);
+ print_one_column(ZPOOL_PROP_FREE,
+ vs->vs_space - vs->vs_alloc, scripted);
+ }
+ print_one_column(ZPOOL_PROP_EXPANDSZ, vs->vs_esize,
+ scripted);
+ (void) printf("\n");
+ }
+
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
+ &child, &children) != 0)
+ return;
+
+ for (c = 0; c < children; c++) {
+ uint64_t ishole = B_FALSE;
+
+ if (nvlist_lookup_uint64(child[c],
+ ZPOOL_CONFIG_IS_HOLE, &ishole) == 0 && ishole)
+ continue;
+
+ vname = zpool_vdev_name(g_zfs, zhp, child[c], B_FALSE);
+ print_list_stats(zhp, vname, child[c], cb, depth + 2);
+ free(vname);
+ }
+
+ /*
+ * Include level 2 ARC devices in iostat output
+ */
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
+ &child, &children) != 0)
+ return;
+
+ if (children > 0) {
+ (void) printf("%-*s - - - - - "
+ "-\n", cb->cb_namewidth, "cache");
+ for (c = 0; c < children; c++) {
+ vname = zpool_vdev_name(g_zfs, zhp, child[c],
+ B_FALSE);
+ print_list_stats(zhp, vname, child[c], cb, depth + 2);
+ free(vname);
+ }
+ }
+}
+
+
/*
* Generic callback function to list a pool.
*/
@@ -2507,14 +2781,18 @@ int
list_callback(zpool_handle_t *zhp, void *data)
{
list_cbdata_t *cbp = data;
+ nvlist_t *config;
+ nvlist_t *nvroot;
- if (cbp->cb_first) {
- if (!cbp->cb_scripted)
- print_header(cbp->cb_proplist);
- cbp->cb_first = B_FALSE;
- }
+ config = zpool_get_config(zhp, NULL);
- print_pool(zhp, cbp->cb_proplist, cbp->cb_scripted);
+ print_pool(zhp, cbp);
+ if (!cbp->cb_verbose)
+ return (0);
+
+ verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
+ &nvroot) == 0);
+ print_list_stats(zhp, NULL, nvroot, cbp, 0);
return (0);
}
@@ -2538,12 +2816,15 @@ zpool_do_list(int argc, char **argv)
int ret;
list_cbdata_t cb = { 0 };
static char default_props[] =
- "name,size,allocated,free,capacity,dedupratio,health,altroot";
+ "name,size,allocated,free,expandsize,capacity,dedupratio,"
+ "health,altroot";
char *props = default_props;
unsigned long interval = 0, count = 0;
+ zpool_list_t *list;
+ boolean_t first = B_TRUE;
/* check options */
- while ((c = getopt(argc, argv, ":Ho:T:")) != -1) {
+ while ((c = getopt(argc, argv, ":Ho:T:v")) != -1) {
switch (c) {
case 'H':
cb.cb_scripted = B_TRUE;
@@ -2554,6 +2835,9 @@ zpool_do_list(int argc, char **argv)
case 'T':
get_timestamp_arg(*optarg);
break;
+ case 'v':
+ cb.cb_verbose = B_TRUE;
+ break;
case ':':
(void) fprintf(stderr, gettext("missing argument for "
"'%c' option\n"), optopt);
@@ -2574,21 +2858,32 @@ zpool_do_list(int argc, char **argv)
if (zprop_get_list(g_zfs, props, &cb.cb_proplist, ZFS_TYPE_POOL) != 0)
usage(B_FALSE);
- cb.cb_first = B_TRUE;
+ if ((list = pool_list_get(argc, argv, &cb.cb_proplist, &ret)) == NULL)
+ return (1);
+
+ if (argc == 0 && !cb.cb_scripted && pool_list_count(list) == 0) {
+ (void) printf(gettext("no pools available\n"));
+ zprop_free_list(cb.cb_proplist);
+ return (0);
+ }
for (;;) {
+ pool_list_update(list);
+
+ if (pool_list_count(list) == 0)
+ break;
+
+ cb.cb_namewidth = 0;
+ (void) pool_list_iter(list, B_FALSE, get_namewidth, &cb);
if (timestamp_fmt != NODATE)
print_timestamp(timestamp_fmt);
- ret = for_each_pool(argc, argv, B_TRUE, &cb.cb_proplist,
- list_callback, &cb);
-
- if (argc == 0 && cb.cb_first && !cb.cb_scripted) {
- (void) printf(gettext("no pools available\n"));
- zprop_free_list(cb.cb_proplist);
- return (0);
+ if (!cb.cb_scripted && (first || cb.cb_verbose)) {
+ print_header(&cb);
+ first = B_FALSE;
}
+ ret = pool_list_iter(list, B_TRUE, list_callback, &cb);
if (interval == 0)
break;
@@ -3162,6 +3457,82 @@ zpool_do_clear(int argc, char **argv)
return (ret);
}
+/*
+ * zpool reguid <pool>
+ */
+int
+zpool_do_reguid(int argc, char **argv)
+{
+ int c;
+ char *poolname;
+ zpool_handle_t *zhp;
+ int ret = 0;
+
+ /* check options */
+ while ((c = getopt(argc, argv, "")) != -1) {
+ switch (c) {
+ case '?':
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ usage(B_FALSE);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* get pool name and check number of arguments */
+ if (argc < 1) {
+ (void) fprintf(stderr, gettext("missing pool name\n"));
+ usage(B_FALSE);
+ }
+
+ if (argc > 1) {
+ (void) fprintf(stderr, gettext("too many arguments\n"));
+ usage(B_FALSE);
+ }
+
+ poolname = argv[0];
+ if ((zhp = zpool_open(g_zfs, poolname)) == NULL)
+ return (1);
+
+ ret = zpool_reguid(zhp);
+
+ zpool_close(zhp);
+ return (ret);
+}
+
+
+/*
+ * zpool reopen <pool>
+ *
+ * Reopen the pool so that the kernel can update the sizes of all vdevs.
+ *
+ * NOTE: This command is currently undocumented. If the command is ever
+ * exposed then the appropriate usage() messages will need to be made.
+ */
+int
+zpool_do_reopen(int argc, char **argv)
+{
+ int ret = 0;
+ zpool_handle_t *zhp;
+ char *pool;
+
+ argc--;
+ argv++;
+
+ if (argc != 1)
+ return (2);
+
+ pool = argv[0];
+ if ((zhp = zpool_open_canfail(g_zfs, pool)) == NULL)
+ return (1);
+
+ ret = zpool_reopen(zhp);
+ zpool_close(zhp);
+ return (ret);
+}
+
typedef struct scrub_cbdata {
int cb_type;
int cb_argc;
@@ -3249,7 +3620,7 @@ print_scan_status(pool_scan_stat_t *ps)
double fraction_done;
char processed_buf[7], examined_buf[7], total_buf[7], rate_buf[7];
- (void) printf(gettext(" scan: "));
+ (void) printf(gettext(" scan: "));
/* If there's never been a scan, there's not much to say. */
if (ps == NULL || ps->pss_func == POOL_SCAN_NONE ||
@@ -3433,14 +3804,20 @@ print_dedup_stats(nvlist_t *config)
/*
* If the pool was faulted then we may not have been able to
- * obtain the config. Otherwise, if have anything in the dedup
+ * obtain the config. Otherwise, if we have anything in the dedup
* table continue processing the stats.
*/
if (nvlist_lookup_uint64_array(config, ZPOOL_CONFIG_DDT_OBJ_STATS,
- (uint64_t **)&ddo, &c) != 0 || ddo->ddo_count == 0)
+ (uint64_t **)&ddo, &c) != 0)
return;
(void) printf("\n");
+ (void) printf(gettext(" dedup: "));
+ if (ddo->ddo_count == 0) {
+ (void) printf(gettext("no DDT entries\n"));
+ return;
+ }
+
(void) printf("DDT entries %llu, size %llu on disk, %llu in core\n",
(u_longlong_t)ddo->ddo_count,
(u_longlong_t)ddo->ddo_dspace,
@@ -3459,7 +3836,7 @@ print_dedup_stats(nvlist_t *config)
* pool: tank
* status: DEGRADED
* reason: One or more devices ...
- * see: http://www.sun.com/msg/ZFS-xxxx-01
+ * see: http://illumos.org/msg/ZFS-xxxx-01
* config:
* mirror DEGRADED
* c1t0d0 OK
@@ -3620,6 +3997,31 @@ status_callback(zpool_handle_t *zhp, void *data)
"backup.\n"));
break;
+ case ZPOOL_STATUS_UNSUP_FEAT_READ:
+ (void) printf(gettext("status: The pool cannot be accessed on "
+ "this system because it uses the\n\tfollowing feature(s) "
+ "not supported on this system:\n"));
+ zpool_print_unsup_feat(config);
+ (void) printf("\n");
+ (void) printf(gettext("action: Access the pool from a system "
+ "that supports the required feature(s),\n\tor restore the "
+ "pool from backup.\n"));
+ break;
+
+ case ZPOOL_STATUS_UNSUP_FEAT_WRITE:
+ (void) printf(gettext("status: The pool can only be accessed "
+ "in read-only mode on this system. It\n\tcannot be "
+ "accessed in read-write mode because it uses the "
+ "following\n\tfeature(s) not supported on this system:\n"));
+ zpool_print_unsup_feat(config);
+ (void) printf("\n");
+ (void) printf(gettext("action: The pool cannot be accessed in "
+ "read-write mode. Import the pool with\n"
+ "\t\"-o readonly=on\", access the pool from a system that "
+ "supports the\n\trequired feature(s), or restore the "
+ "pool from backup.\n"));
+ break;
+
case ZPOOL_STATUS_FAULTED_DEV_R:
(void) printf(gettext("status: One or more devices are "
"faulted in response to persistent errors.\n\tSufficient "
@@ -3667,7 +4069,7 @@ status_callback(zpool_handle_t *zhp, void *data)
}
if (msgid != NULL)
- (void) printf(gettext(" see: http://www.sun.com/msg/%s\n"),
+ (void) printf(gettext(" see: http://illumos.org/msg/%s\n"),
msgid);
if (config != NULL) {
@@ -3844,7 +4246,8 @@ upgrade_cb(zpool_handle_t *zhp, void *arg)
verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
&version) == 0);
- if (!cbp->cb_newer && version < SPA_VERSION) {
+ if (!cbp->cb_newer && SPA_VERSION_IS_SUPPORTED(version) &&
+ version != SPA_VERSION) {
if (!cbp->cb_all) {
if (cbp->cb_first) {
(void) printf(gettext("The following pools are "
@@ -3867,13 +4270,14 @@ upgrade_cb(zpool_handle_t *zhp, void *arg)
"'%s'\n\n"), zpool_get_name(zhp));
}
}
- } else if (cbp->cb_newer && version > SPA_VERSION) {
+ } else if (cbp->cb_newer && !SPA_VERSION_IS_SUPPORTED(version)) {
assert(!cbp->cb_all);
if (cbp->cb_first) {
(void) printf(gettext("The following pools are "
- "formatted using a newer software version and\n"
- "cannot be accessed on the current system.\n\n"));
+ "formatted using an unsupported software version "
+ "and\ncannot be accessed on the current "
+ "system.\n\n"));
(void) printf(gettext("VER POOL\n"));
(void) printf(gettext("--- ------------\n"));
cbp->cb_first = B_FALSE;
@@ -3957,8 +4361,8 @@ zpool_do_upgrade(int argc, char **argv)
break;
case 'V':
cb.cb_version = strtoll(optarg, &end, 10);
- if (*end != '\0' || cb.cb_version > SPA_VERSION ||
- cb.cb_version < SPA_VERSION_1) {
+ if (*end != '\0' ||
+ !SPA_VERSION_IS_SUPPORTED(cb.cb_version)) {
(void) fprintf(stderr,
gettext("invalid version '%s'\n"), optarg);
usage(B_FALSE);
@@ -4003,8 +4407,8 @@ zpool_do_upgrade(int argc, char **argv)
}
}
- (void) printf(gettext("This system is currently running "
- "ZFS pool version %llu.\n\n"), SPA_VERSION);
+ (void) printf(gettext("This system supports ZFS pool feature "
+ "flags.\n\n"));
cb.cb_first = B_TRUE;
if (showversions) {
(void) printf(gettext("The following versions are "
@@ -4255,13 +4659,26 @@ get_callback(zpool_handle_t *zhp, void *data)
pl == cbp->cb_proplist)
continue;
- if (zpool_get_prop(zhp, pl->pl_prop,
- value, sizeof (value), &srctype) != 0)
- continue;
+ if (pl->pl_prop == ZPROP_INVAL &&
+ (zpool_prop_feature(pl->pl_user_prop) ||
+ zpool_prop_unsupported(pl->pl_user_prop))) {
+ srctype = ZPROP_SRC_LOCAL;
- zprop_print_one_property(zpool_get_name(zhp), cbp,
- zpool_prop_to_name(pl->pl_prop), value, srctype, NULL,
- NULL);
+ if (zpool_prop_get_feature(zhp, pl->pl_user_prop,
+ value, sizeof (value)) == 0) {
+ zprop_print_one_property(zpool_get_name(zhp),
+ cbp, pl->pl_user_prop, value, srctype,
+ NULL, NULL);
+ }
+ } else {
+ if (zpool_get_prop(zhp, pl->pl_prop, value,
+ sizeof (value), &srctype) != 0)
+ continue;
+
+ zprop_print_one_property(zpool_get_name(zhp), cbp,
+ zpool_prop_to_name(pl->pl_prop), value, srctype,
+ NULL, NULL);
+ }
}
return (0);
}
@@ -4273,8 +4690,11 @@ zpool_do_get(int argc, char **argv)
zprop_list_t fake_name = { 0 };
int ret;
- if (argc < 3)
+ if (argc < 2) {
+ (void) fprintf(stderr, gettext("missing property "
+ "argument\n"));
usage(B_FALSE);
+ }
cb.cb_first = B_TRUE;
cb.cb_sources = ZPROP_SRC_ALL;
@@ -4284,7 +4704,7 @@ zpool_do_get(int argc, char **argv)
cb.cb_columns[3] = GET_COL_SOURCE;
cb.cb_type = ZFS_TYPE_POOL;
- if (zprop_get_list(g_zfs, argv[1], &cb.cb_proplist,
+ if (zprop_get_list(g_zfs, argv[1], &cb.cb_proplist,
ZFS_TYPE_POOL) != 0)
usage(B_FALSE);
diff --git a/cmd/ztest/ztest.c b/cmd/ztest/ztest.c
index b2d81b558882..d9bcb0408a0b 100644
--- a/cmd/ztest/ztest.c
+++ b/cmd/ztest/ztest.c
@@ -20,6 +20,8 @@
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -49,7 +51,9 @@
* At random times, the child self-immolates with a SIGKILL.
* This is the software equivalent of pulling the power cord.
* The parent then runs the test again, using the existing
- * storage pool, as many times as desired.
+ * storage pool, as many times as desired. If backwards compatability
+ * testing is enabled ztest will sometimes run the "older" version
+ * of ztest after a SIGKILL.
*
* (6) To verify that we don't have future leaks or temporal incursions,
* many of the functional tests record the transaction group number
@@ -66,9 +70,15 @@
* You can ask more more vdevs [-v], datasets [-d], or threads [-t]
* to increase the pool capacity, fanout, and overall stress level.
*
- * The -N(okill) option will suppress kills, so each child runs to completion.
- * This can be useful when you're trying to distinguish temporal incursions
- * from plain old race conditions.
+ * Use the -k option to set the desired frequency of kills.
+ *
+ * When ztest invokes itself it passes all relevant information through a
+ * temporary file which is mmap-ed in the child process. This allows shared
+ * memory to survive the exec syscall. The ztest_shared_hdr_t struct is always
+ * stored at offset 0 of this file and contains information on the size and
+ * number of shared structures in the file. The information stored in this file
+ * must remain backwards compatible with older versions of ztest so that
+ * ztest can invoke them during backwards compatibility testing (-B).
*/
#include <sys/zfs_context.h>
@@ -96,6 +106,7 @@
#include <sys/dsl_scan.h>
#include <sys/zio_checksum.h>
#include <sys/refcount.h>
+#include <sys/zfeature.h>
#include <stdio.h>
#include <stdio_ext.h>
#include <stdlib.h>
@@ -108,28 +119,82 @@
#include <sys/fs/zfs.h>
#include <libnvpair.h>
-static char cmdname[] = "ztest";
-static char *zopt_pool = cmdname;
-
-static uint64_t zopt_vdevs = 5;
-static uint64_t zopt_vdevtime;
-static int zopt_ashift = SPA_MINBLOCKSHIFT;
-static int zopt_mirrors = 2;
-static int zopt_raidz = 4;
-static int zopt_raidz_parity = 1;
-static size_t zopt_vdev_size = SPA_MINDEVSIZE;
-static int zopt_datasets = 7;
-static int zopt_threads = 23;
-static uint64_t zopt_passtime = 60; /* 60 seconds */
-static uint64_t zopt_killrate = 70; /* 70% kill rate */
-static int zopt_verbose = 0;
-static int zopt_init = 1;
-static char *zopt_dir = "/tmp";
-static uint64_t zopt_time = 300; /* 5 minutes */
-static uint64_t zopt_maxloops = 50; /* max loops during spa_freeze() */
+#define ZTEST_FD_DATA 3
+#define ZTEST_FD_RAND 4
+
+typedef struct ztest_shared_hdr {
+ uint64_t zh_hdr_size;
+ uint64_t zh_opts_size;
+ uint64_t zh_size;
+ uint64_t zh_stats_size;
+ uint64_t zh_stats_count;
+ uint64_t zh_ds_size;
+ uint64_t zh_ds_count;
+} ztest_shared_hdr_t;
+
+static ztest_shared_hdr_t *ztest_shared_hdr;
+
+typedef struct ztest_shared_opts {
+ char zo_pool[MAXNAMELEN];
+ char zo_dir[MAXNAMELEN];
+ char zo_alt_ztest[MAXNAMELEN];
+ char zo_alt_libpath[MAXNAMELEN];
+ uint64_t zo_vdevs;
+ uint64_t zo_vdevtime;
+ size_t zo_vdev_size;
+ int zo_ashift;
+ int zo_mirrors;
+ int zo_raidz;
+ int zo_raidz_parity;
+ int zo_datasets;
+ int zo_threads;
+ uint64_t zo_passtime;
+ uint64_t zo_killrate;
+ int zo_verbose;
+ int zo_init;
+ uint64_t zo_time;
+ uint64_t zo_maxloops;
+ uint64_t zo_metaslab_gang_bang;
+} ztest_shared_opts_t;
+
+static const ztest_shared_opts_t ztest_opts_defaults = {
+ .zo_pool = { 'z', 't', 'e', 's', 't', '\0' },
+ .zo_dir = { '/', 't', 'm', 'p', '\0' },
+ .zo_alt_ztest = { '\0' },
+ .zo_alt_libpath = { '\0' },
+ .zo_vdevs = 5,
+ .zo_ashift = SPA_MINBLOCKSHIFT,
+ .zo_mirrors = 2,
+ .zo_raidz = 4,
+ .zo_raidz_parity = 1,
+ .zo_vdev_size = SPA_MINDEVSIZE,
+ .zo_datasets = 7,
+ .zo_threads = 23,
+ .zo_passtime = 60, /* 60 seconds */
+ .zo_killrate = 70, /* 70% kill rate */
+ .zo_verbose = 0,
+ .zo_init = 1,
+ .zo_time = 300, /* 5 minutes */
+ .zo_maxloops = 50, /* max loops during spa_freeze() */
+ .zo_metaslab_gang_bang = 32 << 10
+};
+
+extern uint64_t metaslab_gang_bang;
+extern uint64_t metaslab_df_alloc_threshold;
+
+static ztest_shared_opts_t *ztest_shared_opts;
+static ztest_shared_opts_t ztest_opts;
+
+typedef struct ztest_shared_ds {
+ uint64_t zd_seq;
+} ztest_shared_ds_t;
+
+static ztest_shared_ds_t *ztest_shared_ds;
+#define ZTEST_GET_SHARED_DS(d) (&ztest_shared_ds[d])
#define BT_MAGIC 0x123456789abcdefULL
-#define MAXFAULTS() (MAX(zs->zs_mirrors, 1) * (zopt_raidz_parity + 1) - 1)
+#define MAXFAULTS() \
+ (MAX(zs->zs_mirrors, 1) * (ztest_opts.zo_raidz_parity + 1) - 1)
enum ztest_io_type {
ZTEST_IO_WRITE_TAG,
@@ -201,9 +266,10 @@ typedef struct ztest_od {
* Per-dataset state.
*/
typedef struct ztest_ds {
+ ztest_shared_ds_t *zd_shared;
objset_t *zd_os;
+ rwlock_t zd_zilog_lock;
zilog_t *zd_zilog;
- uint64_t zd_seq;
ztest_od_t *zd_od; /* debugging aid */
char zd_name[MAXNAMELEN];
mutex_t zd_dirobj_lock;
@@ -220,11 +286,17 @@ typedef struct ztest_info {
ztest_func_t *zi_func; /* test function */
uint64_t zi_iters; /* iterations per execution */
uint64_t *zi_interval; /* execute every <interval> seconds */
- uint64_t zi_call_count; /* per-pass count */
- uint64_t zi_call_time; /* per-pass time */
- uint64_t zi_call_next; /* next time to call this function */
} ztest_info_t;
+typedef struct ztest_shared_callstate {
+ uint64_t zc_count; /* per-pass count */
+ uint64_t zc_time; /* per-pass time */
+ uint64_t zc_next; /* next time to call this function */
+} ztest_shared_callstate_t;
+
+static ztest_shared_callstate_t *ztest_shared_callstate;
+#define ZTEST_GET_SHARED_CALLSTATE(c) (&ztest_shared_callstate[c])
+
/*
* Note: these aren't static because we want dladdr() to work.
*/
@@ -235,6 +307,7 @@ ztest_func_t ztest_dmu_commit_callbacks;
ztest_func_t ztest_zap;
ztest_func_t ztest_zap_parallel;
ztest_func_t ztest_zil_commit;
+ztest_func_t ztest_zil_remount;
ztest_func_t ztest_dmu_read_write_zcopy;
ztest_func_t ztest_dmu_objset_create_destroy;
ztest_func_t ztest_dmu_prealloc;
@@ -254,6 +327,7 @@ ztest_func_t ztest_vdev_LUN_growth;
ztest_func_t ztest_vdev_add_remove;
ztest_func_t ztest_vdev_aux_add_remove;
ztest_func_t ztest_split_pool;
+ztest_func_t ztest_reguid;
uint64_t zopt_always = 0ULL * NANOSEC; /* all the time */
uint64_t zopt_incessant = 1ULL * NANOSEC / 10; /* every 1/10 second */
@@ -270,6 +344,7 @@ ztest_info_t ztest_info[] = {
{ ztest_zap_parallel, 100, &zopt_always },
{ ztest_split_pool, 1, &zopt_always },
{ ztest_zil_commit, 1, &zopt_incessant },
+ { ztest_zil_remount, 1, &zopt_sometimes },
{ ztest_dmu_read_write_zcopy, 1, &zopt_often },
{ ztest_dmu_objset_create_destroy, 1, &zopt_often },
{ ztest_dsl_prop_get_set, 1, &zopt_often },
@@ -283,13 +358,16 @@ ztest_info_t ztest_info[] = {
{ ztest_fault_inject, 1, &zopt_sometimes },
{ ztest_ddt_repair, 1, &zopt_sometimes },
{ ztest_dmu_snapshot_hold, 1, &zopt_sometimes },
+ { ztest_reguid, 1, &zopt_sometimes },
{ ztest_spa_rename, 1, &zopt_rarely },
{ ztest_scrub, 1, &zopt_rarely },
{ ztest_dsl_dataset_promote_busy, 1, &zopt_rarely },
{ ztest_vdev_attach_detach, 1, &zopt_rarely },
{ ztest_vdev_LUN_growth, 1, &zopt_rarely },
- { ztest_vdev_add_remove, 1, &zopt_vdevtime },
- { ztest_vdev_aux_add_remove, 1, &zopt_vdevtime },
+ { ztest_vdev_add_remove, 1,
+ &ztest_opts.zo_vdevtime },
+ { ztest_vdev_aux_add_remove, 1,
+ &ztest_opts.zo_vdevtime },
};
#define ZTEST_FUNCS (sizeof (ztest_info) / sizeof (ztest_info_t))
@@ -307,8 +385,7 @@ typedef struct ztest_cb_list {
* Stuff we need to share writably between parent and child.
*/
typedef struct ztest_shared {
- char *zs_pool;
- spa_t *zs_spa;
+ boolean_t zs_do_init;
hrtime_t zs_proc_start;
hrtime_t zs_proc_stop;
hrtime_t zs_thread_start;
@@ -319,12 +396,11 @@ typedef struct ztest_shared {
uint64_t zs_vdev_aux;
uint64_t zs_alloc;
uint64_t zs_space;
- mutex_t zs_vdev_lock;
- rwlock_t zs_name_lock;
- ztest_info_t zs_info[ZTEST_FUNCS];
uint64_t zs_splits;
uint64_t zs_mirrors;
- ztest_ds_t zs_zd[];
+ uint64_t zs_metaslab_sz;
+ uint64_t zs_metaslab_df_alloc_threshold;
+ uint64_t zs_guid;
} ztest_shared_t;
#define ID_PARALLEL -1ULL
@@ -332,20 +408,19 @@ typedef struct ztest_shared {
static char ztest_dev_template[] = "%s/%s.%llua";
static char ztest_aux_template[] = "%s/%s.%s.%llu";
ztest_shared_t *ztest_shared;
-uint64_t *ztest_seq;
-static int ztest_random_fd;
-static int ztest_dump_core = 1;
+static spa_t *ztest_spa = NULL;
+static ztest_ds_t *ztest_ds;
+static mutex_t ztest_vdev_lock;
+static rwlock_t ztest_name_lock;
+
+static boolean_t ztest_dump_core = B_TRUE;
static boolean_t ztest_exiting;
/* Global commit callback list */
static ztest_cb_list_t zcl;
-extern uint64_t metaslab_gang_bang;
-extern uint64_t metaslab_df_alloc_threshold;
-static uint64_t metaslab_sz;
-
enum ztest_object {
ZTEST_META_DNODE = 0,
ZTEST_DIROBJ,
@@ -458,12 +533,14 @@ nicenumtoull(const char *buf)
static void
usage(boolean_t requested)
{
+ const ztest_shared_opts_t *zo = &ztest_opts_defaults;
+
char nice_vdev_size[10];
char nice_gang_bang[10];
FILE *fp = requested ? stdout : stderr;
- nicenum(zopt_vdev_size, nice_vdev_size);
- nicenum(metaslab_gang_bang, nice_gang_bang);
+ nicenum(zo->zo_vdev_size, nice_vdev_size);
+ nicenum(zo->zo_metaslab_gang_bang, nice_gang_bang);
(void) fprintf(fp, "Usage: %s\n"
"\t[-v vdevs (default: %llu)]\n"
@@ -484,39 +561,43 @@ usage(boolean_t requested)
"\t[-T time (default: %llu sec)] total run time\n"
"\t[-F freezeloops (default: %llu)] max loops in spa_freeze()\n"
"\t[-P passtime (default: %llu sec)] time per pass\n"
+ "\t[-B alt_ztest (default: <none>)] alternate ztest path\n"
"\t[-h] (print help)\n"
"",
- cmdname,
- (u_longlong_t)zopt_vdevs, /* -v */
+ zo->zo_pool,
+ (u_longlong_t)zo->zo_vdevs, /* -v */
nice_vdev_size, /* -s */
- zopt_ashift, /* -a */
- zopt_mirrors, /* -m */
- zopt_raidz, /* -r */
- zopt_raidz_parity, /* -R */
- zopt_datasets, /* -d */
- zopt_threads, /* -t */
+ zo->zo_ashift, /* -a */
+ zo->zo_mirrors, /* -m */
+ zo->zo_raidz, /* -r */
+ zo->zo_raidz_parity, /* -R */
+ zo->zo_datasets, /* -d */
+ zo->zo_threads, /* -t */
nice_gang_bang, /* -g */
- zopt_init, /* -i */
- (u_longlong_t)zopt_killrate, /* -k */
- zopt_pool, /* -p */
- zopt_dir, /* -f */
- (u_longlong_t)zopt_time, /* -T */
- (u_longlong_t)zopt_maxloops, /* -F */
- (u_longlong_t)zopt_passtime); /* -P */
+ zo->zo_init, /* -i */
+ (u_longlong_t)zo->zo_killrate, /* -k */
+ zo->zo_pool, /* -p */
+ zo->zo_dir, /* -f */
+ (u_longlong_t)zo->zo_time, /* -T */
+ (u_longlong_t)zo->zo_maxloops, /* -F */
+ (u_longlong_t)zo->zo_passtime);
exit(requested ? 0 : 1);
}
static void
process_options(int argc, char **argv)
{
+ char *path;
+ ztest_shared_opts_t *zo = &ztest_opts;
+
int opt;
uint64_t value;
+ char altdir[MAXNAMELEN] = { 0 };
- /* By default, test gang blocks for blocks 32K and greater */
- metaslab_gang_bang = 32 << 10;
+ bcopy(&ztest_opts_defaults, zo, sizeof (*zo));
while ((opt = getopt(argc, argv,
- "v:s:a:m:r:R:d:t:g:i:k:p:f:VET:P:hF:")) != EOF) {
+ "v:s:a:m:r:R:d:t:g:i:k:p:f:VET:P:hF:B:")) != EOF) {
value = 0;
switch (opt) {
case 'v':
@@ -537,58 +618,71 @@ process_options(int argc, char **argv)
}
switch (opt) {
case 'v':
- zopt_vdevs = value;
+ zo->zo_vdevs = value;
break;
case 's':
- zopt_vdev_size = MAX(SPA_MINDEVSIZE, value);
+ zo->zo_vdev_size = MAX(SPA_MINDEVSIZE, value);
break;
case 'a':
- zopt_ashift = value;
+ zo->zo_ashift = value;
break;
case 'm':
- zopt_mirrors = value;
+ zo->zo_mirrors = value;
break;
case 'r':
- zopt_raidz = MAX(1, value);
+ zo->zo_raidz = MAX(1, value);
break;
case 'R':
- zopt_raidz_parity = MIN(MAX(value, 1), 3);
+ zo->zo_raidz_parity = MIN(MAX(value, 1), 3);
break;
case 'd':
- zopt_datasets = MAX(1, value);
+ zo->zo_datasets = MAX(1, value);
break;
case 't':
- zopt_threads = MAX(1, value);
+ zo->zo_threads = MAX(1, value);
break;
case 'g':
- metaslab_gang_bang = MAX(SPA_MINBLOCKSIZE << 1, value);
+ zo->zo_metaslab_gang_bang = MAX(SPA_MINBLOCKSIZE << 1,
+ value);
break;
case 'i':
- zopt_init = value;
+ zo->zo_init = value;
break;
case 'k':
- zopt_killrate = value;
+ zo->zo_killrate = value;
break;
case 'p':
- zopt_pool = strdup(optarg);
+ (void) strlcpy(zo->zo_pool, optarg,
+ sizeof (zo->zo_pool));
break;
case 'f':
- zopt_dir = strdup(optarg);
+ path = realpath(optarg, NULL);
+ if (path == NULL) {
+ (void) fprintf(stderr, "error: %s: %s\n",
+ optarg, strerror(errno));
+ usage(B_FALSE);
+ } else {
+ (void) strlcpy(zo->zo_dir, path,
+ sizeof (zo->zo_dir));
+ }
break;
case 'V':
- zopt_verbose++;
+ zo->zo_verbose++;
break;
case 'E':
- zopt_init = 0;
+ zo->zo_init = 0;
break;
case 'T':
- zopt_time = value;
+ zo->zo_time = value;
break;
case 'P':
- zopt_passtime = MAX(1, value);
+ zo->zo_passtime = MAX(1, value);
break;
case 'F':
- zopt_maxloops = MAX(1, value);
+ zo->zo_maxloops = MAX(1, value);
+ break;
+ case 'B':
+ (void) strlcpy(altdir, optarg, sizeof (altdir));
break;
case 'h':
usage(B_TRUE);
@@ -600,17 +694,59 @@ process_options(int argc, char **argv)
}
}
- zopt_raidz_parity = MIN(zopt_raidz_parity, zopt_raidz - 1);
+ zo->zo_raidz_parity = MIN(zo->zo_raidz_parity, zo->zo_raidz - 1);
- zopt_vdevtime = (zopt_vdevs > 0 ? zopt_time * NANOSEC / zopt_vdevs :
+ zo->zo_vdevtime =
+ (zo->zo_vdevs > 0 ? zo->zo_time * NANOSEC / zo->zo_vdevs :
UINT64_MAX >> 2);
+
+ if (strlen(altdir) > 0) {
+ char cmd[MAXNAMELEN];
+ char realaltdir[MAXNAMELEN];
+ char *bin;
+ char *ztest;
+ char *isa;
+ int isalen;
+
+ (void) realpath(getexecname(), cmd);
+ if (0 != access(altdir, F_OK)) {
+ ztest_dump_core = B_FALSE;
+ fatal(B_TRUE, "invalid alternate ztest path: %s",
+ altdir);
+ }
+ VERIFY(NULL != realpath(altdir, realaltdir));
+
+ /*
+ * 'cmd' should be of the form "<anything>/usr/bin/<isa>/ztest".
+ * We want to extract <isa> to determine if we should use
+ * 32 or 64 bit binaries.
+ */
+ bin = strstr(cmd, "/usr/bin/");
+ ztest = strstr(bin, "/ztest");
+ isa = bin + 9;
+ isalen = ztest - isa;
+ (void) snprintf(zo->zo_alt_ztest, sizeof (zo->zo_alt_ztest),
+ "%s/usr/bin/%.*s/ztest", realaltdir, isalen, isa);
+ (void) snprintf(zo->zo_alt_libpath, sizeof (zo->zo_alt_libpath),
+ "%s/usr/lib/%.*s", realaltdir, isalen, isa);
+
+ if (0 != access(zo->zo_alt_ztest, X_OK)) {
+ ztest_dump_core = B_FALSE;
+ fatal(B_TRUE, "invalid alternate ztest: %s",
+ zo->zo_alt_ztest);
+ } else if (0 != access(zo->zo_alt_libpath, X_OK)) {
+ ztest_dump_core = B_FALSE;
+ fatal(B_TRUE, "invalid alternate lib directory %s",
+ zo->zo_alt_libpath);
+ }
+ }
}
static void
ztest_kill(ztest_shared_t *zs)
{
- zs->zs_alloc = metaslab_class_get_alloc(spa_normal_class(zs->zs_spa));
- zs->zs_space = metaslab_class_get_space(spa_normal_class(zs->zs_spa));
+ zs->zs_alloc = metaslab_class_get_alloc(spa_normal_class(ztest_spa));
+ zs->zs_space = metaslab_class_get_space(spa_normal_class(ztest_spa));
(void) kill(getpid(), SIGKILL);
}
@@ -622,7 +758,7 @@ ztest_random(uint64_t range)
if (range == 0)
return (0);
- if (read(ztest_random_fd, &r, sizeof (r)) != sizeof (r))
+ if (read(ZTEST_FD_RAND, &r, sizeof (r)) != sizeof (r))
fatal(1, "short read from /dev/urandom");
return (r % range);
@@ -638,9 +774,9 @@ ztest_record_enospc(const char *s)
static uint64_t
ztest_get_ashift(void)
{
- if (zopt_ashift == 0)
+ if (ztest_opts.zo_ashift == 0)
return (SPA_MINBLOCKSHIFT + ztest_random(3));
- return (zopt_ashift);
+ return (ztest_opts.zo_ashift);
}
static nvlist_t *
@@ -658,12 +794,14 @@ make_vdev_file(char *path, char *aux, size_t size, uint64_t ashift)
if (aux != NULL) {
vdev = ztest_shared->zs_vdev_aux;
- (void) sprintf(path, ztest_aux_template,
- zopt_dir, zopt_pool, aux, vdev);
+ (void) snprintf(path, sizeof (pathbuf),
+ ztest_aux_template, ztest_opts.zo_dir,
+ ztest_opts.zo_pool, aux, vdev);
} else {
vdev = ztest_shared->zs_vdev_next_leaf++;
- (void) sprintf(path, ztest_dev_template,
- zopt_dir, zopt_pool, vdev);
+ (void) snprintf(path, sizeof (pathbuf),
+ ztest_dev_template, ztest_opts.zo_dir,
+ ztest_opts.zo_pool, vdev);
}
}
@@ -701,7 +839,7 @@ make_vdev_raidz(char *path, char *aux, size_t size, uint64_t ashift, int r)
VERIFY(nvlist_add_string(raidz, ZPOOL_CONFIG_TYPE,
VDEV_TYPE_RAIDZ) == 0);
VERIFY(nvlist_add_uint64(raidz, ZPOOL_CONFIG_NPARITY,
- zopt_raidz_parity) == 0);
+ ztest_opts.zo_raidz_parity) == 0);
VERIFY(nvlist_add_nvlist_array(raidz, ZPOOL_CONFIG_CHILDREN,
child, r) == 0);
@@ -839,7 +977,7 @@ ztest_dsl_prop_set_uint64(char *osname, zfs_prop_t prop, uint64_t value,
VERIFY3U(dsl_prop_get(osname, propname, sizeof (curval),
1, &curval, setpoint), ==, 0);
- if (zopt_verbose >= 6) {
+ if (ztest_opts.zo_verbose >= 6) {
VERIFY(zfs_prop_index_to_string(prop, curval, &valname) == 0);
(void) printf("%s %s = %s at '%s'\n",
osname, propname, valname, setpoint);
@@ -849,9 +987,9 @@ ztest_dsl_prop_set_uint64(char *osname, zfs_prop_t prop, uint64_t value,
}
static int
-ztest_spa_prop_set_uint64(ztest_shared_t *zs, zpool_prop_t prop, uint64_t value)
+ztest_spa_prop_set_uint64(zpool_prop_t prop, uint64_t value)
{
- spa_t *spa = zs->zs_spa;
+ spa_t *spa = ztest_spa;
nvlist_t *props = NULL;
int error;
@@ -973,13 +1111,17 @@ ztest_range_unlock(rl_t *rl)
}
static void
-ztest_zd_init(ztest_ds_t *zd, objset_t *os)
+ztest_zd_init(ztest_ds_t *zd, ztest_shared_ds_t *szd, objset_t *os)
{
zd->zd_os = os;
zd->zd_zilog = dmu_objset_zil(os);
- zd->zd_seq = 0;
+ zd->zd_shared = szd;
dmu_objset_name(os, zd->zd_name);
+ if (zd->zd_shared != NULL)
+ zd->zd_shared->zd_seq = 0;
+
+ VERIFY(rwlock_init(&zd->zd_zilog_lock, USYNC_THREAD, NULL) == 0);
VERIFY(_mutex_init(&zd->zd_dirobj_lock, USYNC_THREAD, NULL) == 0);
for (int l = 0; l < ZTEST_OBJECT_LOCKS; l++)
@@ -1959,6 +2101,8 @@ ztest_io(ztest_ds_t *zd, uint64_t object, uint64_t offset)
if (ztest_random(2) == 0)
io_type = ZTEST_IO_WRITE_TAG;
+ (void) rw_rdlock(&zd->zd_zilog_lock);
+
switch (io_type) {
case ZTEST_IO_WRITE_TAG:
@@ -1994,6 +2138,8 @@ ztest_io(ztest_ds_t *zd, uint64_t object, uint64_t offset)
break;
}
+ (void) rw_unlock(&zd->zd_zilog_lock);
+
umem_free(data, blocksize);
}
@@ -2048,6 +2194,8 @@ ztest_zil_commit(ztest_ds_t *zd, uint64_t id)
{
zilog_t *zilog = zd->zd_zilog;
+ (void) rw_rdlock(&zd->zd_zilog_lock);
+
zil_commit(zilog, ztest_random(ZTEST_OBJECTS));
/*
@@ -2056,9 +2204,35 @@ ztest_zil_commit(ztest_ds_t *zd, uint64_t id)
* will verify that the log really does contain this record.
*/
mutex_enter(&zilog->zl_lock);
- ASSERT(zd->zd_seq <= zilog->zl_commit_lr_seq);
- zd->zd_seq = zilog->zl_commit_lr_seq;
+ ASSERT(zd->zd_shared != NULL);
+ ASSERT3U(zd->zd_shared->zd_seq, <=, zilog->zl_commit_lr_seq);
+ zd->zd_shared->zd_seq = zilog->zl_commit_lr_seq;
mutex_exit(&zilog->zl_lock);
+
+ (void) rw_unlock(&zd->zd_zilog_lock);
+}
+
+/*
+ * This function is designed to simulate the operations that occur during a
+ * mount/unmount operation. We hold the dataset across these operations in an
+ * attempt to expose any implicit assumptions about ZIL management.
+ */
+/* ARGSUSED */
+void
+ztest_zil_remount(ztest_ds_t *zd, uint64_t id)
+{
+ objset_t *os = zd->zd_os;
+
+ (void) rw_wrlock(&zd->zd_zilog_lock);
+
+ /* zfsvfs_teardown() */
+ zil_close(zd->zd_zilog);
+
+ /* zfsvfs_setup() */
+ VERIFY(zil_open(os, ztest_get_data) == zd->zd_zilog);
+ zil_replay(os, zd, ztest_replay_vector);
+
+ (void) rw_unlock(&zd->zd_zilog_lock);
}
/*
@@ -2069,7 +2243,7 @@ ztest_zil_commit(ztest_ds_t *zd, uint64_t id)
void
ztest_spa_create_destroy(ztest_ds_t *zd, uint64_t id)
{
- ztest_shared_t *zs = ztest_shared;
+ ztest_shared_opts_t *zo = &ztest_opts;
spa_t *spa;
nvlist_t *nvroot;
@@ -2093,15 +2267,15 @@ ztest_spa_create_destroy(ztest_ds_t *zd, uint64_t id)
* Attempt to create an existing pool. It shouldn't matter
* what's in the nvroot; we should fail with EEXIST.
*/
- (void) rw_rdlock(&zs->zs_name_lock);
+ (void) rw_rdlock(&ztest_name_lock);
nvroot = make_vdev_root("/dev/bogus", NULL, 0, 0, 0, 0, 0, 1);
- VERIFY3U(EEXIST, ==, spa_create(zs->zs_pool, nvroot, NULL, NULL, NULL));
+ VERIFY3U(EEXIST, ==, spa_create(zo->zo_pool, nvroot, NULL, NULL, NULL));
nvlist_free(nvroot);
- VERIFY3U(0, ==, spa_open(zs->zs_pool, &spa, FTAG));
- VERIFY3U(EBUSY, ==, spa_destroy(zs->zs_pool));
+ VERIFY3U(0, ==, spa_open(zo->zo_pool, &spa, FTAG));
+ VERIFY3U(EBUSY, ==, spa_destroy(zo->zo_pool));
spa_close(spa, FTAG);
- (void) rw_unlock(&zs->zs_name_lock);
+ (void) rw_unlock(&ztest_name_lock);
}
static vdev_t *
@@ -2148,14 +2322,15 @@ void
ztest_vdev_add_remove(ztest_ds_t *zd, uint64_t id)
{
ztest_shared_t *zs = ztest_shared;
- spa_t *spa = zs->zs_spa;
+ spa_t *spa = ztest_spa;
uint64_t leaves;
uint64_t guid;
nvlist_t *nvroot;
int error;
- VERIFY(mutex_lock(&zs->zs_vdev_lock) == 0);
- leaves = MAX(zs->zs_mirrors + zs->zs_splits, 1) * zopt_raidz;
+ VERIFY(mutex_lock(&ztest_vdev_lock) == 0);
+ leaves =
+ MAX(zs->zs_mirrors + zs->zs_splits, 1) * ztest_opts.zo_raidz;
spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
@@ -2180,9 +2355,9 @@ ztest_vdev_add_remove(ztest_ds_t *zd, uint64_t id)
* dmu_objset_destroy() to fail with EBUSY thus
* leaving the dataset in an inconsistent state.
*/
- VERIFY(rw_wrlock(&ztest_shared->zs_name_lock) == 0);
+ VERIFY(rw_wrlock(&ztest_name_lock) == 0);
error = spa_vdev_remove(spa, guid, B_FALSE);
- VERIFY(rw_unlock(&ztest_shared->zs_name_lock) == 0);
+ VERIFY(rw_unlock(&ztest_name_lock) == 0);
if (error && error != EEXIST)
fatal(0, "spa_vdev_remove() = %d", error);
@@ -2192,8 +2367,10 @@ ztest_vdev_add_remove(ztest_ds_t *zd, uint64_t id)
/*
* Make 1/4 of the devices be log devices.
*/
- nvroot = make_vdev_root(NULL, NULL, zopt_vdev_size, 0,
- ztest_random(4) == 0, zopt_raidz, zs->zs_mirrors, 1);
+ nvroot = make_vdev_root(NULL, NULL,
+ ztest_opts.zo_vdev_size, 0,
+ ztest_random(4) == 0, ztest_opts.zo_raidz,
+ zs->zs_mirrors, 1);
error = spa_vdev_add(spa, nvroot);
nvlist_free(nvroot);
@@ -2204,7 +2381,7 @@ ztest_vdev_add_remove(ztest_ds_t *zd, uint64_t id)
fatal(0, "spa_vdev_add() = %d", error);
}
- VERIFY(mutex_unlock(&ztest_shared->zs_vdev_lock) == 0);
+ VERIFY(mutex_unlock(&ztest_vdev_lock) == 0);
}
/*
@@ -2215,7 +2392,7 @@ void
ztest_vdev_aux_add_remove(ztest_ds_t *zd, uint64_t id)
{
ztest_shared_t *zs = ztest_shared;
- spa_t *spa = zs->zs_spa;
+ spa_t *spa = ztest_spa;
vdev_t *rvd = spa->spa_root_vdev;
spa_aux_vdev_t *sav;
char *aux;
@@ -2230,7 +2407,7 @@ ztest_vdev_aux_add_remove(ztest_ds_t *zd, uint64_t id)
aux = ZPOOL_CONFIG_L2CACHE;
}
- VERIFY(mutex_lock(&zs->zs_vdev_lock) == 0);
+ VERIFY(mutex_lock(&ztest_vdev_lock) == 0);
spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
@@ -2247,8 +2424,9 @@ ztest_vdev_aux_add_remove(ztest_ds_t *zd, uint64_t id)
for (;;) {
char path[MAXPATHLEN];
int c;
- (void) sprintf(path, ztest_aux_template, zopt_dir,
- zopt_pool, aux, zs->zs_vdev_aux);
+ (void) snprintf(path, sizeof (path), ztest_aux_template,
+ ztest_opts.zo_dir, ztest_opts.zo_pool, aux,
+ zs->zs_vdev_aux);
for (c = 0; c < sav->sav_count; c++)
if (strcmp(sav->sav_vdevs[c]->vdev_path,
path) == 0)
@@ -2267,7 +2445,7 @@ ztest_vdev_aux_add_remove(ztest_ds_t *zd, uint64_t id)
* Add a new device.
*/
nvlist_t *nvroot = make_vdev_root(NULL, aux,
- (zopt_vdev_size * 5) / 4, 0, 0, 0, 0, 1);
+ (ztest_opts.zo_vdev_size * 5) / 4, 0, 0, 0, 0, 1);
error = spa_vdev_add(spa, nvroot);
if (error != 0)
fatal(0, "spa_vdev_add(%p) = %d", nvroot, error);
@@ -2286,7 +2464,7 @@ ztest_vdev_aux_add_remove(ztest_ds_t *zd, uint64_t id)
fatal(0, "spa_vdev_remove(%llu) = %d", guid, error);
}
- VERIFY(mutex_unlock(&zs->zs_vdev_lock) == 0);
+ VERIFY(mutex_unlock(&ztest_vdev_lock) == 0);
}
/*
@@ -2297,17 +2475,17 @@ void
ztest_split_pool(ztest_ds_t *zd, uint64_t id)
{
ztest_shared_t *zs = ztest_shared;
- spa_t *spa = zs->zs_spa;
+ spa_t *spa = ztest_spa;
vdev_t *rvd = spa->spa_root_vdev;
nvlist_t *tree, **child, *config, *split, **schild;
uint_t c, children, schildren = 0, lastlogid = 0;
int error = 0;
- VERIFY(mutex_lock(&zs->zs_vdev_lock) == 0);
+ VERIFY(mutex_lock(&ztest_vdev_lock) == 0);
/* ensure we have a useable config; mirrors of raidz aren't supported */
- if (zs->zs_mirrors < 3 || zopt_raidz > 1) {
- VERIFY(mutex_unlock(&zs->zs_vdev_lock) == 0);
+ if (zs->zs_mirrors < 3 || ztest_opts.zo_raidz > 1) {
+ VERIFY(mutex_unlock(&ztest_vdev_lock) == 0);
return;
}
@@ -2366,9 +2544,9 @@ ztest_split_pool(ztest_ds_t *zd, uint64_t id)
spa_config_exit(spa, SCL_VDEV, FTAG);
- (void) rw_wrlock(&zs->zs_name_lock);
+ (void) rw_wrlock(&ztest_name_lock);
error = spa_vdev_split_mirror(spa, "splitp", config, NULL, B_FALSE);
- (void) rw_unlock(&zs->zs_name_lock);
+ (void) rw_unlock(&ztest_name_lock);
nvlist_free(config);
@@ -2381,7 +2559,7 @@ ztest_split_pool(ztest_ds_t *zd, uint64_t id)
++zs->zs_splits;
--zs->zs_mirrors;
}
- VERIFY(mutex_unlock(&zs->zs_vdev_lock) == 0);
+ VERIFY(mutex_unlock(&ztest_vdev_lock) == 0);
}
@@ -2393,7 +2571,7 @@ void
ztest_vdev_attach_detach(ztest_ds_t *zd, uint64_t id)
{
ztest_shared_t *zs = ztest_shared;
- spa_t *spa = zs->zs_spa;
+ spa_t *spa = ztest_spa;
spa_aux_vdev_t *sav = &spa->spa_spares;
vdev_t *rvd = spa->spa_root_vdev;
vdev_t *oldvd, *newvd, *pvd;
@@ -2410,8 +2588,8 @@ ztest_vdev_attach_detach(ztest_ds_t *zd, uint64_t id)
int oldvd_is_log;
int error, expected_error;
- VERIFY(mutex_lock(&zs->zs_vdev_lock) == 0);
- leaves = MAX(zs->zs_mirrors, 1) * zopt_raidz;
+ VERIFY(mutex_lock(&ztest_vdev_lock) == 0);
+ leaves = MAX(zs->zs_mirrors, 1) * ztest_opts.zo_raidz;
spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
@@ -2437,12 +2615,12 @@ ztest_vdev_attach_detach(ztest_ds_t *zd, uint64_t id)
if (zs->zs_mirrors >= 1) {
ASSERT(oldvd->vdev_ops == &vdev_mirror_ops);
ASSERT(oldvd->vdev_children >= zs->zs_mirrors);
- oldvd = oldvd->vdev_child[leaf / zopt_raidz];
+ oldvd = oldvd->vdev_child[leaf / ztest_opts.zo_raidz];
}
- if (zopt_raidz > 1) {
+ if (ztest_opts.zo_raidz > 1) {
ASSERT(oldvd->vdev_ops == &vdev_raidz_ops);
- ASSERT(oldvd->vdev_children == zopt_raidz);
- oldvd = oldvd->vdev_child[leaf % zopt_raidz];
+ ASSERT(oldvd->vdev_children == ztest_opts.zo_raidz);
+ oldvd = oldvd->vdev_child[leaf % ztest_opts.zo_raidz];
}
/*
@@ -2471,7 +2649,7 @@ ztest_vdev_attach_detach(ztest_ds_t *zd, uint64_t id)
if (error != 0 && error != ENODEV && error != EBUSY &&
error != ENOTSUP)
fatal(0, "detach (%s) returned %d", oldpath, error);
- VERIFY(mutex_unlock(&zs->zs_vdev_lock) == 0);
+ VERIFY(mutex_unlock(&ztest_vdev_lock) == 0);
return;
}
@@ -2485,7 +2663,8 @@ ztest_vdev_attach_detach(ztest_ds_t *zd, uint64_t id)
(void) strcpy(newpath, newvd->vdev_path);
} else {
(void) snprintf(newpath, sizeof (newpath), ztest_dev_template,
- zopt_dir, zopt_pool, top * leaves + leaf);
+ ztest_opts.zo_dir, ztest_opts.zo_pool,
+ top * leaves + leaf);
if (ztest_random(2) == 0)
newpath[strlen(newpath) - 1] = 'b';
newvd = vdev_lookup_by_path(rvd, newpath);
@@ -2564,7 +2743,7 @@ ztest_vdev_attach_detach(ztest_ds_t *zd, uint64_t id)
(longlong_t)newsize, replacing, error, expected_error);
}
- VERIFY(mutex_unlock(&zs->zs_vdev_lock) == 0);
+ VERIFY(mutex_unlock(&ztest_vdev_lock) == 0);
}
/*
@@ -2587,7 +2766,7 @@ grow_vdev(vdev_t *vd, void *arg)
fsize = lseek(fd, 0, SEEK_END);
(void) ftruncate(fd, *newsize);
- if (zopt_verbose >= 6) {
+ if (ztest_opts.zo_verbose >= 6) {
(void) printf("%s grew from %lu to %lu bytes\n",
vd->vdev_path, (ulong_t)fsize, (ulong_t)*newsize);
}
@@ -2623,7 +2802,7 @@ online_vdev(vdev_t *vd, void *arg)
* vdev_open fails is by checking the returned newstate.
*/
if (error || newstate != VDEV_STATE_HEALTHY) {
- if (zopt_verbose >= 5) {
+ if (ztest_opts.zo_verbose >= 5) {
(void) printf("Unable to expand vdev, state %llu, "
"error %d\n", (u_longlong_t)newstate, error);
}
@@ -2638,7 +2817,7 @@ online_vdev(vdev_t *vd, void *arg)
* trying to online it.
*/
if (generation != spa->spa_config_generation) {
- if (zopt_verbose >= 5) {
+ if (ztest_opts.zo_verbose >= 5) {
(void) printf("vdev configuration has changed, "
"guid %llu, state %llu, expected gen %llu, "
"got gen %llu\n",
@@ -2684,8 +2863,7 @@ vdev_walk_tree(vdev_t *vd, vdev_t *(*func)(vdev_t *, void *), void *arg)
void
ztest_vdev_LUN_growth(ztest_ds_t *zd, uint64_t id)
{
- ztest_shared_t *zs = ztest_shared;
- spa_t *spa = zs->zs_spa;
+ spa_t *spa = ztest_spa;
vdev_t *vd, *tvd;
metaslab_class_t *mc;
metaslab_group_t *mg;
@@ -2693,7 +2871,7 @@ ztest_vdev_LUN_growth(ztest_ds_t *zd, uint64_t id)
uint64_t top;
uint64_t old_class_space, new_class_space, old_ms_count, new_ms_count;
- VERIFY(mutex_lock(&zs->zs_vdev_lock) == 0);
+ VERIFY(mutex_lock(&ztest_vdev_lock) == 0);
spa_config_enter(spa, SCL_STATE, spa, RW_READER);
top = ztest_random_vdev_top(spa, B_TRUE);
@@ -2719,16 +2897,16 @@ ztest_vdev_LUN_growth(ztest_ds_t *zd, uint64_t id)
* original size, and it has a valid psize.
*/
if (tvd->vdev_state != VDEV_STATE_HEALTHY ||
- psize == 0 || psize >= 4 * zopt_vdev_size) {
+ psize == 0 || psize >= 4 * ztest_opts.zo_vdev_size) {
spa_config_exit(spa, SCL_STATE, spa);
- VERIFY(mutex_unlock(&zs->zs_vdev_lock) == 0);
+ VERIFY(mutex_unlock(&ztest_vdev_lock) == 0);
return;
}
ASSERT(psize > 0);
newsize = psize + psize / 8;
ASSERT3U(newsize, >, psize);
- if (zopt_verbose >= 6) {
+ if (ztest_opts.zo_verbose >= 6) {
(void) printf("Expanding LUN %s from %lu to %lu\n",
vd->vdev_path, (ulong_t)psize, (ulong_t)newsize);
}
@@ -2741,12 +2919,12 @@ ztest_vdev_LUN_growth(ztest_ds_t *zd, uint64_t id)
if (vdev_walk_tree(tvd, grow_vdev, &newsize) != NULL ||
vdev_walk_tree(tvd, online_vdev, NULL) != NULL ||
tvd->vdev_state != VDEV_STATE_HEALTHY) {
- if (zopt_verbose >= 5) {
+ if (ztest_opts.zo_verbose >= 5) {
(void) printf("Could not expand LUN because "
"the vdev configuration changed.\n");
}
spa_config_exit(spa, SCL_STATE, spa);
- VERIFY(mutex_unlock(&zs->zs_vdev_lock) == 0);
+ VERIFY(mutex_unlock(&ztest_vdev_lock) == 0);
return;
}
@@ -2775,12 +2953,12 @@ ztest_vdev_LUN_growth(ztest_ds_t *zd, uint64_t id)
new_class_space = metaslab_class_get_space(mc);
if (tvd->vdev_mg != mg || mg->mg_class != mc) {
- if (zopt_verbose >= 5) {
+ if (ztest_opts.zo_verbose >= 5) {
(void) printf("Could not verify LUN expansion due to "
"intervening vdev offline or remove.\n");
}
spa_config_exit(spa, SCL_STATE, spa);
- VERIFY(mutex_unlock(&zs->zs_vdev_lock) == 0);
+ VERIFY(mutex_unlock(&ztest_vdev_lock) == 0);
return;
}
@@ -2798,7 +2976,7 @@ ztest_vdev_LUN_growth(ztest_ds_t *zd, uint64_t id)
fatal(0, "LUN expansion failed: class_space %llu <= %llu\n",
old_class_space, new_class_space);
- if (zopt_verbose >= 5) {
+ if (ztest_opts.zo_verbose >= 5) {
char oldnumbuf[6], newnumbuf[6];
nicenum(old_class_space, oldnumbuf);
@@ -2808,7 +2986,7 @@ ztest_vdev_LUN_growth(ztest_ds_t *zd, uint64_t id)
}
spa_config_exit(spa, SCL_STATE, spa);
- VERIFY(mutex_unlock(&zs->zs_vdev_lock) == 0);
+ VERIFY(mutex_unlock(&ztest_vdev_lock) == 0);
}
/*
@@ -2835,7 +3013,8 @@ ztest_dataset_create(char *dsname)
if (err || zilset < 80)
return (err);
- (void) printf("Setting dataset %s to sync always\n", dsname);
+ if (ztest_opts.zo_verbose >= 6)
+ (void) printf("Setting dataset %s to sync always\n", dsname);
return (ztest_dsl_prop_set_uint64(dsname, ZFS_PROP_SYNC,
ZFS_SYNC_ALWAYS, B_FALSE));
}
@@ -2907,7 +3086,6 @@ ztest_snapshot_destroy(char *osname, uint64_t id)
void
ztest_dmu_objset_create_destroy(ztest_ds_t *zd, uint64_t id)
{
- ztest_shared_t *zs = ztest_shared;
ztest_ds_t zdtmp;
int iters;
int error;
@@ -2915,10 +3093,10 @@ ztest_dmu_objset_create_destroy(ztest_ds_t *zd, uint64_t id)
char name[MAXNAMELEN];
zilog_t *zilog;
- (void) rw_rdlock(&zs->zs_name_lock);
+ (void) rw_rdlock(&ztest_name_lock);
(void) snprintf(name, MAXNAMELEN, "%s/temp_%llu",
- zs->zs_pool, (u_longlong_t)id);
+ ztest_opts.zo_pool, (u_longlong_t)id);
/*
* If this dataset exists from a previous run, process its replay log
@@ -2927,7 +3105,7 @@ ztest_dmu_objset_create_destroy(ztest_ds_t *zd, uint64_t id)
*/
if (ztest_random(2) == 0 &&
dmu_objset_own(name, DMU_OST_OTHER, B_FALSE, FTAG, &os) == 0) {
- ztest_zd_init(&zdtmp, os);
+ ztest_zd_init(&zdtmp, NULL, os);
zil_replay(os, &zdtmp, ztest_replay_vector);
ztest_zd_fini(&zdtmp);
dmu_objset_disown(os, FTAG);
@@ -2953,7 +3131,7 @@ ztest_dmu_objset_create_destroy(ztest_ds_t *zd, uint64_t id)
if (error) {
if (error == ENOSPC) {
ztest_record_enospc(FTAG);
- (void) rw_unlock(&zs->zs_name_lock);
+ (void) rw_unlock(&ztest_name_lock);
return;
}
fatal(0, "dmu_objset_create(%s) = %d", name, error);
@@ -2962,7 +3140,7 @@ ztest_dmu_objset_create_destroy(ztest_ds_t *zd, uint64_t id)
VERIFY3U(0, ==,
dmu_objset_own(name, DMU_OST_OTHER, B_FALSE, FTAG, &os));
- ztest_zd_init(&zdtmp, os);
+ ztest_zd_init(&zdtmp, NULL, os);
/*
* Open the intent log for it.
@@ -3002,7 +3180,7 @@ ztest_dmu_objset_create_destroy(ztest_ds_t *zd, uint64_t id)
dmu_objset_disown(os, FTAG);
ztest_zd_fini(&zdtmp);
- (void) rw_unlock(&zs->zs_name_lock);
+ (void) rw_unlock(&ztest_name_lock);
}
/*
@@ -3011,12 +3189,10 @@ ztest_dmu_objset_create_destroy(ztest_ds_t *zd, uint64_t id)
void
ztest_dmu_snapshot_create_destroy(ztest_ds_t *zd, uint64_t id)
{
- ztest_shared_t *zs = ztest_shared;
-
- (void) rw_rdlock(&zs->zs_name_lock);
+ (void) rw_rdlock(&ztest_name_lock);
(void) ztest_snapshot_destroy(zd->zd_name, id);
(void) ztest_snapshot_create(zd->zd_name, id);
- (void) rw_unlock(&zs->zs_name_lock);
+ (void) rw_unlock(&ztest_name_lock);
}
/*
@@ -3061,7 +3237,6 @@ ztest_dsl_dataset_cleanup(char *osname, uint64_t id)
void
ztest_dsl_dataset_promote_busy(ztest_ds_t *zd, uint64_t id)
{
- ztest_shared_t *zs = ztest_shared;
objset_t *clone;
dsl_dataset_t *ds;
char snap1name[MAXNAMELEN];
@@ -3072,7 +3247,7 @@ ztest_dsl_dataset_promote_busy(ztest_ds_t *zd, uint64_t id)
char *osname = zd->zd_name;
int error;
- (void) rw_rdlock(&zs->zs_name_lock);
+ (void) rw_rdlock(&ztest_name_lock);
ztest_dsl_dataset_cleanup(osname, id);
@@ -3152,7 +3327,7 @@ ztest_dsl_dataset_promote_busy(ztest_ds_t *zd, uint64_t id)
out:
ztest_dsl_dataset_cleanup(osname, id);
- (void) rw_unlock(&zs->zs_name_lock);
+ (void) rw_unlock(&ztest_name_lock);
}
/*
@@ -3351,7 +3526,7 @@ ztest_dmu_read_write(ztest_ds_t *zd, uint64_t id)
dmu_write(os, packobj, packoff, packsize, packbuf, tx);
if (freeit) {
- if (zopt_verbose >= 7) {
+ if (ztest_opts.zo_verbose >= 7) {
(void) printf("freeing offset %llx size %llx"
" txg %llx\n",
(u_longlong_t)bigoff,
@@ -3360,7 +3535,7 @@ ztest_dmu_read_write(ztest_ds_t *zd, uint64_t id)
}
VERIFY(0 == dmu_free_range(os, bigobj, bigoff, bigsize, tx));
} else {
- if (zopt_verbose >= 7) {
+ if (ztest_opts.zo_verbose >= 7) {
(void) printf("writing offset %llx size %llx"
" txg %llx\n",
(u_longlong_t)bigoff,
@@ -3598,7 +3773,7 @@ ztest_dmu_read_write_zcopy(ztest_ds_t *zd, uint64_t id)
* Now write them out.
*/
dmu_write(os, packobj, packoff, packsize, packbuf, tx);
- if (zopt_verbose >= 7) {
+ if (ztest_opts.zo_verbose >= 7) {
(void) printf("writing offset %llx size %llx"
" txg %llx\n",
(u_longlong_t)bigoff,
@@ -4239,37 +4414,35 @@ ztest_dsl_prop_get_set(ztest_ds_t *zd, uint64_t id)
ZFS_PROP_COPIES,
ZFS_PROP_DEDUP
};
- ztest_shared_t *zs = ztest_shared;
- (void) rw_rdlock(&zs->zs_name_lock);
+ (void) rw_rdlock(&ztest_name_lock);
for (int p = 0; p < sizeof (proplist) / sizeof (proplist[0]); p++)
(void) ztest_dsl_prop_set_uint64(zd->zd_name, proplist[p],
ztest_random_dsl_prop(proplist[p]), (int)ztest_random(2));
- (void) rw_unlock(&zs->zs_name_lock);
+ (void) rw_unlock(&ztest_name_lock);
}
/* ARGSUSED */
void
ztest_spa_prop_get_set(ztest_ds_t *zd, uint64_t id)
{
- ztest_shared_t *zs = ztest_shared;
nvlist_t *props = NULL;
- (void) rw_rdlock(&zs->zs_name_lock);
+ (void) rw_rdlock(&ztest_name_lock);
- (void) ztest_spa_prop_set_uint64(zs, ZPOOL_PROP_DEDUPDITTO,
+ (void) ztest_spa_prop_set_uint64(ZPOOL_PROP_DEDUPDITTO,
ZIO_DEDUPDITTO_MIN + ztest_random(ZIO_DEDUPDITTO_MIN));
- VERIFY3U(spa_prop_get(zs->zs_spa, &props), ==, 0);
+ VERIFY3U(spa_prop_get(ztest_spa, &props), ==, 0);
- if (zopt_verbose >= 6)
+ if (ztest_opts.zo_verbose >= 6)
dump_nvlist(props, 4);
nvlist_free(props);
- (void) rw_unlock(&zs->zs_name_lock);
+ (void) rw_unlock(&ztest_name_lock);
}
/*
@@ -4287,7 +4460,7 @@ ztest_dmu_snapshot_hold(ztest_ds_t *zd, uint64_t id)
char tag[100];
char osname[MAXNAMELEN];
- (void) rw_rdlock(&ztest_shared->zs_name_lock);
+ (void) rw_rdlock(&ztest_name_lock);
dmu_objset_name(os, osname);
@@ -4384,7 +4557,7 @@ ztest_dmu_snapshot_hold(ztest_ds_t *zd, uint64_t id)
VERIFY(dmu_objset_hold(fullname, FTAG, &origin) == ENOENT);
out:
- (void) rw_unlock(&ztest_shared->zs_name_lock);
+ (void) rw_unlock(&ztest_name_lock);
}
/*
@@ -4395,7 +4568,7 @@ void
ztest_fault_inject(ztest_ds_t *zd, uint64_t id)
{
ztest_shared_t *zs = ztest_shared;
- spa_t *spa = zs->zs_spa;
+ spa_t *spa = ztest_spa;
int fd;
uint64_t offset;
uint64_t leaves;
@@ -4412,11 +4585,11 @@ ztest_fault_inject(ztest_ds_t *zd, uint64_t id)
uint64_t guid0 = 0;
boolean_t islog = B_FALSE;
- VERIFY(mutex_lock(&zs->zs_vdev_lock) == 0);
+ VERIFY(mutex_lock(&ztest_vdev_lock) == 0);
maxfaults = MAXFAULTS();
- leaves = MAX(zs->zs_mirrors, 1) * zopt_raidz;
+ leaves = MAX(zs->zs_mirrors, 1) * ztest_opts.zo_raidz;
mirror_save = zs->zs_mirrors;
- VERIFY(mutex_unlock(&zs->zs_vdev_lock) == 0);
+ VERIFY(mutex_unlock(&ztest_vdev_lock) == 0);
ASSERT(leaves >= 1);
@@ -4439,9 +4612,11 @@ ztest_fault_inject(ztest_ds_t *zd, uint64_t id)
* and we'll write random garbage to the randomly chosen leaf.
*/
(void) snprintf(path0, sizeof (path0), ztest_dev_template,
- zopt_dir, zopt_pool, top * leaves + zs->zs_splits);
+ ztest_opts.zo_dir, ztest_opts.zo_pool,
+ top * leaves + zs->zs_splits);
(void) snprintf(pathrand, sizeof (pathrand), ztest_dev_template,
- zopt_dir, zopt_pool, top * leaves + leaf);
+ ztest_opts.zo_dir, ztest_opts.zo_pool,
+ top * leaves + leaf);
vd0 = vdev_lookup_by_path(spa->spa_root_vdev, path0);
if (vd0 != NULL && vd0->vdev_top->vdev_islog)
@@ -4510,12 +4685,12 @@ ztest_fault_inject(ztest_ds_t *zd, uint64_t id)
* leaving the dataset in an inconsistent state.
*/
if (islog)
- (void) rw_wrlock(&ztest_shared->zs_name_lock);
+ (void) rw_wrlock(&ztest_name_lock);
VERIFY(vdev_offline(spa, guid0, flags) != EBUSY);
if (islog)
- (void) rw_unlock(&ztest_shared->zs_name_lock);
+ (void) rw_unlock(&ztest_name_lock);
} else {
(void) vdev_online(spa, guid0, 0, NULL);
}
@@ -4542,9 +4717,9 @@ ztest_fault_inject(ztest_ds_t *zd, uint64_t id)
if (offset >= fsize)
continue;
- VERIFY(mutex_lock(&zs->zs_vdev_lock) == 0);
+ VERIFY(mutex_lock(&ztest_vdev_lock) == 0);
if (mirror_save != zs->zs_mirrors) {
- VERIFY(mutex_unlock(&zs->zs_vdev_lock) == 0);
+ VERIFY(mutex_unlock(&ztest_vdev_lock) == 0);
(void) close(fd);
return;
}
@@ -4553,9 +4728,9 @@ ztest_fault_inject(ztest_ds_t *zd, uint64_t id)
fatal(1, "can't inject bad word at 0x%llx in %s",
offset, pathrand);
- VERIFY(mutex_unlock(&zs->zs_vdev_lock) == 0);
+ VERIFY(mutex_unlock(&ztest_vdev_lock) == 0);
- if (zopt_verbose >= 7)
+ if (ztest_opts.zo_verbose >= 7)
(void) printf("injected bad word into %s,"
" offset 0x%llx\n", pathrand, (u_longlong_t)offset);
}
@@ -4570,7 +4745,7 @@ void
ztest_ddt_repair(ztest_ds_t *zd, uint64_t id)
{
ztest_shared_t *zs = ztest_shared;
- spa_t *spa = zs->zs_spa;
+ spa_t *spa = ztest_spa;
objset_t *os = zd->zd_os;
ztest_od_t od[1];
uint64_t object, blocksize, txg, pattern, psize;
@@ -4593,19 +4768,19 @@ ztest_ddt_repair(ztest_ds_t *zd, uint64_t id)
* Take the name lock as writer to prevent anyone else from changing
* the pool and dataset properies we need to maintain during this test.
*/
- (void) rw_wrlock(&zs->zs_name_lock);
+ (void) rw_wrlock(&ztest_name_lock);
if (ztest_dsl_prop_set_uint64(zd->zd_name, ZFS_PROP_DEDUP, checksum,
B_FALSE) != 0 ||
ztest_dsl_prop_set_uint64(zd->zd_name, ZFS_PROP_COPIES, 1,
B_FALSE) != 0) {
- (void) rw_unlock(&zs->zs_name_lock);
+ (void) rw_unlock(&ztest_name_lock);
return;
}
object = od[0].od_object;
blocksize = od[0].od_blocksize;
- pattern = spa_guid(spa) ^ dmu_objset_fsid_guid(os);
+ pattern = zs->zs_guid ^ dmu_objset_fsid_guid(os);
ASSERT(object != 0);
@@ -4613,7 +4788,7 @@ ztest_ddt_repair(ztest_ds_t *zd, uint64_t id)
dmu_tx_hold_write(tx, object, 0, copies * blocksize);
txg = ztest_tx_assign(tx, TXG_WAIT, FTAG);
if (txg == 0) {
- (void) rw_unlock(&zs->zs_name_lock);
+ (void) rw_unlock(&ztest_name_lock);
return;
}
@@ -4657,7 +4832,7 @@ ztest_ddt_repair(ztest_ds_t *zd, uint64_t id)
zio_buf_free(buf, psize);
- (void) rw_unlock(&zs->zs_name_lock);
+ (void) rw_unlock(&ztest_name_lock);
}
/*
@@ -4667,8 +4842,7 @@ ztest_ddt_repair(ztest_ds_t *zd, uint64_t id)
void
ztest_scrub(ztest_ds_t *zd, uint64_t id)
{
- ztest_shared_t *zs = ztest_shared;
- spa_t *spa = zs->zs_spa;
+ spa_t *spa = ztest_spa;
(void) spa_scan(spa, POOL_SCAN_SCRUB);
(void) poll(NULL, 0, 100); /* wait a moment, then force a restart */
@@ -4676,19 +4850,42 @@ ztest_scrub(ztest_ds_t *zd, uint64_t id)
}
/*
+ * Change the guid for the pool.
+ */
+/* ARGSUSED */
+void
+ztest_reguid(ztest_ds_t *zd, uint64_t id)
+{
+ spa_t *spa = ztest_spa;
+ uint64_t orig, load;
+
+ orig = spa_guid(spa);
+ load = spa_load_guid(spa);
+ if (spa_change_guid(spa) != 0)
+ return;
+
+ if (ztest_opts.zo_verbose >= 3) {
+ (void) printf("Changed guid old %llu -> %llu\n",
+ (u_longlong_t)orig, (u_longlong_t)spa_guid(spa));
+ }
+
+ VERIFY3U(orig, !=, spa_guid(spa));
+ VERIFY3U(load, ==, spa_load_guid(spa));
+}
+
+/*
* Rename the pool to a different name and then rename it back.
*/
/* ARGSUSED */
void
ztest_spa_rename(ztest_ds_t *zd, uint64_t id)
{
- ztest_shared_t *zs = ztest_shared;
char *oldname, *newname;
spa_t *spa;
- (void) rw_wrlock(&zs->zs_name_lock);
+ (void) rw_wrlock(&ztest_name_lock);
- oldname = zs->zs_pool;
+ oldname = ztest_opts.zo_pool;
newname = umem_alloc(strlen(oldname) + 5, UMEM_NOFAIL);
(void) strcpy(newname, oldname);
(void) strcat(newname, "_tmp");
@@ -4708,7 +4905,7 @@ ztest_spa_rename(ztest_ds_t *zd, uint64_t id)
*/
VERIFY3U(0, ==, spa_open(newname, &spa, FTAG));
- ASSERT(spa == zs->zs_spa);
+ ASSERT(spa == ztest_spa);
spa_close(spa, FTAG);
/*
@@ -4721,12 +4918,12 @@ ztest_spa_rename(ztest_ds_t *zd, uint64_t id)
*/
VERIFY3U(0, ==, spa_open(oldname, &spa, FTAG));
- ASSERT(spa == zs->zs_spa);
+ ASSERT(spa == ztest_spa);
spa_close(spa, FTAG);
umem_free(newname, strlen(newname) + 1);
- (void) rw_unlock(&zs->zs_name_lock);
+ (void) rw_unlock(&ztest_name_lock);
}
/*
@@ -4757,19 +4954,19 @@ ztest_run_zdb(char *pool)
"/usr/sbin%.*s/zdb -bcc%s%s -U %s %s",
isalen,
isa,
- zopt_verbose >= 3 ? "s" : "",
- zopt_verbose >= 4 ? "v" : "",
+ ztest_opts.zo_verbose >= 3 ? "s" : "",
+ ztest_opts.zo_verbose >= 4 ? "v" : "",
spa_config_path,
pool);
free(isa);
- if (zopt_verbose >= 5)
+ if (ztest_opts.zo_verbose >= 5)
(void) printf("Executing %s\n", strstr(zdb, "zdb "));
fp = popen(zdb, "r");
while (fgets(zbuf, sizeof (zbuf), fp) != NULL)
- if (zopt_verbose >= 3)
+ if (ztest_opts.zo_verbose >= 3)
(void) printf("%s", zbuf);
status = pclose(fp);
@@ -4789,12 +4986,12 @@ ztest_walk_pool_directory(char *header)
{
spa_t *spa = NULL;
- if (zopt_verbose >= 6)
+ if (ztest_opts.zo_verbose >= 6)
(void) printf("%s\n", header);
mutex_enter(&spa_namespace_lock);
while ((spa = spa_next(spa)) != NULL)
- if (zopt_verbose >= 6)
+ if (ztest_opts.zo_verbose >= 6)
(void) printf("\t%s\n", spa_name(spa));
mutex_exit(&spa_namespace_lock);
}
@@ -4806,7 +5003,7 @@ ztest_spa_import_export(char *oldname, char *newname)
uint64_t pool_guid;
spa_t *spa;
- if (zopt_verbose >= 4) {
+ if (ztest_opts.zo_verbose >= 4) {
(void) printf("import/export: old = %s, new = %s\n",
oldname, newname);
}
@@ -4881,7 +5078,7 @@ ztest_spa_import_export(char *oldname, char *newname)
static void
ztest_resume(spa_t *spa)
{
- if (spa_suspended(spa) && zopt_verbose >= 6)
+ if (spa_suspended(spa) && ztest_opts.zo_verbose >= 6)
(void) printf("resuming from suspended state\n");
spa_vdev_state_enter(spa, SCL_NONE);
vdev_clear(spa, NULL);
@@ -4919,10 +5116,10 @@ ztest_deadman_thread(void *arg)
}
static void
-ztest_execute(ztest_info_t *zi, uint64_t id)
+ztest_execute(int test, ztest_info_t *zi, uint64_t id)
{
- ztest_shared_t *zs = ztest_shared;
- ztest_ds_t *zd = &zs->zs_zd[id % zopt_datasets];
+ ztest_ds_t *zd = &ztest_ds[id % ztest_opts.zo_datasets];
+ ztest_shared_callstate_t *zc = ZTEST_GET_SHARED_CALLSTATE(test);
hrtime_t functime = gethrtime();
for (int i = 0; i < zi->zi_iters; i++)
@@ -4930,10 +5127,10 @@ ztest_execute(ztest_info_t *zi, uint64_t id)
functime = gethrtime() - functime;
- atomic_add_64(&zi->zi_call_count, 1);
- atomic_add_64(&zi->zi_call_time, functime);
+ atomic_add_64(&zc->zc_count, 1);
+ atomic_add_64(&zc->zc_time, functime);
- if (zopt_verbose >= 4) {
+ if (ztest_opts.zo_verbose >= 4) {
Dl_info dli;
(void) dladdr((void *)zi->zi_func, &dli);
(void) printf("%6.2f sec in %s\n",
@@ -4944,11 +5141,13 @@ ztest_execute(ztest_info_t *zi, uint64_t id)
static void *
ztest_thread(void *arg)
{
+ int rand;
uint64_t id = (uintptr_t)arg;
ztest_shared_t *zs = ztest_shared;
uint64_t call_next;
hrtime_t now;
ztest_info_t *zi;
+ ztest_shared_callstate_t *zc;
while ((now = gethrtime()) < zs->zs_thread_stop) {
/*
@@ -4966,13 +5165,16 @@ ztest_thread(void *arg)
/*
* Pick a random function to execute.
*/
- zi = &zs->zs_info[ztest_random(ZTEST_FUNCS)];
- call_next = zi->zi_call_next;
+ rand = ztest_random(ZTEST_FUNCS);
+ zi = &ztest_info[rand];
+ zc = ZTEST_GET_SHARED_CALLSTATE(rand);
+ call_next = zc->zc_next;
if (now >= call_next &&
- atomic_cas_64(&zi->zi_call_next, call_next, call_next +
- ztest_random(2 * zi->zi_interval[0] + 1)) == call_next)
- ztest_execute(zi, id);
+ atomic_cas_64(&zc->zc_next, call_next, call_next +
+ ztest_random(2 * zi->zi_interval[0] + 1)) == call_next) {
+ ztest_execute(rand, zi, id);
+ }
}
return (NULL);
@@ -4985,13 +5187,13 @@ ztest_dataset_name(char *dsname, char *pool, int d)
}
static void
-ztest_dataset_destroy(ztest_shared_t *zs, int d)
+ztest_dataset_destroy(int d)
{
char name[MAXNAMELEN];
- ztest_dataset_name(name, zs->zs_pool, d);
+ ztest_dataset_name(name, ztest_opts.zo_pool, d);
- if (zopt_verbose >= 3)
+ if (ztest_opts.zo_verbose >= 3)
(void) printf("Destroying %s to free up space\n", name);
/*
@@ -4999,8 +5201,10 @@ ztest_dataset_destroy(ztest_shared_t *zs, int d)
* ztest thread t operates on dataset (t % zopt_datasets),
* so there may be more than one thing to clean up.
*/
- for (int t = d; t < zopt_threads; t += zopt_datasets)
+ for (int t = d; t < ztest_opts.zo_threads;
+ t += ztest_opts.zo_datasets) {
ztest_dsl_dataset_cleanup(name, t);
+ }
(void) dmu_objset_find(name, ztest_objset_destroy_cb, NULL,
DS_FIND_SNAPSHOTS | DS_FIND_CHILDREN);
@@ -5028,31 +5232,31 @@ ztest_dataset_dirobj_verify(ztest_ds_t *zd)
}
static int
-ztest_dataset_open(ztest_shared_t *zs, int d)
+ztest_dataset_open(int d)
{
- ztest_ds_t *zd = &zs->zs_zd[d];
- uint64_t committed_seq = zd->zd_seq;
+ ztest_ds_t *zd = &ztest_ds[d];
+ uint64_t committed_seq = ZTEST_GET_SHARED_DS(d)->zd_seq;
objset_t *os;
zilog_t *zilog;
char name[MAXNAMELEN];
int error;
- ztest_dataset_name(name, zs->zs_pool, d);
+ ztest_dataset_name(name, ztest_opts.zo_pool, d);
- (void) rw_rdlock(&zs->zs_name_lock);
+ (void) rw_rdlock(&ztest_name_lock);
error = ztest_dataset_create(name);
if (error == ENOSPC) {
- (void) rw_unlock(&zs->zs_name_lock);
+ (void) rw_unlock(&ztest_name_lock);
ztest_record_enospc(FTAG);
return (error);
}
ASSERT(error == 0 || error == EEXIST);
VERIFY3U(dmu_objset_hold(name, zd, &os), ==, 0);
- (void) rw_unlock(&zs->zs_name_lock);
+ (void) rw_unlock(&ztest_name_lock);
- ztest_zd_init(zd, os);
+ ztest_zd_init(zd, ZTEST_GET_SHARED_DS(d), os);
zilog = zd->zd_zilog;
@@ -5067,7 +5271,7 @@ ztest_dataset_open(ztest_shared_t *zs, int d)
ztest_dataset_dirobj_verify(zd);
- if (zopt_verbose >= 6)
+ if (ztest_opts.zo_verbose >= 6)
(void) printf("%s replay %llu blocks, %llu records, seq %llu\n",
zd->zd_name,
(u_longlong_t)zilog->zl_parse_blk_count,
@@ -5085,9 +5289,9 @@ ztest_dataset_open(ztest_shared_t *zs, int d)
}
static void
-ztest_dataset_close(ztest_shared_t *zs, int d)
+ztest_dataset_close(int d)
{
- ztest_ds_t *zd = &zs->zs_zd[d];
+ ztest_ds_t *zd = &ztest_ds[d];
zil_close(zd->zd_zilog);
dmu_objset_rele(zd->zd_os, zd);
@@ -5103,6 +5307,7 @@ ztest_run(ztest_shared_t *zs)
{
thread_t *tid;
spa_t *spa;
+ objset_t *os;
thread_t resume_tid;
int error;
@@ -5111,15 +5316,18 @@ ztest_run(ztest_shared_t *zs)
/*
* Initialize parent/child shared state.
*/
- VERIFY(_mutex_init(&zs->zs_vdev_lock, USYNC_THREAD, NULL) == 0);
- VERIFY(rwlock_init(&zs->zs_name_lock, USYNC_THREAD, NULL) == 0);
+ VERIFY(_mutex_init(&ztest_vdev_lock, USYNC_THREAD, NULL) == 0);
+ VERIFY(rwlock_init(&ztest_name_lock, USYNC_THREAD, NULL) == 0);
zs->zs_thread_start = gethrtime();
- zs->zs_thread_stop = zs->zs_thread_start + zopt_passtime * NANOSEC;
+ zs->zs_thread_stop =
+ zs->zs_thread_start + ztest_opts.zo_passtime * NANOSEC;
zs->zs_thread_stop = MIN(zs->zs_thread_stop, zs->zs_proc_stop);
zs->zs_thread_kill = zs->zs_thread_stop;
- if (ztest_random(100) < zopt_killrate)
- zs->zs_thread_kill -= ztest_random(zopt_passtime * NANOSEC);
+ if (ztest_random(100) < ztest_opts.zo_killrate) {
+ zs->zs_thread_kill -=
+ ztest_random(ztest_opts.zo_passtime * NANOSEC);
+ }
(void) _mutex_init(&zcl.zcl_callbacks_lock, USYNC_THREAD, NULL);
@@ -5130,8 +5338,13 @@ ztest_run(ztest_shared_t *zs)
* Open our pool.
*/
kernel_init(FREAD | FWRITE);
- VERIFY(spa_open(zs->zs_pool, &spa, FTAG) == 0);
- zs->zs_spa = spa;
+ VERIFY(spa_open(ztest_opts.zo_pool, &spa, FTAG) == 0);
+ spa->spa_debug = B_TRUE;
+ ztest_spa = spa;
+
+ VERIFY3U(0, ==, dmu_objset_hold(ztest_opts.zo_pool, FTAG, &os));
+ zs->zs_guid = dmu_objset_fsid_guid(os);
+ dmu_objset_rele(os, FTAG);
spa->spa_dedup_ditto = 2 * ZIO_DEDUPDITTO_MIN;
@@ -5176,21 +5389,23 @@ ztest_run(ztest_shared_t *zs)
* If we got any ENOSPC errors on the previous run, destroy something.
*/
if (zs->zs_enospc_count != 0) {
- int d = ztest_random(zopt_datasets);
- ztest_dataset_destroy(zs, d);
+ int d = ztest_random(ztest_opts.zo_datasets);
+ ztest_dataset_destroy(d);
}
zs->zs_enospc_count = 0;
- tid = umem_zalloc(zopt_threads * sizeof (thread_t), UMEM_NOFAIL);
+ tid = umem_zalloc(ztest_opts.zo_threads * sizeof (thread_t),
+ UMEM_NOFAIL);
- if (zopt_verbose >= 4)
+ if (ztest_opts.zo_verbose >= 4)
(void) printf("starting main threads...\n");
/*
* Kick off all the tests that run in parallel.
*/
- for (int t = 0; t < zopt_threads; t++) {
- if (t < zopt_datasets && ztest_dataset_open(zs, t) != 0)
+ for (int t = 0; t < ztest_opts.zo_threads; t++) {
+ if (t < ztest_opts.zo_datasets &&
+ ztest_dataset_open(t) != 0)
return;
VERIFY(thr_create(0, 0, ztest_thread, (void *)(uintptr_t)t,
THR_BOUND, &tid[t]) == 0);
@@ -5200,10 +5415,10 @@ ztest_run(ztest_shared_t *zs)
* Wait for all of the tests to complete. We go in reverse order
* so we don't close datasets while threads are still using them.
*/
- for (int t = zopt_threads - 1; t >= 0; t--) {
+ for (int t = ztest_opts.zo_threads - 1; t >= 0; t--) {
VERIFY(thr_join(tid[t], NULL, NULL) == 0);
- if (t < zopt_datasets)
- ztest_dataset_close(zs, t);
+ if (t < ztest_opts.zo_datasets)
+ ztest_dataset_close(t);
}
txg_wait_synced(spa_get_dsl(spa), 0);
@@ -5211,7 +5426,7 @@ ztest_run(ztest_shared_t *zs)
zs->zs_alloc = metaslab_class_get_alloc(spa_normal_class(spa));
zs->zs_space = metaslab_class_get_space(spa_normal_class(spa));
- umem_free(tid, zopt_threads * sizeof (thread_t));
+ umem_free(tid, ztest_opts.zo_threads * sizeof (thread_t));
/* Kill the resume thread */
ztest_exiting = B_TRUE;
@@ -5232,7 +5447,7 @@ ztest_run(ztest_shared_t *zs)
*/
mutex_enter(&spa_namespace_lock);
for (spa = spa_next(NULL); spa != NULL; spa = spa_next(spa))
- if (zopt_verbose > 3)
+ if (ztest_opts.zo_verbose > 3)
(void) printf("spa_next: found %s\n", spa_name(spa));
mutex_exit(&spa_namespace_lock);
@@ -5242,9 +5457,10 @@ ztest_run(ztest_shared_t *zs)
*/
if (ztest_random(2) == 0) {
char name[MAXNAMELEN];
- (void) snprintf(name, MAXNAMELEN, "%s_import", zs->zs_pool);
- ztest_spa_import_export(zs->zs_pool, name);
- ztest_spa_import_export(name, zs->zs_pool);
+ (void) snprintf(name, MAXNAMELEN, "%s_import",
+ ztest_opts.zo_pool);
+ ztest_spa_import_export(ztest_opts.zo_pool, name);
+ ztest_spa_import_export(name, ztest_opts.zo_pool);
}
kernel_fini();
@@ -5253,23 +5469,23 @@ ztest_run(ztest_shared_t *zs)
(void) _mutex_destroy(&zcl.zcl_callbacks_lock);
- (void) rwlock_destroy(&zs->zs_name_lock);
- (void) _mutex_destroy(&zs->zs_vdev_lock);
+ (void) rwlock_destroy(&ztest_name_lock);
+ (void) _mutex_destroy(&ztest_vdev_lock);
}
static void
-ztest_freeze(ztest_shared_t *zs)
+ztest_freeze(void)
{
- ztest_ds_t *zd = &zs->zs_zd[0];
+ ztest_ds_t *zd = &ztest_ds[0];
spa_t *spa;
int numloops = 0;
- if (zopt_verbose >= 3)
+ if (ztest_opts.zo_verbose >= 3)
(void) printf("testing spa_freeze()...\n");
kernel_init(FREAD | FWRITE);
- VERIFY3U(0, ==, spa_open(zs->zs_pool, &spa, FTAG));
- VERIFY3U(0, ==, ztest_dataset_open(zs, 0));
+ VERIFY3U(0, ==, spa_open(ztest_opts.zo_pool, &spa, FTAG));
+ VERIFY3U(0, ==, ztest_dataset_open(0));
/*
* Force the first log block to be transactionally allocated.
@@ -5296,7 +5512,8 @@ ztest_freeze(ztest_shared_t *zs)
* to increase well beyond the last synced value in the uberblock.
* The ZIL should be OK with that.
*/
- while (ztest_random(10) != 0 && numloops++ < zopt_maxloops) {
+ while (ztest_random(10) != 0 &&
+ numloops++ < ztest_opts.zo_maxloops) {
ztest_dmu_write_parallel(zd, 0);
ztest_dmu_object_alloc_free(zd, 0);
txg_wait_synced(spa_get_dsl(spa), 0);
@@ -5311,7 +5528,7 @@ ztest_freeze(ztest_shared_t *zs)
/*
* Close our dataset and close the pool.
*/
- ztest_dataset_close(zs, 0);
+ ztest_dataset_close(0);
spa_close(spa, FTAG);
kernel_fini();
@@ -5319,9 +5536,9 @@ ztest_freeze(ztest_shared_t *zs)
* Open and close the pool and dataset to induce log replay.
*/
kernel_init(FREAD | FWRITE);
- VERIFY3U(0, ==, spa_open(zs->zs_pool, &spa, FTAG));
- VERIFY3U(0, ==, ztest_dataset_open(zs, 0));
- ztest_dataset_close(zs, 0);
+ VERIFY3U(0, ==, spa_open(ztest_opts.zo_pool, &spa, FTAG));
+ VERIFY3U(0, ==, ztest_dataset_open(0));
+ ztest_dataset_close(0);
spa_close(spa, FTAG);
kernel_fini();
}
@@ -5356,15 +5573,11 @@ make_random_props()
{
nvlist_t *props;
- if (ztest_random(2) == 0)
- return (NULL);
-
VERIFY(nvlist_alloc(&props, NV_UNIQUE_NAME, 0) == 0);
+ if (ztest_random(2) == 0)
+ return (props);
VERIFY(nvlist_add_uint64(props, "autoreplace", 1) == 0);
- (void) printf("props:\n");
- dump_nvlist(props, 4);
-
return (props);
}
@@ -5378,38 +5591,212 @@ ztest_init(ztest_shared_t *zs)
spa_t *spa;
nvlist_t *nvroot, *props;
- VERIFY(_mutex_init(&zs->zs_vdev_lock, USYNC_THREAD, NULL) == 0);
- VERIFY(rwlock_init(&zs->zs_name_lock, USYNC_THREAD, NULL) == 0);
+ VERIFY(_mutex_init(&ztest_vdev_lock, USYNC_THREAD, NULL) == 0);
+ VERIFY(rwlock_init(&ztest_name_lock, USYNC_THREAD, NULL) == 0);
kernel_init(FREAD | FWRITE);
/*
* Create the storage pool.
*/
- (void) spa_destroy(zs->zs_pool);
+ (void) spa_destroy(ztest_opts.zo_pool);
ztest_shared->zs_vdev_next_leaf = 0;
zs->zs_splits = 0;
- zs->zs_mirrors = zopt_mirrors;
- nvroot = make_vdev_root(NULL, NULL, zopt_vdev_size, 0,
- 0, zopt_raidz, zs->zs_mirrors, 1);
+ zs->zs_mirrors = ztest_opts.zo_mirrors;
+ nvroot = make_vdev_root(NULL, NULL, ztest_opts.zo_vdev_size, 0,
+ 0, ztest_opts.zo_raidz, zs->zs_mirrors, 1);
props = make_random_props();
- VERIFY3U(0, ==, spa_create(zs->zs_pool, nvroot, props, NULL, NULL));
+ for (int i = 0; i < SPA_FEATURES; i++) {
+ char buf[1024];
+ (void) snprintf(buf, sizeof (buf), "feature@%s",
+ spa_feature_table[i].fi_uname);
+ VERIFY3U(0, ==, nvlist_add_uint64(props, buf, 0));
+ }
+ VERIFY3U(0, ==, spa_create(ztest_opts.zo_pool, nvroot, props,
+ NULL, NULL));
nvlist_free(nvroot);
- VERIFY3U(0, ==, spa_open(zs->zs_pool, &spa, FTAG));
- metaslab_sz = 1ULL << spa->spa_root_vdev->vdev_child[0]->vdev_ms_shift;
+ VERIFY3U(0, ==, spa_open(ztest_opts.zo_pool, &spa, FTAG));
+ zs->zs_metaslab_sz =
+ 1ULL << spa->spa_root_vdev->vdev_child[0]->vdev_ms_shift;
+
spa_close(spa, FTAG);
kernel_fini();
- ztest_run_zdb(zs->zs_pool);
+ ztest_run_zdb(ztest_opts.zo_pool);
+
+ ztest_freeze();
+
+ ztest_run_zdb(ztest_opts.zo_pool);
- ztest_freeze(zs);
+ (void) rwlock_destroy(&ztest_name_lock);
+ (void) _mutex_destroy(&ztest_vdev_lock);
+}
+
+static void
+setup_fds(void)
+{
+ int fd;
- ztest_run_zdb(zs->zs_pool);
+ char *tmp = tempnam(NULL, NULL);
+ fd = open(tmp, O_RDWR | O_CREAT, 0700);
+ ASSERT3U(fd, ==, ZTEST_FD_DATA);
+ (void) unlink(tmp);
+ free(tmp);
- (void) rwlock_destroy(&zs->zs_name_lock);
- (void) _mutex_destroy(&zs->zs_vdev_lock);
+ fd = open("/dev/urandom", O_RDONLY);
+ ASSERT3U(fd, ==, ZTEST_FD_RAND);
+}
+
+static int
+shared_data_size(ztest_shared_hdr_t *hdr)
+{
+ int size;
+
+ size = hdr->zh_hdr_size;
+ size += hdr->zh_opts_size;
+ size += hdr->zh_size;
+ size += hdr->zh_stats_size * hdr->zh_stats_count;
+ size += hdr->zh_ds_size * hdr->zh_ds_count;
+
+ return (size);
+}
+
+static void
+setup_hdr(void)
+{
+ int size;
+ ztest_shared_hdr_t *hdr;
+
+ hdr = (void *)mmap(0, P2ROUNDUP(sizeof (*hdr), getpagesize()),
+ PROT_READ | PROT_WRITE, MAP_SHARED, ZTEST_FD_DATA, 0);
+ ASSERT(hdr != MAP_FAILED);
+
+ VERIFY3U(0, ==, ftruncate(ZTEST_FD_DATA, sizeof (ztest_shared_hdr_t)));
+
+ hdr->zh_hdr_size = sizeof (ztest_shared_hdr_t);
+ hdr->zh_opts_size = sizeof (ztest_shared_opts_t);
+ hdr->zh_size = sizeof (ztest_shared_t);
+ hdr->zh_stats_size = sizeof (ztest_shared_callstate_t);
+ hdr->zh_stats_count = ZTEST_FUNCS;
+ hdr->zh_ds_size = sizeof (ztest_shared_ds_t);
+ hdr->zh_ds_count = ztest_opts.zo_datasets;
+
+ size = shared_data_size(hdr);
+ VERIFY3U(0, ==, ftruncate(ZTEST_FD_DATA, size));
+
+ (void) munmap((caddr_t)hdr, P2ROUNDUP(sizeof (*hdr), getpagesize()));
+}
+
+static void
+setup_data(void)
+{
+ int size, offset;
+ ztest_shared_hdr_t *hdr;
+ uint8_t *buf;
+
+ hdr = (void *)mmap(0, P2ROUNDUP(sizeof (*hdr), getpagesize()),
+ PROT_READ, MAP_SHARED, ZTEST_FD_DATA, 0);
+ ASSERT(hdr != MAP_FAILED);
+
+ size = shared_data_size(hdr);
+
+ (void) munmap((caddr_t)hdr, P2ROUNDUP(sizeof (*hdr), getpagesize()));
+ hdr = ztest_shared_hdr = (void *)mmap(0, P2ROUNDUP(size, getpagesize()),
+ PROT_READ | PROT_WRITE, MAP_SHARED, ZTEST_FD_DATA, 0);
+ ASSERT(hdr != MAP_FAILED);
+ buf = (uint8_t *)hdr;
+
+ offset = hdr->zh_hdr_size;
+ ztest_shared_opts = (void *)&buf[offset];
+ offset += hdr->zh_opts_size;
+ ztest_shared = (void *)&buf[offset];
+ offset += hdr->zh_size;
+ ztest_shared_callstate = (void *)&buf[offset];
+ offset += hdr->zh_stats_size * hdr->zh_stats_count;
+ ztest_shared_ds = (void *)&buf[offset];
+}
+
+static boolean_t
+exec_child(char *cmd, char *libpath, boolean_t ignorekill, int *statusp)
+{
+ pid_t pid;
+ int status;
+ char cmdbuf[MAXPATHLEN];
+
+ pid = fork();
+
+ if (cmd == NULL) {
+ (void) strlcpy(cmdbuf, getexecname(), sizeof (cmdbuf));
+ cmd = cmdbuf;
+ }
+
+ if (pid == -1)
+ fatal(1, "fork failed");
+
+ if (pid == 0) { /* child */
+ char *emptyargv[2] = { cmd, NULL };
+
+ struct rlimit rl = { 1024, 1024 };
+ (void) setrlimit(RLIMIT_NOFILE, &rl);
+ (void) enable_extended_FILE_stdio(-1, -1);
+ if (libpath != NULL)
+ VERIFY(0 == setenv("LD_LIBRARY_PATH", libpath, 1));
+ (void) execv(cmd, emptyargv);
+ ztest_dump_core = B_FALSE;
+ fatal(B_TRUE, "exec failed: %s", cmd);
+ }
+
+ while (waitpid(pid, &status, 0) != pid)
+ continue;
+ if (statusp != NULL)
+ *statusp = status;
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) != 0) {
+ (void) fprintf(stderr, "child exited with code %d\n",
+ WEXITSTATUS(status));
+ exit(2);
+ }
+ return (B_FALSE);
+ } else if (WIFSIGNALED(status)) {
+ if (!ignorekill || WTERMSIG(status) != SIGKILL) {
+ (void) fprintf(stderr, "child died with signal %d\n",
+ WTERMSIG(status));
+ exit(3);
+ }
+ return (B_TRUE);
+ } else {
+ (void) fprintf(stderr, "something strange happened to child\n");
+ exit(4);
+ /* NOTREACHED */
+ }
+}
+
+static void
+ztest_run_init(void)
+{
+ ztest_shared_t *zs = ztest_shared;
+
+ ASSERT(ztest_opts.zo_init != 0);
+
+ /*
+ * Blow away any existing copy of zpool.cache
+ */
+ (void) remove(spa_config_path);
+
+ /*
+ * Create and initialize our storage pool.
+ */
+ for (int i = 1; i <= ztest_opts.zo_init; i++) {
+ bzero(zs, sizeof (ztest_shared_t));
+ if (ztest_opts.zo_verbose >= 3 &&
+ ztest_opts.zo_init != 1) {
+ (void) printf("ztest_init(), pass %d\n", i);
+ }
+ ztest_init(zs);
+ }
}
int
@@ -5417,63 +5804,92 @@ main(int argc, char **argv)
{
int kills = 0;
int iters = 0;
+ int older = 0;
+ int newer = 0;
ztest_shared_t *zs;
- size_t shared_size;
ztest_info_t *zi;
+ ztest_shared_callstate_t *zc;
char timebuf[100];
char numbuf[6];
spa_t *spa;
+ char cmd[MAXNAMELEN];
+ boolean_t hasalt;
+
+ boolean_t ischild = (0 == lseek(ZTEST_FD_DATA, 0, SEEK_CUR));
+ ASSERT(ischild || errno == EBADF);
(void) setvbuf(stdout, NULL, _IOLBF, 0);
- ztest_random_fd = open("/dev/urandom", O_RDONLY);
+ if (!ischild) {
+ process_options(argc, argv);
- process_options(argc, argv);
+ setup_fds();
+ setup_hdr();
+ setup_data();
+ bcopy(&ztest_opts, ztest_shared_opts,
+ sizeof (*ztest_shared_opts));
+ } else {
+ setup_data();
+ bcopy(ztest_shared_opts, &ztest_opts, sizeof (ztest_opts));
+ }
+ ASSERT3U(ztest_opts.zo_datasets, ==, ztest_shared_hdr->zh_ds_count);
/* Override location of zpool.cache */
- (void) asprintf((char **)&spa_config_path, "%s/zpool.cache", zopt_dir);
+ (void) asprintf((char **)&spa_config_path, "%s/zpool.cache",
+ ztest_opts.zo_dir);
- /*
- * Blow away any existing copy of zpool.cache
- */
- if (zopt_init != 0)
- (void) remove(spa_config_path);
+ ztest_ds = umem_alloc(ztest_opts.zo_datasets * sizeof (ztest_ds_t),
+ UMEM_NOFAIL);
+ zs = ztest_shared;
+
+ if (ischild) {
+ metaslab_gang_bang = ztest_opts.zo_metaslab_gang_bang;
+ metaslab_df_alloc_threshold =
+ zs->zs_metaslab_df_alloc_threshold;
- shared_size = sizeof (*zs) + zopt_datasets * sizeof (ztest_ds_t);
+ if (zs->zs_do_init)
+ ztest_run_init();
+ else
+ ztest_run(zs);
+ exit(0);
+ }
- zs = ztest_shared = (void *)mmap(0,
- P2ROUNDUP(shared_size, getpagesize()),
- PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
+ hasalt = (strlen(ztest_opts.zo_alt_ztest) != 0);
- if (zopt_verbose >= 1) {
+ if (ztest_opts.zo_verbose >= 1) {
(void) printf("%llu vdevs, %d datasets, %d threads,"
" %llu seconds...\n",
- (u_longlong_t)zopt_vdevs, zopt_datasets, zopt_threads,
- (u_longlong_t)zopt_time);
+ (u_longlong_t)ztest_opts.zo_vdevs,
+ ztest_opts.zo_datasets,
+ ztest_opts.zo_threads,
+ (u_longlong_t)ztest_opts.zo_time);
}
- /*
- * Create and initialize our storage pool.
- */
- for (int i = 1; i <= zopt_init; i++) {
- bzero(zs, sizeof (ztest_shared_t));
- if (zopt_verbose >= 3 && zopt_init != 1)
- (void) printf("ztest_init(), pass %d\n", i);
- zs->zs_pool = zopt_pool;
- ztest_init(zs);
+ (void) strlcpy(cmd, getexecname(), sizeof (cmd));
+
+ zs->zs_do_init = B_TRUE;
+ if (strlen(ztest_opts.zo_alt_ztest) != 0) {
+ if (ztest_opts.zo_verbose >= 1) {
+ (void) printf("Executing older ztest for "
+ "initialization: %s\n", ztest_opts.zo_alt_ztest);
+ }
+ VERIFY(!exec_child(ztest_opts.zo_alt_ztest,
+ ztest_opts.zo_alt_libpath, B_FALSE, NULL));
+ } else {
+ VERIFY(!exec_child(NULL, NULL, B_FALSE, NULL));
}
+ zs->zs_do_init = B_FALSE;
- zs->zs_pool = zopt_pool;
zs->zs_proc_start = gethrtime();
- zs->zs_proc_stop = zs->zs_proc_start + zopt_time * NANOSEC;
+ zs->zs_proc_stop = zs->zs_proc_start + ztest_opts.zo_time * NANOSEC;
for (int f = 0; f < ZTEST_FUNCS; f++) {
- zi = &zs->zs_info[f];
- *zi = ztest_info[f];
+ zi = &ztest_info[f];
+ zc = ZTEST_GET_SHARED_CALLSTATE(f);
if (zs->zs_proc_start + zi->zi_interval[0] > zs->zs_proc_stop)
- zi->zi_call_next = UINT64_MAX;
+ zc->zc_next = UINT64_MAX;
else
- zi->zi_call_next = zs->zs_proc_start +
+ zc->zc_next = zs->zs_proc_start +
ztest_random(2 * zi->zi_interval[0] + 1);
}
@@ -5484,60 +5900,43 @@ main(int argc, char **argv)
*/
while (gethrtime() < zs->zs_proc_stop) {
int status;
- pid_t pid;
+ boolean_t killed;
/*
* Initialize the workload counters for each function.
*/
for (int f = 0; f < ZTEST_FUNCS; f++) {
- zi = &zs->zs_info[f];
- zi->zi_call_count = 0;
- zi->zi_call_time = 0;
+ zc = ZTEST_GET_SHARED_CALLSTATE(f);
+ zc->zc_count = 0;
+ zc->zc_time = 0;
}
/* Set the allocation switch size */
- metaslab_df_alloc_threshold = ztest_random(metaslab_sz / 4) + 1;
-
- pid = fork();
+ zs->zs_metaslab_df_alloc_threshold =
+ ztest_random(zs->zs_metaslab_sz / 4) + 1;
- if (pid == -1)
- fatal(1, "fork failed");
-
- if (pid == 0) { /* child */
- struct rlimit rl = { 1024, 1024 };
- (void) setrlimit(RLIMIT_NOFILE, &rl);
- (void) enable_extended_FILE_stdio(-1, -1);
- ztest_run(zs);
- exit(0);
- }
-
- while (waitpid(pid, &status, 0) != pid)
- continue;
-
- if (WIFEXITED(status)) {
- if (WEXITSTATUS(status) != 0) {
- (void) fprintf(stderr,
- "child exited with code %d\n",
- WEXITSTATUS(status));
- exit(2);
+ if (!hasalt || ztest_random(2) == 0) {
+ if (hasalt && ztest_opts.zo_verbose >= 1) {
+ (void) printf("Executing newer ztest: %s\n",
+ cmd);
}
- } else if (WIFSIGNALED(status)) {
- if (WTERMSIG(status) != SIGKILL) {
- (void) fprintf(stderr,
- "child died with signal %d\n",
- WTERMSIG(status));
- exit(3);
- }
- kills++;
+ newer++;
+ killed = exec_child(cmd, NULL, B_TRUE, &status);
} else {
- (void) fprintf(stderr, "something strange happened "
- "to child\n");
- exit(4);
+ if (hasalt && ztest_opts.zo_verbose >= 1) {
+ (void) printf("Executing older ztest: %s\n",
+ ztest_opts.zo_alt_ztest);
+ }
+ older++;
+ killed = exec_child(ztest_opts.zo_alt_ztest,
+ ztest_opts.zo_alt_libpath, B_TRUE, &status);
}
+ if (killed)
+ kills++;
iters++;
- if (zopt_verbose >= 1) {
+ if (ztest_opts.zo_verbose >= 1) {
hrtime_t now = gethrtime();
now = MIN(now, zs->zs_proc_stop);
@@ -5552,10 +5951,10 @@ main(int argc, char **argv)
100.0 * zs->zs_alloc / zs->zs_space,
numbuf,
100.0 * (now - zs->zs_proc_start) /
- (zopt_time * NANOSEC), timebuf);
+ (ztest_opts.zo_time * NANOSEC), timebuf);
}
- if (zopt_verbose >= 2) {
+ if (ztest_opts.zo_verbose >= 2) {
(void) printf("\nWorkload summary:\n\n");
(void) printf("%7s %9s %s\n",
"Calls", "Time", "Function");
@@ -5564,11 +5963,12 @@ main(int argc, char **argv)
for (int f = 0; f < ZTEST_FUNCS; f++) {
Dl_info dli;
- zi = &zs->zs_info[f];
- print_time(zi->zi_call_time, timebuf);
+ zi = &ztest_info[f];
+ zc = ZTEST_GET_SHARED_CALLSTATE(f);
+ print_time(zc->zc_time, timebuf);
(void) dladdr((void *)zi->zi_func, &dli);
(void) printf("%7llu %9s %s\n",
- (u_longlong_t)zi->zi_call_count, timebuf,
+ (u_longlong_t)zc->zc_count, timebuf,
dli.dli_sname);
}
(void) printf("\n");
@@ -5580,22 +5980,28 @@ main(int argc, char **argv)
* instead of 'ztest'. Do a blind rename in case this happened.
*/
kernel_init(FREAD);
- if (spa_open(zopt_pool, &spa, FTAG) == 0) {
+ if (spa_open(ztest_opts.zo_pool, &spa, FTAG) == 0) {
spa_close(spa, FTAG);
} else {
char tmpname[MAXNAMELEN];
kernel_fini();
kernel_init(FREAD | FWRITE);
(void) snprintf(tmpname, sizeof (tmpname), "%s_tmp",
- zopt_pool);
- (void) spa_rename(tmpname, zopt_pool);
+ ztest_opts.zo_pool);
+ (void) spa_rename(tmpname, ztest_opts.zo_pool);
}
kernel_fini();
- ztest_run_zdb(zopt_pool);
+ ztest_run_zdb(ztest_opts.zo_pool);
}
- if (zopt_verbose >= 1) {
+ if (ztest_opts.zo_verbose >= 1) {
+ if (hasalt) {
+ (void) printf("%d runs of older ztest: %s\n", older,
+ ztest_opts.zo_alt_ztest);
+ (void) printf("%d runs of newer ztest: %s\n", newer,
+ cmd);
+ }
(void) printf("%d killed, %d completed, %.0f%% kill rate\n",
kills, iters - kills, (100.0 * kills) / MAX(1, iters));
}
diff --git a/lib/libnvpair/libnvpair.c b/lib/libnvpair/libnvpair.c
index 16bce483bee5..c2e5a1b46dad 100644
--- a/lib/libnvpair/libnvpair.c
+++ b/lib/libnvpair/libnvpair.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
*/
#include <unistd.h>
@@ -803,6 +804,10 @@ dump_nvlist(nvlist_t *list, int indent)
while ((elem = nvlist_next_nvpair(list, elem)) != NULL) {
switch (nvpair_type(elem)) {
+ case DATA_TYPE_BOOLEAN:
+ (void) printf("%*s%s\n", indent, "", nvpair_name(elem));
+ break;
+
case DATA_TYPE_BOOLEAN_VALUE:
(void) nvpair_value_boolean_value(elem, &bool_value);
(void) printf("%*s%s: %s\n", indent, "",
diff --git a/lib/libuutil/common/uu_list.c b/lib/libuutil/common/uu_list.c
index 35c7ba800103..93795e5289b2 100644
--- a/lib/libuutil/common/uu_list.c
+++ b/lib/libuutil/common/uu_list.c
@@ -18,13 +18,14 @@
*
* CDDL HEADER END
*/
+
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2011 Jason King. All rights reserved.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include "libuutil_common.h"
#include <stdlib.h>
@@ -318,6 +319,8 @@ uu_list_find(uu_list_t *lp, void *elem, void *private, uu_list_index_t *out)
uu_compare_fn_t *func = lp->ul_pool->ulp_cmp;
uu_list_node_impl_t *np;
+ uu_set_error(UU_ERROR_NONE);
+
if (func == NULL) {
if (out != NULL)
*out = 0;
diff --git a/lib/libzfs/common/libzfs.h b/lib/libzfs/common/libzfs.h
index ea34cc9efa31..4dc039c7739e 100644
--- a/lib/libzfs/common/libzfs.h
+++ b/lib/libzfs/common/libzfs.h
@@ -21,6 +21,9 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
*/
#ifndef _LIBZFS_H
@@ -229,6 +232,8 @@ typedef struct splitflags {
*/
extern int zpool_scan(zpool_handle_t *, pool_scan_func_t);
extern int zpool_clear(zpool_handle_t *, const char *, nvlist_t *);
+extern int zpool_reguid(zpool_handle_t *);
+extern int zpool_reopen(zpool_handle_t *);
extern int zpool_vdev_online(zpool_handle_t *, const char *, int,
vdev_state_t *);
@@ -286,6 +291,15 @@ typedef enum {
ZPOOL_STATUS_BAD_LOG, /* cannot read log chain(s) */
/*
+ * If the pool has unsupported features but can still be opened in
+ * read-only mode, its status is ZPOOL_STATUS_UNSUP_FEAT_WRITE. If the
+ * pool has unsupported features but cannot be opened at all, its
+ * status is ZPOOL_STATUS_UNSUP_FEAT_READ.
+ */
+ ZPOOL_STATUS_UNSUP_FEAT_READ, /* unsupported features for read */
+ ZPOOL_STATUS_UNSUP_FEAT_WRITE, /* unsupported features for write */
+
+ /*
* These faults have no corresponding message ID. At the time we are
* checking the status, the original reason for the FMA fault (I/O or
* checksum errors) has been lost.
@@ -317,6 +331,7 @@ extern void zpool_dump_ddt(const ddt_stat_t *dds, const ddt_histogram_t *ddh);
* Statistics and configuration functions.
*/
extern nvlist_t *zpool_get_config(zpool_handle_t *, nvlist_t **);
+extern nvlist_t *zpool_get_features(zpool_handle_t *);
extern int zpool_refresh_stats(zpool_handle_t *, boolean_t *);
extern int zpool_get_errlog(zpool_handle_t *, nvlist_t **);
@@ -329,6 +344,7 @@ extern int zpool_import(libzfs_handle_t *, nvlist_t *, const char *,
char *altroot);
extern int zpool_import_props(libzfs_handle_t *, nvlist_t *, const char *,
nvlist_t *, int);
+extern void zpool_print_unsup_feat(nvlist_t *config);
/*
* Search for pools to import
@@ -380,6 +396,7 @@ extern void zpool_explain_recover(libzfs_handle_t *, const char *, int,
* underlying datasets, only the references to them.
*/
extern zfs_handle_t *zfs_open(libzfs_handle_t *, const char *, int);
+extern zfs_handle_t *zfs_handle_dup(zfs_handle_t *);
extern void zfs_close(zfs_handle_t *);
extern zfs_type_t zfs_get_type(const zfs_handle_t *);
extern const char *zfs_get_name(const zfs_handle_t *);
@@ -413,12 +430,22 @@ extern int zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname,
uint64_t *propvalue);
extern int zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
char *propbuf, int proplen, boolean_t literal);
+extern int zfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname,
+ uint64_t *propvalue);
+extern int zfs_prop_get_written(zfs_handle_t *zhp, const char *propname,
+ char *propbuf, int proplen, boolean_t literal);
+extern int zfs_prop_get_feature(zfs_handle_t *zhp, const char *propname,
+ char *buf, size_t len);
+extern int zfs_get_snapused_int(zfs_handle_t *firstsnap, zfs_handle_t *lastsnap,
+ uint64_t *usedp);
extern uint64_t zfs_prop_get_int(zfs_handle_t *, zfs_prop_t);
extern int zfs_prop_inherit(zfs_handle_t *, const char *, boolean_t);
extern const char *zfs_prop_values(zfs_prop_t);
extern int zfs_prop_is_string(zfs_prop_t prop);
extern nvlist_t *zfs_get_user_props(zfs_handle_t *);
extern nvlist_t *zfs_get_recvd_props(zfs_handle_t *);
+extern nvlist_t *zfs_get_clones_nvl(zfs_handle_t *);
+
typedef struct zprop_list {
int pl_prop;
@@ -436,10 +463,19 @@ extern void zfs_prune_proplist(zfs_handle_t *, uint8_t *);
#define ZFS_MOUNTPOINT_NONE "none"
#define ZFS_MOUNTPOINT_LEGACY "legacy"
+#define ZFS_FEATURE_DISABLED "disabled"
+#define ZFS_FEATURE_ENABLED "enabled"
+#define ZFS_FEATURE_ACTIVE "active"
+
+#define ZFS_UNSUPPORTED_INACTIVE "inactive"
+#define ZFS_UNSUPPORTED_READONLY "readonly"
+
/*
* zpool property management
*/
extern int zpool_expand_proplist(zpool_handle_t *, zprop_list_t **);
+extern int zpool_prop_get_feature(zpool_handle_t *, const char *, char *,
+ size_t);
extern const char *zpool_prop_default_string(zpool_prop_t);
extern uint64_t zpool_prop_default_numeric(zpool_prop_t);
extern const char *zpool_prop_column_name(zpool_prop_t);
@@ -493,6 +529,7 @@ extern int zfs_iter_dependents(zfs_handle_t *, boolean_t, zfs_iter_f, void *);
extern int zfs_iter_filesystems(zfs_handle_t *, zfs_iter_f, void *);
extern int zfs_iter_snapshots(zfs_handle_t *, zfs_iter_f, void *);
extern int zfs_iter_snapshots_sorted(zfs_handle_t *, zfs_iter_f, void *);
+extern int zfs_iter_snapspec(zfs_handle_t *, const char *, zfs_iter_f, void *);
typedef struct get_all_cb {
zfs_handle_t **cb_handles;
@@ -513,79 +550,92 @@ extern int zfs_create(libzfs_handle_t *, const char *, zfs_type_t,
extern int zfs_create_ancestors(libzfs_handle_t *, const char *);
extern int zfs_destroy(zfs_handle_t *, boolean_t);
extern int zfs_destroy_snaps(zfs_handle_t *, char *, boolean_t);
+extern int zfs_destroy_snaps_nvl(zfs_handle_t *, nvlist_t *, boolean_t);
extern int zfs_clone(zfs_handle_t *, const char *, nvlist_t *);
extern int zfs_snapshot(libzfs_handle_t *, const char *, boolean_t, nvlist_t *);
extern int zfs_rollback(zfs_handle_t *, zfs_handle_t *, boolean_t);
-extern int zfs_rename(zfs_handle_t *, const char *, boolean_t);
+extern int zfs_rename(zfs_handle_t *, const char *, boolean_t, boolean_t);
typedef struct sendflags {
/* print informational messages (ie, -v was specified) */
- int verbose : 1;
+ boolean_t verbose;
/* recursive send (ie, -R) */
- int replicate : 1;
+ boolean_t replicate;
/* for incrementals, do all intermediate snapshots */
- int doall : 1; /* (ie, -I) */
+ boolean_t doall;
/* if dataset is a clone, do incremental from its origin */
- int fromorigin : 1;
+ boolean_t fromorigin;
/* do deduplication */
- int dedup : 1;
+ boolean_t dedup;
/* send properties (ie, -p) */
- int props : 1;
+ boolean_t props;
+
+ /* do not send (no-op, ie. -n) */
+ boolean_t dryrun;
+
+ /* parsable verbose output (ie. -P) */
+ boolean_t parsable;
+
+ /* show progress (ie. -v) */
+ boolean_t progress;
} sendflags_t;
typedef boolean_t (snapfilter_cb_t)(zfs_handle_t *, void *);
-extern int zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
- sendflags_t flags, int outfd, snapfilter_cb_t filter_func,
- void *cb_arg, nvlist_t **debugnvp);
+extern int zfs_send(zfs_handle_t *, const char *, const char *,
+ sendflags_t *, int, snapfilter_cb_t, void *, nvlist_t **);
extern int zfs_promote(zfs_handle_t *);
extern int zfs_hold(zfs_handle_t *, const char *, const char *, boolean_t,
boolean_t, boolean_t, int, uint64_t, uint64_t);
extern int zfs_release(zfs_handle_t *, const char *, const char *, boolean_t);
+extern int zfs_get_holds(zfs_handle_t *, nvlist_t **);
extern uint64_t zvol_volsize_to_reservation(uint64_t, nvlist_t *);
typedef int (*zfs_userspace_cb_t)(void *arg, const char *domain,
uid_t rid, uint64_t space);
-extern int zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
- zfs_userspace_cb_t func, void *arg);
+extern int zfs_userspace(zfs_handle_t *, zfs_userquota_prop_t,
+ zfs_userspace_cb_t, void *);
+
+extern int zfs_get_fsacl(zfs_handle_t *, nvlist_t **);
+extern int zfs_set_fsacl(zfs_handle_t *, boolean_t, nvlist_t *);
typedef struct recvflags {
/* print informational messages (ie, -v was specified) */
- int verbose : 1;
+ boolean_t verbose;
/* the destination is a prefix, not the exact fs (ie, -d) */
- int isprefix : 1;
+ boolean_t isprefix;
/*
* Only the tail of the sent snapshot path is appended to the
* destination to determine the received snapshot name (ie, -e).
*/
- int istail : 1;
+ boolean_t istail;
/* do not actually do the recv, just check if it would work (ie, -n) */
- int dryrun : 1;
+ boolean_t dryrun;
/* rollback/destroy filesystems as necessary (eg, -F) */
- int force : 1;
+ boolean_t force;
/* set "canmount=off" on all modified filesystems */
- int canmountoff : 1;
+ boolean_t canmountoff;
/* byteswap flag is used internally; callers need not specify */
- int byteswap : 1;
+ boolean_t byteswap;
/* do not mount file systems as they are extracted (private) */
- int nomount : 1;
+ boolean_t nomount;
} recvflags_t;
-extern int zfs_receive(libzfs_handle_t *, const char *, recvflags_t,
+extern int zfs_receive(libzfs_handle_t *, const char *, recvflags_t *,
int, avl_tree_t *);
typedef enum diff_flags {
diff --git a/lib/libzfs/common/libzfs_config.c b/lib/libzfs/common/libzfs_config.c
index dc27238c9cf3..f756da262d84 100644
--- a/lib/libzfs/common/libzfs_config.c
+++ b/lib/libzfs/common/libzfs_config.c
@@ -18,12 +18,17 @@
*
* CDDL HEADER END
*/
+
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ */
+
+/*
* The pool configuration repository is stored in /etc/zfs/zpool.cache as a
* single packed nvlist. While it would be nice to just read in this
* file from userland, this wouldn't work from a local zone. So we have to have
@@ -218,6 +223,36 @@ zpool_get_config(zpool_handle_t *zhp, nvlist_t **oldconfig)
}
/*
+ * Retrieves a list of enabled features and their refcounts and caches it in
+ * the pool handle.
+ */
+nvlist_t *
+zpool_get_features(zpool_handle_t *zhp)
+{
+ nvlist_t *config, *features;
+
+ config = zpool_get_config(zhp, NULL);
+
+ if (config == NULL || !nvlist_exists(config,
+ ZPOOL_CONFIG_FEATURE_STATS)) {
+ int error;
+ boolean_t missing = B_FALSE;
+
+ error = zpool_refresh_stats(zhp, &missing);
+
+ if (error != 0 || missing)
+ return (NULL);
+
+ config = zpool_get_config(zhp, NULL);
+ }
+
+ verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_FEATURE_STATS,
+ &features) == 0);
+
+ return (features);
+}
+
+/*
* Refresh the vdev statistics associated with the given pool. This is used in
* iostat to show configuration changes and determine the delta from the last
* time the function was called. This function can fail, in case the pool has
diff --git a/lib/libzfs/common/libzfs_dataset.c b/lib/libzfs/common/libzfs_dataset.c
index b7c1360db4b7..c1767cb910c0 100644
--- a/lib/libzfs/common/libzfs_dataset.c
+++ b/lib/libzfs/common/libzfs_dataset.c
@@ -21,6 +21,9 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2010 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2012 DEY Storage Systems, Inc. All rights reserved.
*/
#include <ctype.h>
@@ -132,6 +135,7 @@ zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
namecheck_err_t why;
char what;
+ (void) zfs_prop_get_table();
if (dataset_namecheck(path, &why, &what) != 0) {
if (hdl != NULL) {
switch (why) {
@@ -493,7 +497,7 @@ make_dataset_handle(libzfs_handle_t *hdl, const char *path)
return (zhp);
}
-static zfs_handle_t *
+zfs_handle_t *
make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc)
{
zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
@@ -510,6 +514,53 @@ make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc)
return (zhp);
}
+zfs_handle_t *
+zfs_handle_dup(zfs_handle_t *zhp_orig)
+{
+ zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
+
+ if (zhp == NULL)
+ return (NULL);
+
+ zhp->zfs_hdl = zhp_orig->zfs_hdl;
+ zhp->zpool_hdl = zhp_orig->zpool_hdl;
+ (void) strlcpy(zhp->zfs_name, zhp_orig->zfs_name,
+ sizeof (zhp->zfs_name));
+ zhp->zfs_type = zhp_orig->zfs_type;
+ zhp->zfs_head_type = zhp_orig->zfs_head_type;
+ zhp->zfs_dmustats = zhp_orig->zfs_dmustats;
+ if (zhp_orig->zfs_props != NULL) {
+ if (nvlist_dup(zhp_orig->zfs_props, &zhp->zfs_props, 0) != 0) {
+ (void) no_memory(zhp->zfs_hdl);
+ zfs_close(zhp);
+ return (NULL);
+ }
+ }
+ if (zhp_orig->zfs_user_props != NULL) {
+ if (nvlist_dup(zhp_orig->zfs_user_props,
+ &zhp->zfs_user_props, 0) != 0) {
+ (void) no_memory(zhp->zfs_hdl);
+ zfs_close(zhp);
+ return (NULL);
+ }
+ }
+ if (zhp_orig->zfs_recvd_props != NULL) {
+ if (nvlist_dup(zhp_orig->zfs_recvd_props,
+ &zhp->zfs_recvd_props, 0)) {
+ (void) no_memory(zhp->zfs_hdl);
+ zfs_close(zhp);
+ return (NULL);
+ }
+ }
+ zhp->zfs_mntcheck = zhp_orig->zfs_mntcheck;
+ if (zhp_orig->zfs_mntopts != NULL) {
+ zhp->zfs_mntopts = zfs_strdup(zhp_orig->zfs_hdl,
+ zhp_orig->zfs_mntopts);
+ }
+ zhp->zfs_props_table = zhp_orig->zfs_props_table;
+ return (zhp);
+}
+
/*
* Opens the given snapshot, filesystem, or volume. The 'types'
* argument is a mask of acceptable types. The function will print an
@@ -873,6 +924,12 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
goto error;
}
continue;
+ } else if (prop == ZPROP_INVAL && zfs_prop_written(propname)) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "'%s' is readonly"),
+ propname);
+ (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
+ goto error;
}
if (prop == ZPROP_INVAL) {
@@ -1846,8 +1903,6 @@ zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf,
err = zfs_prop_get(zhp, prop, propbuf, proplen,
NULL, NULL, 0, literal);
zfs_unset_recvd_props_mode(zhp, &cookie);
- } else if (zfs_prop_userquota(propname)) {
- return (-1);
} else {
nvlist_t *propval;
char *recvdval;
@@ -1862,6 +1917,120 @@ zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf,
return (err == 0 ? 0 : -1);
}
+static int
+get_clones_string(zfs_handle_t *zhp, char *propbuf, size_t proplen)
+{
+ nvlist_t *value;
+ nvpair_t *pair;
+
+ value = zfs_get_clones_nvl(zhp);
+ if (value == NULL)
+ return (-1);
+
+ propbuf[0] = '\0';
+ for (pair = nvlist_next_nvpair(value, NULL); pair != NULL;
+ pair = nvlist_next_nvpair(value, pair)) {
+ if (propbuf[0] != '\0')
+ (void) strlcat(propbuf, ",", proplen);
+ (void) strlcat(propbuf, nvpair_name(pair), proplen);
+ }
+
+ return (0);
+}
+
+struct get_clones_arg {
+ uint64_t numclones;
+ nvlist_t *value;
+ const char *origin;
+ char buf[ZFS_MAXNAMELEN];
+};
+
+int
+get_clones_cb(zfs_handle_t *zhp, void *arg)
+{
+ struct get_clones_arg *gca = arg;
+
+ if (gca->numclones == 0) {
+ zfs_close(zhp);
+ return (0);
+ }
+
+ if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, gca->buf, sizeof (gca->buf),
+ NULL, NULL, 0, B_TRUE) != 0)
+ goto out;
+ if (strcmp(gca->buf, gca->origin) == 0) {
+ if (nvlist_add_boolean(gca->value, zfs_get_name(zhp)) != 0) {
+ zfs_close(zhp);
+ return (no_memory(zhp->zfs_hdl));
+ }
+ gca->numclones--;
+ }
+
+out:
+ (void) zfs_iter_children(zhp, get_clones_cb, gca);
+ zfs_close(zhp);
+ return (0);
+}
+
+nvlist_t *
+zfs_get_clones_nvl(zfs_handle_t *zhp)
+{
+ nvlist_t *nv, *value;
+
+ if (nvlist_lookup_nvlist(zhp->zfs_props,
+ zfs_prop_to_name(ZFS_PROP_CLONES), &nv) != 0) {
+ struct get_clones_arg gca;
+
+ /*
+ * if this is a snapshot, then the kernel wasn't able
+ * to get the clones. Do it by slowly iterating.
+ */
+ if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT)
+ return (NULL);
+ if (nvlist_alloc(&nv, NV_UNIQUE_NAME, 0) != 0)
+ return (NULL);
+ if (nvlist_alloc(&value, NV_UNIQUE_NAME, 0) != 0) {
+ nvlist_free(nv);
+ return (NULL);
+ }
+
+ gca.numclones = zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES);
+ gca.value = value;
+ gca.origin = zhp->zfs_name;
+
+ if (gca.numclones != 0) {
+ zfs_handle_t *root;
+ char pool[ZFS_MAXNAMELEN];
+ char *cp = pool;
+
+ /* get the pool name */
+ (void) strlcpy(pool, zhp->zfs_name, sizeof (pool));
+ (void) strsep(&cp, "/@");
+ root = zfs_open(zhp->zfs_hdl, pool,
+ ZFS_TYPE_FILESYSTEM);
+
+ (void) get_clones_cb(root, &gca);
+ }
+
+ if (gca.numclones != 0 ||
+ nvlist_add_nvlist(nv, ZPROP_VALUE, value) != 0 ||
+ nvlist_add_nvlist(zhp->zfs_props,
+ zfs_prop_to_name(ZFS_PROP_CLONES), nv) != 0) {
+ nvlist_free(nv);
+ nvlist_free(value);
+ return (NULL);
+ }
+ nvlist_free(nv);
+ nvlist_free(value);
+ verify(0 == nvlist_lookup_nvlist(zhp->zfs_props,
+ zfs_prop_to_name(ZFS_PROP_CLONES), &nv));
+ }
+
+ verify(nvlist_lookup_nvlist(nv, ZPROP_VALUE, &value) == 0);
+
+ return (value);
+}
+
/*
* Retrieve a property from the given object. If 'literal' is specified, then
* numbers are left as exact values. Otherwise, numbers are converted to a
@@ -1990,6 +2159,11 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
return (-1);
break;
+ case ZFS_PROP_CLONES:
+ if (get_clones_string(zhp, propbuf, proplen) != 0)
+ return (-1);
+ break;
+
case ZFS_PROP_QUOTA:
case ZFS_PROP_REFQUOTA:
case ZFS_PROP_RESERVATION:
@@ -2018,6 +2192,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
}
break;
+ case ZFS_PROP_REFRATIO:
case ZFS_PROP_COMPRESSRATIO:
if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
return (-1);
@@ -2106,6 +2281,17 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
}
break;
+ case ZFS_PROP_GUID:
+ /*
+ * GUIDs are stored as numbers, but they are identifiers.
+ * We don't want them to be pretty printed, because pretty
+ * printing mangles the ID into a truncated and useless value.
+ */
+ if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
+ return (-1);
+ (void) snprintf(propbuf, proplen, "%llu", (u_longlong_t)val);
+ break;
+
default:
switch (zfs_prop_get_type(prop)) {
case PROP_TYPE_NUMBER:
@@ -2349,7 +2535,7 @@ zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname,
int err;
zfs_cmd_t zc = { 0 };
- (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
err = userquota_propname_decode(propname,
zfs_prop_get_int(zhp, ZFS_PROP_ZONED),
@@ -2401,144 +2587,95 @@ zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
return (0);
}
-/*
- * Returns the name of the given zfs handle.
- */
-const char *
-zfs_get_name(const zfs_handle_t *zhp)
+int
+zfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname,
+ uint64_t *propvalue)
{
- return (zhp->zfs_name);
-}
+ int err;
+ zfs_cmd_t zc = { 0 };
+ const char *snapname;
-/*
- * Returns the type of the given zfs handle.
- */
-zfs_type_t
-zfs_get_type(const zfs_handle_t *zhp)
-{
- return (zhp->zfs_type);
-}
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-static int
-zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc)
-{
- int rc;
- uint64_t orig_cookie;
+ snapname = strchr(propname, '@') + 1;
+ if (strchr(snapname, '@')) {
+ (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
+ } else {
+ /* snapname is the short name, append it to zhp's fsname */
+ char *cp;
+
+ (void) strlcpy(zc.zc_value, zhp->zfs_name,
+ sizeof (zc.zc_value));
+ cp = strchr(zc.zc_value, '@');
+ if (cp != NULL)
+ *cp = '\0';
+ (void) strlcat(zc.zc_value, "@", sizeof (zc.zc_value));
+ (void) strlcat(zc.zc_value, snapname, sizeof (zc.zc_value));
+ }
- orig_cookie = zc->zc_cookie;
-top:
- (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
- rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc);
+ err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_WRITTEN, &zc);
+ if (err)
+ return (err);
- if (rc == -1) {
- switch (errno) {
- case ENOMEM:
- /* expand nvlist memory and try again */
- if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) {
- zcmd_free_nvlists(zc);
- return (-1);
- }
- zc->zc_cookie = orig_cookie;
- goto top;
- /*
- * An errno value of ESRCH indicates normal completion.
- * If ENOENT is returned, then the underlying dataset
- * has been removed since we obtained the handle.
- */
- case ESRCH:
- case ENOENT:
- rc = 1;
- break;
- default:
- rc = zfs_standard_error(zhp->zfs_hdl, errno,
- dgettext(TEXT_DOMAIN,
- "cannot iterate filesystems"));
- break;
- }
- }
- return (rc);
+ *propvalue = zc.zc_cookie;
+ return (0);
}
-/*
- * Iterate over all child filesystems
- */
int
-zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
+zfs_prop_get_written(zfs_handle_t *zhp, const char *propname,
+ char *propbuf, int proplen, boolean_t literal)
{
- zfs_cmd_t zc = { 0 };
- zfs_handle_t *nzhp;
- int ret;
+ int err;
+ uint64_t propvalue;
- if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM)
- return (0);
+ err = zfs_prop_get_written_int(zhp, propname, &propvalue);
- if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
- return (-1);
-
- while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT,
- &zc)) == 0) {
- /*
- * Silently ignore errors, as the only plausible explanation is
- * that the pool has since been removed.
- */
- if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
- &zc)) == NULL) {
- continue;
- }
+ if (err)
+ return (err);
- if ((ret = func(nzhp, data)) != 0) {
- zcmd_free_nvlists(&zc);
- return (ret);
- }
+ if (literal) {
+ (void) snprintf(propbuf, proplen, "%llu", propvalue);
+ } else {
+ zfs_nicenum(propvalue, propbuf, proplen);
}
- zcmd_free_nvlists(&zc);
- return ((ret < 0) ? ret : 0);
+ return (0);
}
-/*
- * Iterate over all snapshots
- */
int
-zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data)
+zfs_get_snapused_int(zfs_handle_t *firstsnap, zfs_handle_t *lastsnap,
+ uint64_t *usedp)
{
+ int err;
zfs_cmd_t zc = { 0 };
- zfs_handle_t *nzhp;
- int ret;
- if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
- return (0);
+ (void) strlcpy(zc.zc_name, lastsnap->zfs_name, sizeof (zc.zc_name));
+ (void) strlcpy(zc.zc_value, firstsnap->zfs_name, sizeof (zc.zc_value));
- if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
- return (-1);
- while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT,
- &zc)) == 0) {
+ err = ioctl(lastsnap->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_SNAPS, &zc);
+ if (err)
+ return (err);
- if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
- &zc)) == NULL) {
- continue;
- }
+ *usedp = zc.zc_cookie;
- if ((ret = func(nzhp, data)) != 0) {
- zcmd_free_nvlists(&zc);
- return (ret);
- }
- }
- zcmd_free_nvlists(&zc);
- return ((ret < 0) ? ret : 0);
+ return (0);
}
/*
- * Iterate over all children, snapshots and filesystems
+ * Returns the name of the given zfs handle.
*/
-int
-zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
+const char *
+zfs_get_name(const zfs_handle_t *zhp)
{
- int ret;
-
- if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0)
- return (ret);
+ return (zhp->zfs_name);
+}
- return (zfs_iter_snapshots(zhp, func, data));
+/*
+ * Returns the type of the given zfs handle.
+ */
+zfs_type_t
+zfs_get_type(const zfs_handle_t *zhp)
+{
+ return (zhp->zfs_type);
}
/*
@@ -2564,18 +2701,19 @@ is_descendant(const char *ds1, const char *ds2)
/*
* Given a complete name, return just the portion that refers to the parent.
- * Can return NULL if this is a pool.
+ * Will return -1 if there is no parent (path is just the name of the
+ * pool).
*/
static int
parent_name(const char *path, char *buf, size_t buflen)
{
- char *loc;
+ char *slashp;
- if ((loc = strrchr(path, '/')) == NULL)
- return (-1);
+ (void) strlcpy(buf, path, buflen);
- (void) strncpy(buf, path, MIN(buflen, loc - path));
- buf[loc - path] = '\0';
+ if ((slashp = strrchr(buf, '/')) == NULL)
+ return (-1);
+ *slashp = '\0';
return (0);
}
@@ -2974,9 +3112,8 @@ zfs_destroy(zfs_handle_t *zhp, boolean_t defer)
}
struct destroydata {
- char *snapname;
- boolean_t gotone;
- boolean_t closezhp;
+ nvlist_t *nvl;
+ const char *snapname;
};
static int
@@ -2985,24 +3122,19 @@ zfs_check_snap_cb(zfs_handle_t *zhp, void *arg)
struct destroydata *dd = arg;
zfs_handle_t *szhp;
char name[ZFS_MAXNAMELEN];
- boolean_t closezhp = dd->closezhp;
int rv = 0;
- (void) strlcpy(name, zhp->zfs_name, sizeof (name));
- (void) strlcat(name, "@", sizeof (name));
- (void) strlcat(name, dd->snapname, sizeof (name));
+ (void) snprintf(name, sizeof (name),
+ "%s@%s", zhp->zfs_name, dd->snapname);
szhp = make_dataset_handle(zhp->zfs_hdl, name);
if (szhp) {
- dd->gotone = B_TRUE;
+ verify(nvlist_add_boolean(dd->nvl, name) == 0);
zfs_close(szhp);
}
- dd->closezhp = B_TRUE;
- if (!dd->gotone)
- rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, arg);
- if (closezhp)
- zfs_close(zhp);
+ rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, dd);
+ zfs_close(zhp);
return (rv);
}
@@ -3012,29 +3144,45 @@ zfs_check_snap_cb(zfs_handle_t *zhp, void *arg)
int
zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer)
{
- zfs_cmd_t zc = { 0 };
int ret;
struct destroydata dd = { 0 };
dd.snapname = snapname;
- (void) zfs_check_snap_cb(zhp, &dd);
+ verify(nvlist_alloc(&dd.nvl, NV_UNIQUE_NAME, 0) == 0);
+ (void) zfs_check_snap_cb(zfs_handle_dup(zhp), &dd);
- if (!dd.gotone) {
- return (zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,
+ if (nvlist_next_nvpair(dd.nvl, NULL) == NULL) {
+ ret = zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,
dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"),
- zhp->zfs_name, snapname));
+ zhp->zfs_name, snapname);
+ } else {
+ ret = zfs_destroy_snaps_nvl(zhp, dd.nvl, defer);
}
+ nvlist_free(dd.nvl);
+ return (ret);
+}
+
+/*
+ * Destroys all the snapshots named in the nvlist. They must be underneath
+ * the zhp (either snapshots of it, or snapshots of its descendants).
+ */
+int
+zfs_destroy_snaps_nvl(zfs_handle_t *zhp, nvlist_t *snaps, boolean_t defer)
+{
+ int ret;
+ zfs_cmd_t zc = { 0 };
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
+ if (zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, snaps) != 0)
+ return (-1);
zc.zc_defer_destroy = defer;
- ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc);
+ ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS_NVL, &zc);
if (ret != 0) {
char errbuf[1024];
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot destroy '%s@%s'"), zc.zc_name, snapname);
+ "cannot destroy snapshots in %s"), zc.zc_name);
switch (errno) {
case EEXIST:
@@ -3070,7 +3218,7 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"cannot create '%s'"), target);
- /* validate the target name */
+ /* validate the target/clone name */
if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE))
return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
@@ -3407,46 +3555,11 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
}
/*
- * Iterate over all dependents for a given dataset. This includes both
- * hierarchical dependents (children) and data dependents (snapshots and
- * clones). The bulk of the processing occurs in get_dependents() in
- * libzfs_graph.c.
- */
-int
-zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
- zfs_iter_f func, void *data)
-{
- char **dependents;
- size_t count;
- int i;
- zfs_handle_t *child;
- int ret = 0;
-
- if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name,
- &dependents, &count) != 0)
- return (-1);
-
- for (i = 0; i < count; i++) {
- if ((child = make_dataset_handle(zhp->zfs_hdl,
- dependents[i])) == NULL)
- continue;
-
- if ((ret = func(child, data)) != 0)
- break;
- }
-
- for (i = 0; i < count; i++)
- free(dependents[i]);
- free(dependents);
-
- return (ret);
-}
-
-/*
* Renames the given dataset.
*/
int
-zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive)
+zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
+ boolean_t force_unmount)
{
int ret;
zfs_cmd_t zc = { 0 };
@@ -3558,7 +3671,8 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive)
}
} else {
- if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0)) == NULL)
+ if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0,
+ force_unmount ? MS_FORCE : 0)) == NULL)
return (-1);
if (changelist_haszonedchild(cl)) {
@@ -3891,7 +4005,7 @@ zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
int error;
zfs_useracct_t buf[100];
- (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
zc.zc_objset_type = type;
zc.zc_nvlist_dst = (uintptr_t)buf;
@@ -4019,6 +4133,193 @@ zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
return (0);
}
+int
+zfs_get_fsacl(zfs_handle_t *zhp, nvlist_t **nvl)
+{
+ zfs_cmd_t zc = { 0 };
+ libzfs_handle_t *hdl = zhp->zfs_hdl;
+ int nvsz = 2048;
+ void *nvbuf;
+ int err = 0;
+ char errbuf[ZFS_MAXNAMELEN+32];
+
+ assert(zhp->zfs_type == ZFS_TYPE_VOLUME ||
+ zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
+
+tryagain:
+
+ nvbuf = malloc(nvsz);
+ if (nvbuf == NULL) {
+ err = (zfs_error(hdl, EZFS_NOMEM, strerror(errno)));
+ goto out;
+ }
+
+ zc.zc_nvlist_dst_size = nvsz;
+ zc.zc_nvlist_dst = (uintptr_t)nvbuf;
+
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, ZFS_MAXNAMELEN);
+
+ if (ioctl(hdl->libzfs_fd, ZFS_IOC_GET_FSACL, &zc) != 0) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "cannot get permissions on '%s'"),
+ zc.zc_name);
+ switch (errno) {
+ case ENOMEM:
+ free(nvbuf);
+ nvsz = zc.zc_nvlist_dst_size;
+ goto tryagain;
+
+ case ENOTSUP:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "pool must be upgraded"));
+ err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
+ break;
+ case EINVAL:
+ err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
+ break;
+ case ENOENT:
+ err = zfs_error(hdl, EZFS_NOENT, errbuf);
+ break;
+ default:
+ err = zfs_standard_error_fmt(hdl, errno, errbuf);
+ break;
+ }
+ } else {
+ /* success */
+ int rc = nvlist_unpack(nvbuf, zc.zc_nvlist_dst_size, nvl, 0);
+ if (rc) {
+ (void) snprintf(errbuf, sizeof (errbuf), dgettext(
+ TEXT_DOMAIN, "cannot get permissions on '%s'"),
+ zc.zc_name);
+ err = zfs_standard_error_fmt(hdl, rc, errbuf);
+ }
+ }
+
+ free(nvbuf);
+out:
+ return (err);
+}
+
+int
+zfs_set_fsacl(zfs_handle_t *zhp, boolean_t un, nvlist_t *nvl)
+{
+ zfs_cmd_t zc = { 0 };
+ libzfs_handle_t *hdl = zhp->zfs_hdl;
+ char *nvbuf;
+ char errbuf[ZFS_MAXNAMELEN+32];
+ size_t nvsz;
+ int err;
+
+ assert(zhp->zfs_type == ZFS_TYPE_VOLUME ||
+ zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
+
+ err = nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE);
+ assert(err == 0);
+
+ nvbuf = malloc(nvsz);
+
+ err = nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0);
+ assert(err == 0);
+
+ zc.zc_nvlist_src_size = nvsz;
+ zc.zc_nvlist_src = (uintptr_t)nvbuf;
+ zc.zc_perm_action = un;
+
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+
+ if (zfs_ioctl(hdl, ZFS_IOC_SET_FSACL, &zc) != 0) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "cannot set permissions on '%s'"),
+ zc.zc_name);
+ switch (errno) {
+ case ENOTSUP:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "pool must be upgraded"));
+ err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
+ break;
+ case EINVAL:
+ err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
+ break;
+ case ENOENT:
+ err = zfs_error(hdl, EZFS_NOENT, errbuf);
+ break;
+ default:
+ err = zfs_standard_error_fmt(hdl, errno, errbuf);
+ break;
+ }
+ }
+
+ free(nvbuf);
+
+ return (err);
+}
+
+int
+zfs_get_holds(zfs_handle_t *zhp, nvlist_t **nvl)
+{
+ zfs_cmd_t zc = { 0 };
+ libzfs_handle_t *hdl = zhp->zfs_hdl;
+ int nvsz = 2048;
+ void *nvbuf;
+ int err = 0;
+ char errbuf[ZFS_MAXNAMELEN+32];
+
+ assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
+
+tryagain:
+
+ nvbuf = malloc(nvsz);
+ if (nvbuf == NULL) {
+ err = (zfs_error(hdl, EZFS_NOMEM, strerror(errno)));
+ goto out;
+ }
+
+ zc.zc_nvlist_dst_size = nvsz;
+ zc.zc_nvlist_dst = (uintptr_t)nvbuf;
+
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, ZFS_MAXNAMELEN);
+
+ if (zfs_ioctl(hdl, ZFS_IOC_GET_HOLDS, &zc) != 0) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "cannot get holds for '%s'"),
+ zc.zc_name);
+ switch (errno) {
+ case ENOMEM:
+ free(nvbuf);
+ nvsz = zc.zc_nvlist_dst_size;
+ goto tryagain;
+
+ case ENOTSUP:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "pool must be upgraded"));
+ err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
+ break;
+ case EINVAL:
+ err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
+ break;
+ case ENOENT:
+ err = zfs_error(hdl, EZFS_NOENT, errbuf);
+ break;
+ default:
+ err = zfs_standard_error_fmt(hdl, errno, errbuf);
+ break;
+ }
+ } else {
+ /* success */
+ int rc = nvlist_unpack(nvbuf, zc.zc_nvlist_dst_size, nvl, 0);
+ if (rc) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "cannot get holds for '%s'"),
+ zc.zc_name);
+ err = zfs_standard_error_fmt(hdl, rc, errbuf);
+ }
+ }
+
+ free(nvbuf);
+out:
+ return (err);
+}
+
uint64_t
zvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props)
{
diff --git a/lib/libzfs/common/libzfs_graph.c b/lib/libzfs/common/libzfs_graph.c
deleted file mode 100644
index bc21c51ae26c..000000000000
--- a/lib/libzfs/common/libzfs_graph.c
+++ /dev/null
@@ -1,653 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-/*
- * Iterate over all children of the current object. This includes the normal
- * dataset hierarchy, but also arbitrary hierarchies due to clones. We want to
- * walk all datasets in the pool, and construct a directed graph of the form:
- *
- * home
- * |
- * +----+----+
- * | |
- * v v ws
- * bar baz |
- * | |
- * v v
- * @yesterday ----> foo
- *
- * In order to construct this graph, we have to walk every dataset in the pool,
- * because the clone parent is stored as a property of the child, not the
- * parent. The parent only keeps track of the number of clones.
- *
- * In the normal case (without clones) this would be rather expensive. To avoid
- * unnecessary computation, we first try a walk of the subtree hierarchy
- * starting from the initial node. At each dataset, we construct a node in the
- * graph and an edge leading from its parent. If we don't see any snapshots
- * with a non-zero clone count, then we are finished.
- *
- * If we do find a cloned snapshot, then we finish the walk of the current
- * subtree, but indicate that we need to do a complete walk. We then perform a
- * global walk of all datasets, avoiding the subtree we already processed.
- *
- * At the end of this, we'll end up with a directed graph of all relevant (and
- * possible some irrelevant) datasets in the system. We need to both find our
- * limiting subgraph and determine a safe ordering in which to destroy the
- * datasets. We do a topological ordering of our graph starting at our target
- * dataset, and then walk the results in reverse.
- *
- * It's possible for the graph to have cycles if, for example, the user renames
- * a clone to be the parent of its origin snapshot. The user can request to
- * generate an error in this case, or ignore the cycle and continue.
- *
- * When removing datasets, we want to destroy the snapshots in chronological
- * order (because this is the most efficient method). In order to accomplish
- * this, we store the creation transaction group with each vertex and keep each
- * vertex's edges sorted according to this value. The topological sort will
- * automatically walk the snapshots in the correct order.
- */
-
-#include <assert.h>
-#include <libintl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-#include <unistd.h>
-
-#include <libzfs.h>
-
-#include "libzfs_impl.h"
-#include "zfs_namecheck.h"
-
-#define MIN_EDGECOUNT 4
-
-/*
- * Vertex structure. Indexed by dataset name, this structure maintains a list
- * of edges to other vertices.
- */
-struct zfs_edge;
-typedef struct zfs_vertex {
- char zv_dataset[ZFS_MAXNAMELEN];
- struct zfs_vertex *zv_next;
- int zv_visited;
- uint64_t zv_txg;
- struct zfs_edge **zv_edges;
- int zv_edgecount;
- int zv_edgealloc;
-} zfs_vertex_t;
-
-enum {
- VISIT_SEEN = 1,
- VISIT_SORT_PRE,
- VISIT_SORT_POST
-};
-
-/*
- * Edge structure. Simply maintains a pointer to the destination vertex. There
- * is no need to store the source vertex, since we only use edges in the context
- * of the source vertex.
- */
-typedef struct zfs_edge {
- zfs_vertex_t *ze_dest;
- struct zfs_edge *ze_next;
-} zfs_edge_t;
-
-#define ZFS_GRAPH_SIZE 1027 /* this could be dynamic some day */
-
-/*
- * Graph structure. Vertices are maintained in a hash indexed by dataset name.
- */
-typedef struct zfs_graph {
- zfs_vertex_t **zg_hash;
- size_t zg_size;
- size_t zg_nvertex;
- const char *zg_root;
- int zg_clone_count;
-} zfs_graph_t;
-
-/*
- * Allocate a new edge pointing to the target vertex.
- */
-static zfs_edge_t *
-zfs_edge_create(libzfs_handle_t *hdl, zfs_vertex_t *dest)
-{
- zfs_edge_t *zep = zfs_alloc(hdl, sizeof (zfs_edge_t));
-
- if (zep == NULL)
- return (NULL);
-
- zep->ze_dest = dest;
-
- return (zep);
-}
-
-/*
- * Destroy an edge.
- */
-static void
-zfs_edge_destroy(zfs_edge_t *zep)
-{
- free(zep);
-}
-
-/*
- * Allocate a new vertex with the given name.
- */
-static zfs_vertex_t *
-zfs_vertex_create(libzfs_handle_t *hdl, const char *dataset)
-{
- zfs_vertex_t *zvp = zfs_alloc(hdl, sizeof (zfs_vertex_t));
-
- if (zvp == NULL)
- return (NULL);
-
- assert(strlen(dataset) < ZFS_MAXNAMELEN);
-
- (void) strlcpy(zvp->zv_dataset, dataset, sizeof (zvp->zv_dataset));
-
- if ((zvp->zv_edges = zfs_alloc(hdl,
- MIN_EDGECOUNT * sizeof (void *))) == NULL) {
- free(zvp);
- return (NULL);
- }
-
- zvp->zv_edgealloc = MIN_EDGECOUNT;
-
- return (zvp);
-}
-
-/*
- * Destroy a vertex. Frees up any associated edges.
- */
-static void
-zfs_vertex_destroy(zfs_vertex_t *zvp)
-{
- int i;
-
- for (i = 0; i < zvp->zv_edgecount; i++)
- zfs_edge_destroy(zvp->zv_edges[i]);
-
- free(zvp->zv_edges);
- free(zvp);
-}
-
-/*
- * Given a vertex, add an edge to the destination vertex.
- */
-static int
-zfs_vertex_add_edge(libzfs_handle_t *hdl, zfs_vertex_t *zvp,
- zfs_vertex_t *dest)
-{
- zfs_edge_t *zep = zfs_edge_create(hdl, dest);
-
- if (zep == NULL)
- return (-1);
-
- if (zvp->zv_edgecount == zvp->zv_edgealloc) {
- void *ptr;
-
- if ((ptr = zfs_realloc(hdl, zvp->zv_edges,
- zvp->zv_edgealloc * sizeof (void *),
- zvp->zv_edgealloc * 2 * sizeof (void *))) == NULL)
- return (-1);
-
- zvp->zv_edges = ptr;
- zvp->zv_edgealloc *= 2;
- }
-
- zvp->zv_edges[zvp->zv_edgecount++] = zep;
-
- return (0);
-}
-
-static int
-zfs_edge_compare(const void *a, const void *b)
-{
- const zfs_edge_t *ea = *((zfs_edge_t **)a);
- const zfs_edge_t *eb = *((zfs_edge_t **)b);
-
- if (ea->ze_dest->zv_txg < eb->ze_dest->zv_txg)
- return (-1);
- if (ea->ze_dest->zv_txg > eb->ze_dest->zv_txg)
- return (1);
- return (0);
-}
-
-/*
- * Sort the given vertex edges according to the creation txg of each vertex.
- */
-static void
-zfs_vertex_sort_edges(zfs_vertex_t *zvp)
-{
- if (zvp->zv_edgecount == 0)
- return;
-
- qsort(zvp->zv_edges, zvp->zv_edgecount, sizeof (void *),
- zfs_edge_compare);
-}
-
-/*
- * Construct a new graph object. We allow the size to be specified as a
- * parameter so in the future we can size the hash according to the number of
- * datasets in the pool.
- */
-static zfs_graph_t *
-zfs_graph_create(libzfs_handle_t *hdl, const char *dataset, size_t size)
-{
- zfs_graph_t *zgp = zfs_alloc(hdl, sizeof (zfs_graph_t));
-
- if (zgp == NULL)
- return (NULL);
-
- zgp->zg_size = size;
- if ((zgp->zg_hash = zfs_alloc(hdl,
- size * sizeof (zfs_vertex_t *))) == NULL) {
- free(zgp);
- return (NULL);
- }
-
- zgp->zg_root = dataset;
- zgp->zg_clone_count = 0;
-
- return (zgp);
-}
-
-/*
- * Destroy a graph object. We have to iterate over all the hash chains,
- * destroying each vertex in the process.
- */
-static void
-zfs_graph_destroy(zfs_graph_t *zgp)
-{
- int i;
- zfs_vertex_t *current, *next;
-
- for (i = 0; i < zgp->zg_size; i++) {
- current = zgp->zg_hash[i];
- while (current != NULL) {
- next = current->zv_next;
- zfs_vertex_destroy(current);
- current = next;
- }
- }
-
- free(zgp->zg_hash);
- free(zgp);
-}
-
-/*
- * Graph hash function. Classic bernstein k=33 hash function, taken from
- * usr/src/cmd/sgs/tools/common/strhash.c
- */
-static size_t
-zfs_graph_hash(zfs_graph_t *zgp, const char *str)
-{
- size_t hash = 5381;
- int c;
-
- while ((c = *str++) != 0)
- hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
-
- return (hash % zgp->zg_size);
-}
-
-/*
- * Given a dataset name, finds the associated vertex, creating it if necessary.
- */
-static zfs_vertex_t *
-zfs_graph_lookup(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset,
- uint64_t txg)
-{
- size_t idx = zfs_graph_hash(zgp, dataset);
- zfs_vertex_t *zvp;
-
- for (zvp = zgp->zg_hash[idx]; zvp != NULL; zvp = zvp->zv_next) {
- if (strcmp(zvp->zv_dataset, dataset) == 0) {
- if (zvp->zv_txg == 0)
- zvp->zv_txg = txg;
- return (zvp);
- }
- }
-
- if ((zvp = zfs_vertex_create(hdl, dataset)) == NULL)
- return (NULL);
-
- zvp->zv_next = zgp->zg_hash[idx];
- zvp->zv_txg = txg;
- zgp->zg_hash[idx] = zvp;
- zgp->zg_nvertex++;
-
- return (zvp);
-}
-
-/*
- * Given two dataset names, create an edge between them. For the source vertex,
- * mark 'zv_visited' to indicate that we have seen this vertex, and not simply
- * created it as a destination of another edge. If 'dest' is NULL, then this
- * is an individual vertex (i.e. the starting vertex), so don't add an edge.
- */
-static int
-zfs_graph_add(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *source,
- const char *dest, uint64_t txg)
-{
- zfs_vertex_t *svp, *dvp;
-
- if ((svp = zfs_graph_lookup(hdl, zgp, source, 0)) == NULL)
- return (-1);
- svp->zv_visited = VISIT_SEEN;
- if (dest != NULL) {
- dvp = zfs_graph_lookup(hdl, zgp, dest, txg);
- if (dvp == NULL)
- return (-1);
- if (zfs_vertex_add_edge(hdl, svp, dvp) != 0)
- return (-1);
- }
-
- return (0);
-}
-
-/*
- * Iterate over all children of the given dataset, adding any vertices
- * as necessary. Returns -1 if there was an error, or 0 otherwise.
- * This is a simple recursive algorithm - the ZFS namespace typically
- * is very flat. We manually invoke the necessary ioctl() calls to
- * avoid the overhead and additional semantics of zfs_open().
- */
-static int
-iterate_children(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
-{
- zfs_cmd_t zc = { 0 };
- zfs_vertex_t *zvp;
-
- /*
- * Look up the source vertex, and avoid it if we've seen it before.
- */
- zvp = zfs_graph_lookup(hdl, zgp, dataset, 0);
- if (zvp == NULL)
- return (-1);
- if (zvp->zv_visited == VISIT_SEEN)
- return (0);
-
- /*
- * Iterate over all children
- */
- for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
- ioctl(hdl->libzfs_fd, ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0;
- (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) {
- /*
- * Get statistics for this dataset, to determine the type of the
- * dataset and clone statistics. If this fails, the dataset has
- * since been removed, and we're pretty much screwed anyway.
- */
- zc.zc_objset_stats.dds_origin[0] = '\0';
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
- continue;
-
- if (zc.zc_objset_stats.dds_origin[0] != '\0') {
- if (zfs_graph_add(hdl, zgp,
- zc.zc_objset_stats.dds_origin, zc.zc_name,
- zc.zc_objset_stats.dds_creation_txg) != 0)
- return (-1);
- /*
- * Count origins only if they are contained in the graph
- */
- if (isa_child_of(zc.zc_objset_stats.dds_origin,
- zgp->zg_root))
- zgp->zg_clone_count--;
- }
-
- /*
- * Add an edge between the parent and the child.
- */
- if (zfs_graph_add(hdl, zgp, dataset, zc.zc_name,
- zc.zc_objset_stats.dds_creation_txg) != 0)
- return (-1);
-
- /*
- * Recursively visit child
- */
- if (iterate_children(hdl, zgp, zc.zc_name))
- return (-1);
- }
-
- /*
- * Now iterate over all snapshots.
- */
- bzero(&zc, sizeof (zc));
-
- for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
- ioctl(hdl->libzfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT, &zc) == 0;
- (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) {
-
- /*
- * Get statistics for this dataset, to determine the type of the
- * dataset and clone statistics. If this fails, the dataset has
- * since been removed, and we're pretty much screwed anyway.
- */
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
- continue;
-
- /*
- * Add an edge between the parent and the child.
- */
- if (zfs_graph_add(hdl, zgp, dataset, zc.zc_name,
- zc.zc_objset_stats.dds_creation_txg) != 0)
- return (-1);
-
- zgp->zg_clone_count += zc.zc_objset_stats.dds_num_clones;
- }
-
- zvp->zv_visited = VISIT_SEEN;
-
- return (0);
-}
-
-/*
- * Returns false if there are no snapshots with dependent clones in this
- * subtree or if all of those clones are also in this subtree. Returns
- * true if there is an error or there are external dependents.
- */
-static boolean_t
-external_dependents(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
-{
- zfs_cmd_t zc = { 0 };
-
- /*
- * Check whether this dataset is a clone or has clones since
- * iterate_children() only checks the children.
- */
- (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
- return (B_TRUE);
-
- if (zc.zc_objset_stats.dds_origin[0] != '\0') {
- if (zfs_graph_add(hdl, zgp,
- zc.zc_objset_stats.dds_origin, zc.zc_name,
- zc.zc_objset_stats.dds_creation_txg) != 0)
- return (B_TRUE);
- if (isa_child_of(zc.zc_objset_stats.dds_origin, dataset))
- zgp->zg_clone_count--;
- }
-
- if ((zc.zc_objset_stats.dds_num_clones) ||
- iterate_children(hdl, zgp, dataset))
- return (B_TRUE);
-
- return (zgp->zg_clone_count != 0);
-}
-
-/*
- * Construct a complete graph of all necessary vertices. First, iterate over
- * only our object's children. If no cloned snapshots are found, or all of
- * the cloned snapshots are in this subtree then return a graph of the subtree.
- * Otherwise, start at the root of the pool and iterate over all datasets.
- */
-static zfs_graph_t *
-construct_graph(libzfs_handle_t *hdl, const char *dataset)
-{
- zfs_graph_t *zgp = zfs_graph_create(hdl, dataset, ZFS_GRAPH_SIZE);
- int ret = 0;
-
- if (zgp == NULL)
- return (zgp);
-
- if ((strchr(dataset, '/') == NULL) ||
- (external_dependents(hdl, zgp, dataset))) {
- /*
- * Determine pool name and try again.
- */
- int len = strcspn(dataset, "/@") + 1;
- char *pool = zfs_alloc(hdl, len);
-
- if (pool == NULL) {
- zfs_graph_destroy(zgp);
- return (NULL);
- }
- (void) strlcpy(pool, dataset, len);
-
- if (iterate_children(hdl, zgp, pool) == -1 ||
- zfs_graph_add(hdl, zgp, pool, NULL, 0) != 0) {
- free(pool);
- zfs_graph_destroy(zgp);
- return (NULL);
- }
- free(pool);
- }
-
- if (ret == -1 || zfs_graph_add(hdl, zgp, dataset, NULL, 0) != 0) {
- zfs_graph_destroy(zgp);
- return (NULL);
- }
-
- return (zgp);
-}
-
-/*
- * Given a graph, do a recursive topological sort into the given array. This is
- * really just a depth first search, so that the deepest nodes appear first.
- * hijack the 'zv_visited' marker to avoid visiting the same vertex twice.
- */
-static int
-topo_sort(libzfs_handle_t *hdl, boolean_t allowrecursion, char **result,
- size_t *idx, zfs_vertex_t *zgv)
-{
- int i;
-
- if (zgv->zv_visited == VISIT_SORT_PRE && !allowrecursion) {
- /*
- * If we've already seen this vertex as part of our depth-first
- * search, then we have a cyclic dependency, and we must return
- * an error.
- */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "recursive dependency at '%s'"),
- zgv->zv_dataset);
- return (zfs_error(hdl, EZFS_RECURSIVE,
- dgettext(TEXT_DOMAIN,
- "cannot determine dependent datasets")));
- } else if (zgv->zv_visited >= VISIT_SORT_PRE) {
- /*
- * If we've already processed this as part of the topological
- * sort, then don't bother doing so again.
- */
- return (0);
- }
-
- zgv->zv_visited = VISIT_SORT_PRE;
-
- /* avoid doing a search if we don't have to */
- zfs_vertex_sort_edges(zgv);
- for (i = 0; i < zgv->zv_edgecount; i++) {
- if (topo_sort(hdl, allowrecursion, result, idx,
- zgv->zv_edges[i]->ze_dest) != 0)
- return (-1);
- }
-
- /* we may have visited this in the course of the above */
- if (zgv->zv_visited == VISIT_SORT_POST)
- return (0);
-
- if ((result[*idx] = zfs_alloc(hdl,
- strlen(zgv->zv_dataset) + 1)) == NULL)
- return (-1);
-
- (void) strcpy(result[*idx], zgv->zv_dataset);
- *idx += 1;
- zgv->zv_visited = VISIT_SORT_POST;
- return (0);
-}
-
-/*
- * The only public interface for this file. Do the dirty work of constructing a
- * child list for the given object. Construct the graph, do the toplogical
- * sort, and then return the array of strings to the caller.
- *
- * The 'allowrecursion' parameter controls behavior when cycles are found. If
- * it is set, the the cycle is ignored and the results returned as if the cycle
- * did not exist. If it is not set, then the routine will generate an error if
- * a cycle is found.
- */
-int
-get_dependents(libzfs_handle_t *hdl, boolean_t allowrecursion,
- const char *dataset, char ***result, size_t *count)
-{
- zfs_graph_t *zgp;
- zfs_vertex_t *zvp;
-
- if ((zgp = construct_graph(hdl, dataset)) == NULL)
- return (-1);
-
- if ((*result = zfs_alloc(hdl,
- zgp->zg_nvertex * sizeof (char *))) == NULL) {
- zfs_graph_destroy(zgp);
- return (-1);
- }
-
- if ((zvp = zfs_graph_lookup(hdl, zgp, dataset, 0)) == NULL) {
- free(*result);
- zfs_graph_destroy(zgp);
- return (-1);
- }
-
- *count = 0;
- if (topo_sort(hdl, allowrecursion, *result, count, zvp) != 0) {
- free(*result);
- zfs_graph_destroy(zgp);
- return (-1);
- }
-
- /*
- * Get rid of the last entry, which is our starting vertex and not
- * strictly a dependent.
- */
- assert(*count > 0);
- free((*result)[*count - 1]);
- (*count)--;
-
- zfs_graph_destroy(zgp);
-
- return (0);
-}
diff --git a/lib/libzfs/common/libzfs_impl.h b/lib/libzfs/common/libzfs_impl.h
index c9b09a205024..b1eae47ed204 100644
--- a/lib/libzfs/common/libzfs_impl.h
+++ b/lib/libzfs/common/libzfs_impl.h
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
*/
#ifndef _LIBFS_IMPL_H
@@ -115,7 +116,7 @@ struct zpool_handle {
diskaddr_t zpool_start_block;
};
-typedef enum {
+typedef enum {
PROTO_NFS = 0,
PROTO_SMB = 1,
PROTO_END = 2
@@ -147,6 +148,7 @@ int zpool_standard_error_fmt(libzfs_handle_t *, int, const char *, ...);
int get_dependents(libzfs_handle_t *, boolean_t, const char *, char ***,
size_t *);
+zfs_handle_t *make_dataset_handle_zc(libzfs_handle_t *, zfs_cmd_t *);
int zprop_parse_value(libzfs_handle_t *, nvpair_t *, int, zfs_type_t,
diff --git a/lib/libzfs/common/libzfs_import.c b/lib/libzfs/common/libzfs_import.c
index e1370350fd52..414aa2f747b1 100644
--- a/lib/libzfs/common/libzfs_import.c
+++ b/lib/libzfs/common/libzfs_import.c
@@ -20,6 +20,8 @@
*/
/*
* 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.
*/
/*
@@ -437,7 +439,7 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok)
uint_t i, nspares, nl2cache;
boolean_t config_seen;
uint64_t best_txg;
- char *name, *hostname;
+ char *name, *hostname, *comment;
uint64_t version, guid;
uint_t children = 0;
nvlist_t **child = NULL;
@@ -526,6 +528,7 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok)
* version
* pool guid
* name
+ * comment (if available)
* pool state
* hostid (if available)
* hostname (if available)
@@ -547,11 +550,24 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok)
if (nvlist_add_string(config,
ZPOOL_CONFIG_POOL_NAME, name) != 0)
goto nomem;
+
+ /*
+ * COMMENT is optional, don't bail if it's not
+ * there, instead, set it to NULL.
+ */
+ if (nvlist_lookup_string(tmp,
+ ZPOOL_CONFIG_COMMENT, &comment) != 0)
+ comment = NULL;
+ else if (nvlist_add_string(config,
+ ZPOOL_CONFIG_COMMENT, comment) != 0)
+ goto nomem;
+
verify(nvlist_lookup_uint64(tmp,
ZPOOL_CONFIG_POOL_STATE, &state) == 0);
if (nvlist_add_uint64(config,
ZPOOL_CONFIG_POOL_STATE, state) != 0)
goto nomem;
+
hostid = 0;
if (nvlist_lookup_uint64(tmp,
ZPOOL_CONFIG_HOSTID, &hostid) == 0) {
diff --git a/lib/libzfs/common/libzfs_iter.c b/lib/libzfs/common/libzfs_iter.c
new file mode 100644
index 000000000000..212383d0e6fb
--- /dev/null
+++ b/lib/libzfs/common/libzfs_iter.c
@@ -0,0 +1,462 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2010 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <libintl.h>
+#include <libzfs.h>
+
+#include "libzfs_impl.h"
+
+int
+zfs_iter_clones(zfs_handle_t *zhp, zfs_iter_f func, void *data)
+{
+ nvlist_t *nvl = zfs_get_clones_nvl(zhp);
+ nvpair_t *pair;
+
+ if (nvl == NULL)
+ return (0);
+
+ for (pair = nvlist_next_nvpair(nvl, NULL); pair != NULL;
+ pair = nvlist_next_nvpair(nvl, pair)) {
+ zfs_handle_t *clone = zfs_open(zhp->zfs_hdl, nvpair_name(pair),
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
+ if (clone != NULL) {
+ int err = func(clone, data);
+ if (err != 0)
+ return (err);
+ }
+ }
+ return (0);
+}
+
+static int
+zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc)
+{
+ int rc;
+ uint64_t orig_cookie;
+
+ orig_cookie = zc->zc_cookie;
+top:
+ (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
+ rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc);
+
+ if (rc == -1) {
+ switch (errno) {
+ case ENOMEM:
+ /* expand nvlist memory and try again */
+ if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) {
+ zcmd_free_nvlists(zc);
+ return (-1);
+ }
+ zc->zc_cookie = orig_cookie;
+ goto top;
+ /*
+ * An errno value of ESRCH indicates normal completion.
+ * If ENOENT is returned, then the underlying dataset
+ * has been removed since we obtained the handle.
+ */
+ case ESRCH:
+ case ENOENT:
+ rc = 1;
+ break;
+ default:
+ rc = zfs_standard_error(zhp->zfs_hdl, errno,
+ dgettext(TEXT_DOMAIN,
+ "cannot iterate filesystems"));
+ break;
+ }
+ }
+ return (rc);
+}
+
+/*
+ * Iterate over all child filesystems
+ */
+int
+zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
+{
+ zfs_cmd_t zc = { 0 };
+ zfs_handle_t *nzhp;
+ int ret;
+
+ if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM)
+ return (0);
+
+ if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
+ return (-1);
+
+ while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT,
+ &zc)) == 0) {
+ /*
+ * Silently ignore errors, as the only plausible explanation is
+ * that the pool has since been removed.
+ */
+ if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
+ &zc)) == NULL) {
+ continue;
+ }
+
+ if ((ret = func(nzhp, data)) != 0) {
+ zcmd_free_nvlists(&zc);
+ return (ret);
+ }
+ }
+ zcmd_free_nvlists(&zc);
+ return ((ret < 0) ? ret : 0);
+}
+
+/*
+ * Iterate over all snapshots
+ */
+int
+zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data)
+{
+ zfs_cmd_t zc = { 0 };
+ zfs_handle_t *nzhp;
+ int ret;
+
+ if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
+ return (0);
+
+ if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
+ return (-1);
+ while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT,
+ &zc)) == 0) {
+
+ if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
+ &zc)) == NULL) {
+ continue;
+ }
+
+ if ((ret = func(nzhp, data)) != 0) {
+ zcmd_free_nvlists(&zc);
+ return (ret);
+ }
+ }
+ zcmd_free_nvlists(&zc);
+ return ((ret < 0) ? ret : 0);
+}
+
+/*
+ * Routines for dealing with the sorted snapshot functionality
+ */
+typedef struct zfs_node {
+ zfs_handle_t *zn_handle;
+ avl_node_t zn_avlnode;
+} zfs_node_t;
+
+static int
+zfs_sort_snaps(zfs_handle_t *zhp, void *data)
+{
+ avl_tree_t *avl = data;
+ zfs_node_t *node;
+ zfs_node_t search;
+
+ search.zn_handle = zhp;
+ node = avl_find(avl, &search, NULL);
+ if (node) {
+ /*
+ * If this snapshot was renamed while we were creating the
+ * AVL tree, it's possible that we already inserted it under
+ * its old name. Remove the old handle before adding the new
+ * one.
+ */
+ zfs_close(node->zn_handle);
+ avl_remove(avl, node);
+ free(node);
+ }
+
+ node = zfs_alloc(zhp->zfs_hdl, sizeof (zfs_node_t));
+ node->zn_handle = zhp;
+ avl_add(avl, node);
+
+ return (0);
+}
+
+static int
+zfs_snapshot_compare(const void *larg, const void *rarg)
+{
+ zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;
+ zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;
+ uint64_t lcreate, rcreate;
+
+ /*
+ * Sort them according to creation time. We use the hidden
+ * CREATETXG property to get an absolute ordering of snapshots.
+ */
+ lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG);
+ rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG);
+
+ if (lcreate < rcreate)
+ return (-1);
+ else if (lcreate > rcreate)
+ return (+1);
+ else
+ return (0);
+}
+
+int
+zfs_iter_snapshots_sorted(zfs_handle_t *zhp, zfs_iter_f callback, void *data)
+{
+ int ret = 0;
+ zfs_node_t *node;
+ avl_tree_t avl;
+ void *cookie = NULL;
+
+ avl_create(&avl, zfs_snapshot_compare,
+ sizeof (zfs_node_t), offsetof(zfs_node_t, zn_avlnode));
+
+ ret = zfs_iter_snapshots(zhp, zfs_sort_snaps, &avl);
+
+ for (node = avl_first(&avl); node != NULL; node = AVL_NEXT(&avl, node))
+ ret |= callback(node->zn_handle, data);
+
+ while ((node = avl_destroy_nodes(&avl, &cookie)) != NULL)
+ free(node);
+
+ avl_destroy(&avl);
+
+ return (ret);
+}
+
+typedef struct {
+ char *ssa_first;
+ char *ssa_last;
+ boolean_t ssa_seenfirst;
+ boolean_t ssa_seenlast;
+ zfs_iter_f ssa_func;
+ void *ssa_arg;
+} snapspec_arg_t;
+
+static int
+snapspec_cb(zfs_handle_t *zhp, void *arg) {
+ snapspec_arg_t *ssa = arg;
+ char *shortsnapname;
+ int err = 0;
+
+ if (ssa->ssa_seenlast)
+ return (0);
+ shortsnapname = zfs_strdup(zhp->zfs_hdl,
+ strchr(zfs_get_name(zhp), '@') + 1);
+
+ if (!ssa->ssa_seenfirst && strcmp(shortsnapname, ssa->ssa_first) == 0)
+ ssa->ssa_seenfirst = B_TRUE;
+
+ if (ssa->ssa_seenfirst) {
+ err = ssa->ssa_func(zhp, ssa->ssa_arg);
+ } else {
+ zfs_close(zhp);
+ }
+
+ if (strcmp(shortsnapname, ssa->ssa_last) == 0)
+ ssa->ssa_seenlast = B_TRUE;
+ free(shortsnapname);
+
+ return (err);
+}
+
+/*
+ * spec is a string like "A,B%C,D"
+ *
+ * <snaps>, where <snaps> can be:
+ * <snap> (single snapshot)
+ * <snap>%<snap> (range of snapshots, inclusive)
+ * %<snap> (range of snapshots, starting with earliest)
+ * <snap>% (range of snapshots, ending with last)
+ * % (all snapshots)
+ * <snaps>[,...] (comma separated list of the above)
+ *
+ * If a snapshot can not be opened, continue trying to open the others, but
+ * return ENOENT at the end.
+ */
+int
+zfs_iter_snapspec(zfs_handle_t *fs_zhp, const char *spec_orig,
+ zfs_iter_f func, void *arg)
+{
+ char buf[ZFS_MAXNAMELEN];
+ char *comma_separated, *cp;
+ int err = 0;
+ int ret = 0;
+
+ (void) strlcpy(buf, spec_orig, sizeof (buf));
+ cp = buf;
+
+ while ((comma_separated = strsep(&cp, ",")) != NULL) {
+ char *pct = strchr(comma_separated, '%');
+ if (pct != NULL) {
+ snapspec_arg_t ssa = { 0 };
+ ssa.ssa_func = func;
+ ssa.ssa_arg = arg;
+
+ if (pct == comma_separated)
+ ssa.ssa_seenfirst = B_TRUE;
+ else
+ ssa.ssa_first = comma_separated;
+ *pct = '\0';
+ ssa.ssa_last = pct + 1;
+
+ /*
+ * If there is a lastname specified, make sure it
+ * exists.
+ */
+ if (ssa.ssa_last[0] != '\0') {
+ char snapname[ZFS_MAXNAMELEN];
+ (void) snprintf(snapname, sizeof (snapname),
+ "%s@%s", zfs_get_name(fs_zhp),
+ ssa.ssa_last);
+ if (!zfs_dataset_exists(fs_zhp->zfs_hdl,
+ snapname, ZFS_TYPE_SNAPSHOT)) {
+ ret = ENOENT;
+ continue;
+ }
+ }
+
+ err = zfs_iter_snapshots_sorted(fs_zhp,
+ snapspec_cb, &ssa);
+ if (ret == 0)
+ ret = err;
+ if (ret == 0 && (!ssa.ssa_seenfirst ||
+ (ssa.ssa_last[0] != '\0' && !ssa.ssa_seenlast))) {
+ ret = ENOENT;
+ }
+ } else {
+ char snapname[ZFS_MAXNAMELEN];
+ zfs_handle_t *snap_zhp;
+ (void) snprintf(snapname, sizeof (snapname), "%s@%s",
+ zfs_get_name(fs_zhp), comma_separated);
+ snap_zhp = make_dataset_handle(fs_zhp->zfs_hdl,
+ snapname);
+ if (snap_zhp == NULL) {
+ ret = ENOENT;
+ continue;
+ }
+ err = func(snap_zhp, arg);
+ if (ret == 0)
+ ret = err;
+ }
+ }
+
+ return (ret);
+}
+
+/*
+ * Iterate over all children, snapshots and filesystems
+ */
+int
+zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
+{
+ int ret;
+
+ if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0)
+ return (ret);
+
+ return (zfs_iter_snapshots(zhp, func, data));
+}
+
+
+typedef struct iter_stack_frame {
+ struct iter_stack_frame *next;
+ zfs_handle_t *zhp;
+} iter_stack_frame_t;
+
+typedef struct iter_dependents_arg {
+ boolean_t first;
+ boolean_t allowrecursion;
+ iter_stack_frame_t *stack;
+ zfs_iter_f func;
+ void *data;
+} iter_dependents_arg_t;
+
+static int
+iter_dependents_cb(zfs_handle_t *zhp, void *arg)
+{
+ iter_dependents_arg_t *ida = arg;
+ int err;
+ boolean_t first = ida->first;
+ ida->first = B_FALSE;
+
+ if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
+ err = zfs_iter_clones(zhp, iter_dependents_cb, ida);
+ } else {
+ iter_stack_frame_t isf;
+ iter_stack_frame_t *f;
+
+ /*
+ * check if there is a cycle by seeing if this fs is already
+ * on the stack.
+ */
+ for (f = ida->stack; f != NULL; f = f->next) {
+ if (f->zhp->zfs_dmustats.dds_guid ==
+ zhp->zfs_dmustats.dds_guid) {
+ if (ida->allowrecursion) {
+ zfs_close(zhp);
+ return (0);
+ } else {
+ zfs_error_aux(zhp->zfs_hdl,
+ dgettext(TEXT_DOMAIN,
+ "recursive dependency at '%s'"),
+ zfs_get_name(zhp));
+ err = zfs_error(zhp->zfs_hdl,
+ EZFS_RECURSIVE,
+ dgettext(TEXT_DOMAIN,
+ "cannot determine dependent "
+ "datasets"));
+ zfs_close(zhp);
+ return (err);
+ }
+ }
+ }
+
+ isf.zhp = zhp;
+ isf.next = ida->stack;
+ ida->stack = &isf;
+ err = zfs_iter_filesystems(zhp, iter_dependents_cb, ida);
+ if (err == 0)
+ err = zfs_iter_snapshots(zhp, iter_dependents_cb, ida);
+ ida->stack = isf.next;
+ }
+ if (!first && err == 0)
+ err = ida->func(zhp, ida->data);
+ return (err);
+}
+
+int
+zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
+ zfs_iter_f func, void *data)
+{
+ iter_dependents_arg_t ida;
+ ida.allowrecursion = allowrecursion;
+ ida.stack = NULL;
+ ida.func = func;
+ ida.data = data;
+ ida.first = B_TRUE;
+ return (iter_dependents_cb(zfs_handle_dup(zhp), &ida));
+}
diff --git a/lib/libzfs/common/libzfs_pool.c b/lib/libzfs/common/libzfs_pool.c
index 7df7e910ddc5..df89a2b445f6 100644
--- a/lib/libzfs/common/libzfs_pool.c
+++ b/lib/libzfs/common/libzfs_pool.c
@@ -21,6 +21,8 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
*/
#include <ctype.h>
@@ -41,6 +43,7 @@
#include "zfs_prop.h"
#include "libzfs_impl.h"
#include "zfs_comutil.h"
+#include "zfeature_common.h"
static int read_efi_label(nvlist_t *config, diskaddr_t *sb);
@@ -233,6 +236,7 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len,
case ZPOOL_PROP_ALTROOT:
case ZPOOL_PROP_CACHEFILE:
+ case ZPOOL_PROP_COMMENT:
if (zhp->zpool_props != NULL ||
zpool_get_all_props(zhp) == 0) {
(void) strlcpy(buf,
@@ -270,6 +274,8 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len,
case ZPOOL_PROP_SIZE:
case ZPOOL_PROP_ALLOCATED:
case ZPOOL_PROP_FREE:
+ case ZPOOL_PROP_FREEING:
+ case ZPOOL_PROP_EXPANDSZ:
(void) zfs_nicenum(intval, buf, len);
break;
@@ -294,6 +300,12 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len,
(void) strlcpy(buf, zpool_state_to_name(intval,
vs->vs_aux), len);
break;
+ case ZPOOL_PROP_VERSION:
+ if (intval >= SPA_VERSION_FEATURES) {
+ (void) snprintf(buf, len, "-");
+ break;
+ }
+ /* FALLTHROUGH */
default:
(void) snprintf(buf, len, "%llu", intval);
}
@@ -357,8 +369,8 @@ pool_uses_efi(nvlist_t *config)
return (B_FALSE);
}
-static boolean_t
-pool_is_bootable(zpool_handle_t *zhp)
+boolean_t
+zpool_is_bootable(zpool_handle_t *zhp)
{
char bootfs[ZPOOL_MAXNAMELEN];
@@ -382,7 +394,7 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
zpool_prop_t prop;
char *strval;
uint64_t intval;
- char *slash;
+ char *slash, *check;
struct stat64 statbuf;
zpool_handle_t *zhp;
nvlist_t *nvroot;
@@ -396,10 +408,48 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
const char *propname = nvpair_name(elem);
+ prop = zpool_name_to_prop(propname);
+ if (prop == ZPROP_INVAL && zpool_prop_feature(propname)) {
+ int err;
+ zfeature_info_t *feature;
+ char *fname = strchr(propname, '@') + 1;
+
+ err = zfeature_lookup_name(fname, &feature);
+ if (err != 0) {
+ ASSERT3U(err, ==, ENOENT);
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "invalid feature '%s'"), fname);
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+ }
+
+ if (nvpair_type(elem) != DATA_TYPE_STRING) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "'%s' must be a string"), propname);
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+ }
+
+ (void) nvpair_value_string(elem, &strval);
+ if (strcmp(strval, ZFS_FEATURE_ENABLED) != 0) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "property '%s' can only be set to "
+ "'enabled'"), propname);
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+ }
+
+ if (nvlist_add_uint64(retprops, propname, 0) != 0) {
+ (void) no_memory(hdl);
+ goto error;
+ }
+ continue;
+ }
+
/*
* Make sure this property is valid and applies to this type.
*/
- if ((prop = zpool_name_to_prop(propname)) == ZPROP_INVAL) {
+ if (prop == ZPROP_INVAL) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"invalid property '%s'"), propname);
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
@@ -422,7 +472,8 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
*/
switch (prop) {
case ZPOOL_PROP_VERSION:
- if (intval < version || intval > SPA_VERSION) {
+ if (intval < version ||
+ !SPA_VERSION_IS_SUPPORTED(intval)) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"property '%s' number %d is invalid."),
propname, intval);
@@ -541,6 +592,26 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
*slash = '/';
break;
+ case ZPOOL_PROP_COMMENT:
+ for (check = strval; *check != '\0'; check++) {
+ if (!isprint(*check)) {
+ zfs_error_aux(hdl,
+ dgettext(TEXT_DOMAIN,
+ "comment may only have printable "
+ "characters"));
+ (void) zfs_error(hdl, EZFS_BADPROP,
+ errbuf);
+ goto error;
+ }
+ }
+ if (strlen(strval) > ZPROP_MAX_COMMENT) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "comment must not exceed %d characters"),
+ ZPROP_MAX_COMMENT);
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+ }
+ break;
case ZPOOL_PROP_READONLY:
if (!flags.import) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
@@ -624,10 +695,77 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp)
libzfs_handle_t *hdl = zhp->zpool_hdl;
zprop_list_t *entry;
char buf[ZFS_MAXPROPLEN];
+ nvlist_t *features = NULL;
+ zprop_list_t **last;
+ boolean_t firstexpand = (NULL == *plp);
if (zprop_expand_list(hdl, plp, ZFS_TYPE_POOL) != 0)
return (-1);
+ last = plp;
+ while (*last != NULL)
+ last = &(*last)->pl_next;
+
+ if ((*plp)->pl_all)
+ features = zpool_get_features(zhp);
+
+ if ((*plp)->pl_all && firstexpand) {
+ for (int i = 0; i < SPA_FEATURES; i++) {
+ zprop_list_t *entry = zfs_alloc(hdl,
+ sizeof (zprop_list_t));
+ entry->pl_prop = ZPROP_INVAL;
+ entry->pl_user_prop = zfs_asprintf(hdl, "feature@%s",
+ spa_feature_table[i].fi_uname);
+ entry->pl_width = strlen(entry->pl_user_prop);
+ entry->pl_all = B_TRUE;
+
+ *last = entry;
+ last = &entry->pl_next;
+ }
+ }
+
+ /* add any unsupported features */
+ for (nvpair_t *nvp = nvlist_next_nvpair(features, NULL);
+ nvp != NULL; nvp = nvlist_next_nvpair(features, nvp)) {
+ char *propname;
+ boolean_t found;
+ zprop_list_t *entry;
+
+ if (zfeature_is_supported(nvpair_name(nvp)))
+ continue;
+
+ propname = zfs_asprintf(hdl, "unsupported@%s",
+ nvpair_name(nvp));
+
+ /*
+ * Before adding the property to the list make sure that no
+ * other pool already added the same property.
+ */
+ found = B_FALSE;
+ entry = *plp;
+ while (entry != NULL) {
+ if (entry->pl_user_prop != NULL &&
+ strcmp(propname, entry->pl_user_prop) == 0) {
+ found = B_TRUE;
+ break;
+ }
+ entry = entry->pl_next;
+ }
+ if (found) {
+ free(propname);
+ continue;
+ }
+
+ entry = zfs_alloc(hdl, sizeof (zprop_list_t));
+ entry->pl_prop = ZPROP_INVAL;
+ entry->pl_user_prop = propname;
+ entry->pl_width = strlen(entry->pl_user_prop);
+ entry->pl_all = B_TRUE;
+
+ *last = entry;
+ last = &entry->pl_next;
+ }
+
for (entry = *plp; entry != NULL; entry = entry->pl_next) {
if (entry->pl_fixed)
@@ -644,6 +782,66 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp)
return (0);
}
+/*
+ * Get the state for the given feature on the given ZFS pool.
+ */
+int
+zpool_prop_get_feature(zpool_handle_t *zhp, const char *propname, char *buf,
+ size_t len)
+{
+ uint64_t refcount;
+ boolean_t found = B_FALSE;
+ nvlist_t *features = zpool_get_features(zhp);
+ boolean_t supported;
+ const char *feature = strchr(propname, '@') + 1;
+
+ supported = zpool_prop_feature(propname);
+ ASSERT(supported || zfs_prop_unsupported(propname));
+
+ /*
+ * Convert from feature name to feature guid. This conversion is
+ * unecessary for unsupported@... properties because they already
+ * use guids.
+ */
+ if (supported) {
+ int ret;
+ zfeature_info_t *fi;
+
+ ret = zfeature_lookup_name(feature, &fi);
+ if (ret != 0) {
+ (void) strlcpy(buf, "-", len);
+ return (ENOTSUP);
+ }
+ feature = fi->fi_guid;
+ }
+
+ if (nvlist_lookup_uint64(features, feature, &refcount) == 0)
+ found = B_TRUE;
+
+ if (supported) {
+ if (!found) {
+ (void) strlcpy(buf, ZFS_FEATURE_DISABLED, len);
+ } else {
+ if (refcount == 0)
+ (void) strlcpy(buf, ZFS_FEATURE_ENABLED, len);
+ else
+ (void) strlcpy(buf, ZFS_FEATURE_ACTIVE, len);
+ }
+ } else {
+ if (found) {
+ if (refcount == 0) {
+ (void) strcpy(buf, ZFS_UNSUPPORTED_INACTIVE);
+ } else {
+ (void) strcpy(buf, ZFS_UNSUPPORTED_READONLY);
+ }
+ } else {
+ (void) strlcpy(buf, "-", len);
+ return (ENOTSUP);
+ }
+ }
+
+ return (0);
+}
/*
* Don't start the slice at the default block of 34; many storage
@@ -1071,7 +1269,7 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot)
return (zfs_error(hdl, EZFS_BADVERSION, msg));
}
- if (pool_is_bootable(zhp) && nvlist_lookup_nvlist_array(nvroot,
+ if (zpool_is_bootable(zhp) && nvlist_lookup_nvlist_array(nvroot,
ZPOOL_CONFIG_SPARES, &spares, &nspares) == 0) {
uint64_t s;
@@ -1230,8 +1428,10 @@ zpool_rewind_exclaim(libzfs_handle_t *hdl, const char *name, boolean_t dryrun,
if (!hdl->libzfs_printerr || config == NULL)
return;
- if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO, &nv) != 0)
+ if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO, &nv) != 0 ||
+ nvlist_lookup_nvlist(nv, ZPOOL_CONFIG_REWIND_INFO, &nv) != 0) {
return;
+ }
if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_LOAD_TIME, &rewindto) != 0)
return;
@@ -1287,6 +1487,7 @@ zpool_explain_recover(libzfs_handle_t *hdl, const char *name, int reason,
/* All attempted rewinds failed if ZPOOL_CONFIG_LOAD_TIME missing */
if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO, &nv) != 0 ||
+ nvlist_lookup_nvlist(nv, ZPOOL_CONFIG_REWIND_INFO, &nv) != 0 ||
nvlist_lookup_uint64(nv, ZPOOL_CONFIG_LOAD_TIME, &rewindto) != 0)
goto no_info;
@@ -1409,6 +1610,30 @@ print_vdev_tree(libzfs_handle_t *hdl, const char *name, nvlist_t *nv,
}
}
+void
+zpool_print_unsup_feat(nvlist_t *config)
+{
+ nvlist_t *nvinfo, *unsup_feat;
+
+ verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO, &nvinfo) ==
+ 0);
+ verify(nvlist_lookup_nvlist(nvinfo, ZPOOL_CONFIG_UNSUP_FEAT,
+ &unsup_feat) == 0);
+
+ for (nvpair_t *nvp = nvlist_next_nvpair(unsup_feat, NULL); nvp != NULL;
+ nvp = nvlist_next_nvpair(unsup_feat, nvp)) {
+ char *desc;
+
+ verify(nvpair_type(nvp) == DATA_TYPE_STRING);
+ verify(nvpair_value_string(nvp, &desc) == 0);
+
+ if (strlen(desc) > 0)
+ (void) printf("\t%s (%s)\n", nvpair_name(nvp), desc);
+ else
+ (void) printf("\t%s\n", nvpair_name(nvp));
+ }
+}
+
/*
* Import the given pool using the known configuration and a list of
* properties to be set. The configuration should have come from
@@ -1515,6 +1740,22 @@ zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
switch (error) {
case ENOTSUP:
+ if (nv != NULL && nvlist_lookup_nvlist(nv,
+ ZPOOL_CONFIG_LOAD_INFO, &nvinfo) == 0 &&
+ nvlist_exists(nvinfo, ZPOOL_CONFIG_UNSUP_FEAT)) {
+ (void) printf(dgettext(TEXT_DOMAIN, "This "
+ "pool uses the following feature(s) not "
+ "supported by this system:\n"));
+ zpool_print_unsup_feat(nv);
+ if (nvlist_exists(nvinfo,
+ ZPOOL_CONFIG_CAN_RDONLY)) {
+ (void) printf(dgettext(TEXT_DOMAIN,
+ "All unsupported features are only "
+ "required for writing to the pool."
+ "\nThe pool can be imported using "
+ "'-o readonly=on'.\n"));
+ }
+ }
/*
* Unsupported version.
*/
@@ -2355,7 +2596,7 @@ zpool_vdev_attach(zpool_handle_t *zhp,
uint_t children;
nvlist_t *config_root;
libzfs_handle_t *hdl = zhp->zpool_hdl;
- boolean_t rootpool = pool_is_bootable(zhp);
+ boolean_t rootpool = zpool_is_bootable(zhp);
if (replacing)
(void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
@@ -2967,6 +3208,46 @@ zpool_vdev_clear(zpool_handle_t *zhp, uint64_t guid)
}
/*
+ * Change the GUID for a pool.
+ */
+int
+zpool_reguid(zpool_handle_t *zhp)
+{
+ char msg[1024];
+ libzfs_handle_t *hdl = zhp->zpool_hdl;
+ zfs_cmd_t zc = { 0 };
+
+ (void) snprintf(msg, sizeof (msg),
+ dgettext(TEXT_DOMAIN, "cannot reguid '%s'"), zhp->zpool_name);
+
+ (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
+ if (zfs_ioctl(hdl, ZFS_IOC_POOL_REGUID, &zc) == 0)
+ return (0);
+
+ return (zpool_standard_error(hdl, errno, msg));
+}
+
+/*
+ * Reopen the pool.
+ */
+int
+zpool_reopen(zpool_handle_t *zhp)
+{
+ zfs_cmd_t zc = { 0 };
+ char msg[1024];
+ libzfs_handle_t *hdl = zhp->zpool_hdl;
+
+ (void) snprintf(msg, sizeof (msg),
+ dgettext(TEXT_DOMAIN, "cannot reopen '%s'"),
+ zhp->zpool_name);
+
+ (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
+ if (zfs_ioctl(hdl, ZFS_IOC_POOL_REOPEN, &zc) == 0)
+ return (0);
+ return (zpool_standard_error(hdl, errno, msg));
+}
+
+/*
* Convert from a devid string to a path.
*/
static char *
@@ -3599,7 +3880,7 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name)
if (zhp) {
nvlist_t *nvroot;
- if (pool_is_bootable(zhp)) {
+ if (zpool_is_bootable(zhp)) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"EFI labeled devices are not supported on root "
"pools."));
diff --git a/lib/libzfs/common/libzfs_sendrecv.c b/lib/libzfs/common/libzfs_sendrecv.c
index 3093ab974d06..a02a41b116a8 100644
--- a/lib/libzfs/common/libzfs_sendrecv.c
+++ b/lib/libzfs/common/libzfs_sendrecv.c
@@ -21,6 +21,8 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
*/
#include <assert.h>
@@ -36,6 +38,7 @@
#include <sys/mount.h>
#include <pthread.h>
#include <umem.h>
+#include <time.h>
#include <libzfs.h>
@@ -50,7 +53,7 @@
/* in libzfs_dataset.c */
extern void zfs_setprop_error(libzfs_handle_t *, zfs_prop_t, int, char *);
-static int zfs_receive_impl(libzfs_handle_t *, const char *, recvflags_t,
+static int zfs_receive_impl(libzfs_handle_t *, const char *, recvflags_t *,
int, const char *, nvlist_t *, avl_tree_t *, char **, int, uint64_t *);
static const zio_cksum_t zero_cksum = { 0 };
@@ -61,6 +64,12 @@ typedef struct dedup_arg {
libzfs_handle_t *dedup_hdl;
} dedup_arg_t;
+typedef struct progress_arg {
+ zfs_handle_t *pa_zhp;
+ int pa_fd;
+ boolean_t pa_parsable;
+} progress_arg_t;
+
typedef struct dataref {
uint64_t ref_guid;
uint64_t ref_object;
@@ -771,88 +780,6 @@ gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap,
}
/*
- * Routines for dealing with the sorted snapshot functionality
- */
-typedef struct zfs_node {
- zfs_handle_t *zn_handle;
- avl_node_t zn_avlnode;
-} zfs_node_t;
-
-static int
-zfs_sort_snaps(zfs_handle_t *zhp, void *data)
-{
- avl_tree_t *avl = data;
- zfs_node_t *node;
- zfs_node_t search;
-
- search.zn_handle = zhp;
- node = avl_find(avl, &search, NULL);
- if (node) {
- /*
- * If this snapshot was renamed while we were creating the
- * AVL tree, it's possible that we already inserted it under
- * its old name. Remove the old handle before adding the new
- * one.
- */
- zfs_close(node->zn_handle);
- avl_remove(avl, node);
- free(node);
- }
-
- node = zfs_alloc(zhp->zfs_hdl, sizeof (zfs_node_t));
- node->zn_handle = zhp;
- avl_add(avl, node);
-
- return (0);
-}
-
-static int
-zfs_snapshot_compare(const void *larg, const void *rarg)
-{
- zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;
- zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;
- uint64_t lcreate, rcreate;
-
- /*
- * Sort them according to creation time. We use the hidden
- * CREATETXG property to get an absolute ordering of snapshots.
- */
- lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG);
- rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG);
-
- if (lcreate < rcreate)
- return (-1);
- else if (lcreate > rcreate)
- return (+1);
- else
- return (0);
-}
-
-int
-zfs_iter_snapshots_sorted(zfs_handle_t *zhp, zfs_iter_f callback, void *data)
-{
- int ret = 0;
- zfs_node_t *node;
- avl_tree_t avl;
- void *cookie = NULL;
-
- avl_create(&avl, zfs_snapshot_compare,
- sizeof (zfs_node_t), offsetof(zfs_node_t, zn_avlnode));
-
- ret = zfs_iter_snapshots(zhp, zfs_sort_snaps, &avl);
-
- for (node = avl_first(&avl); node != NULL; node = AVL_NEXT(&avl, node))
- ret |= callback(node->zn_handle, data);
-
- while ((node = avl_destroy_nodes(&avl, &cookie)) != NULL)
- free(node);
-
- avl_destroy(&avl);
-
- return (ret);
-}
-
-/*
* Routines specific to "zfs send"
*/
typedef struct send_dump_data {
@@ -862,7 +789,7 @@ typedef struct send_dump_data {
char prevsnap[ZFS_MAXNAMELEN];
uint64_t prevsnap_obj;
boolean_t seenfrom, seento, replicate, doall, fromorigin;
- boolean_t verbose;
+ boolean_t verbose, dryrun, parsable, progress;
int outfd;
boolean_t err;
nvlist_t *fss;
@@ -872,8 +799,69 @@ typedef struct send_dump_data {
nvlist_t *debugnv;
char holdtag[ZFS_MAXNAMELEN];
int cleanup_fd;
+ uint64_t size;
} send_dump_data_t;
+static int
+estimate_ioctl(zfs_handle_t *zhp, uint64_t fromsnap_obj,
+ boolean_t fromorigin, uint64_t *sizep)
+{
+ zfs_cmd_t zc = { 0 };
+ libzfs_handle_t *hdl = zhp->zfs_hdl;
+
+ assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
+ assert(fromsnap_obj == 0 || !fromorigin);
+
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+ zc.zc_obj = fromorigin;
+ zc.zc_sendobj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
+ zc.zc_fromobj = fromsnap_obj;
+ zc.zc_guid = 1; /* estimate flag */
+
+ if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SEND, &zc) != 0) {
+ char errbuf[1024];
+ (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+ "warning: cannot estimate space for '%s'"), zhp->zfs_name);
+
+ switch (errno) {
+ case EXDEV:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "not an earlier snapshot from the same fs"));
+ return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
+
+ case ENOENT:
+ if (zfs_dataset_exists(hdl, zc.zc_name,
+ ZFS_TYPE_SNAPSHOT)) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "incremental source (@%s) does not exist"),
+ zc.zc_value);
+ }
+ return (zfs_error(hdl, EZFS_NOENT, errbuf));
+
+ case EDQUOT:
+ case EFBIG:
+ case EIO:
+ case ENOLINK:
+ case ENOSPC:
+ case ENOSTR:
+ case ENXIO:
+ case EPIPE:
+ case ERANGE:
+ case EFAULT:
+ case EROFS:
+ zfs_error_aux(hdl, strerror(errno));
+ return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
+
+ default:
+ return (zfs_standard_error(hdl, errno, errbuf));
+ }
+ }
+
+ *sizep = zc.zc_objset_type;
+
+ return (0);
+}
+
/*
* Dumps a backup of the given snapshot (incremental from fromsnap if it's not
* NULL) to the file descriptor specified by outfd.
@@ -901,7 +889,7 @@ dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, uint64_t fromsnap_obj,
"fromsnap", fromsnap));
}
- if (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SEND, &zc) != 0) {
+ if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SEND, &zc) != 0) {
char errbuf[1024];
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"warning: cannot send '%s'"), zhp->zfs_name);
@@ -914,7 +902,6 @@ dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, uint64_t fromsnap_obj,
nvlist_free(thisdbg);
switch (errno) {
-
case EXDEV:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"not an earlier snapshot from the same fs"));
@@ -964,6 +951,9 @@ hold_for_send(zfs_handle_t *zhp, send_dump_data_t *sdd)
assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
+ if (sdd->dryrun)
+ return (0);
+
/*
* zfs_send() only opens a cleanup_fd for sends that need it,
* e.g. replication and doall.
@@ -991,13 +981,63 @@ hold_for_send(zfs_handle_t *zhp, send_dump_data_t *sdd)
return (error);
}
+static void *
+send_progress_thread(void *arg)
+{
+ progress_arg_t *pa = arg;
+
+ zfs_cmd_t zc = { 0 };
+ zfs_handle_t *zhp = pa->pa_zhp;
+ libzfs_handle_t *hdl = zhp->zfs_hdl;
+ unsigned long long bytes;
+ char buf[16];
+
+ time_t t;
+ struct tm *tm;
+
+ assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+
+ if (!pa->pa_parsable)
+ (void) fprintf(stderr, "TIME SENT SNAPSHOT\n");
+
+ /*
+ * Print the progress from ZFS_IOC_SEND_PROGRESS every second.
+ */
+ for (;;) {
+ (void) sleep(1);
+
+ zc.zc_cookie = pa->pa_fd;
+ if (zfs_ioctl(hdl, ZFS_IOC_SEND_PROGRESS, &zc) != 0)
+ return ((void *)-1);
+
+ (void) time(&t);
+ tm = localtime(&t);
+ bytes = zc.zc_cookie;
+
+ if (pa->pa_parsable) {
+ (void) fprintf(stderr, "%02d:%02d:%02d\t%llu\t%s\n",
+ tm->tm_hour, tm->tm_min, tm->tm_sec,
+ bytes, zhp->zfs_name);
+ } else {
+ zfs_nicenum(bytes, buf, sizeof (buf));
+ (void) fprintf(stderr, "%02d:%02d:%02d %5s %s\n",
+ tm->tm_hour, tm->tm_min, tm->tm_sec,
+ buf, zhp->zfs_name);
+ }
+ }
+}
+
static int
dump_snapshot(zfs_handle_t *zhp, void *arg)
{
send_dump_data_t *sdd = arg;
+ progress_arg_t pa = { 0 };
+ pthread_t tid;
+
char *thissnap;
int err;
- boolean_t isfromsnap, istosnap;
+ boolean_t isfromsnap, istosnap, fromorigin;
boolean_t exclude = B_FALSE;
thissnap = strchr(zhp->zfs_name, '@') + 1;
@@ -1074,15 +1114,68 @@ dump_snapshot(zfs_handle_t *zhp, void *arg)
return (err);
}
- /* send it */
+ fromorigin = sdd->prevsnap[0] == '\0' &&
+ (sdd->fromorigin || sdd->replicate);
+
if (sdd->verbose) {
- (void) fprintf(stderr, "sending from @%s to %s\n",
- sdd->prevsnap, zhp->zfs_name);
+ uint64_t size;
+ err = estimate_ioctl(zhp, sdd->prevsnap_obj,
+ fromorigin, &size);
+
+ if (sdd->parsable) {
+ if (sdd->prevsnap[0] != '\0') {
+ (void) fprintf(stderr, "incremental\t%s\t%s",
+ sdd->prevsnap, zhp->zfs_name);
+ } else {
+ (void) fprintf(stderr, "full\t%s",
+ zhp->zfs_name);
+ }
+ } else {
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ "send from @%s to %s"),
+ sdd->prevsnap, zhp->zfs_name);
+ }
+ if (err == 0) {
+ if (sdd->parsable) {
+ (void) fprintf(stderr, "\t%llu\n",
+ (longlong_t)size);
+ } else {
+ char buf[16];
+ zfs_nicenum(size, buf, sizeof (buf));
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ " estimated size is %s\n"), buf);
+ }
+ sdd->size += size;
+ } else {
+ (void) fprintf(stderr, "\n");
+ }
}
- err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj,
- sdd->prevsnap[0] == '\0' && (sdd->fromorigin || sdd->replicate),
- sdd->outfd, sdd->debugnv);
+ if (!sdd->dryrun) {
+ /*
+ * If progress reporting is requested, spawn a new thread to
+ * poll ZFS_IOC_SEND_PROGRESS at a regular interval.
+ */
+ if (sdd->progress) {
+ pa.pa_zhp = zhp;
+ pa.pa_fd = sdd->outfd;
+ pa.pa_parsable = sdd->parsable;
+
+ if (err = pthread_create(&tid, NULL,
+ send_progress_thread, &pa)) {
+ zfs_close(zhp);
+ return (err);
+ }
+ }
+
+ err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj,
+ fromorigin, sdd->outfd, sdd->debugnv);
+
+ if (sdd->progress) {
+ (void) pthread_cancel(tid);
+ (void) pthread_join(tid, NULL);
+ }
+ }
(void) strcpy(sdd->prevsnap, thissnap);
sdd->prevsnap_obj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
@@ -1101,8 +1194,8 @@ dump_filesystem(zfs_handle_t *zhp, void *arg)
(void) snprintf(zc.zc_name, sizeof (zc.zc_name), "%s@%s",
zhp->zfs_name, sdd->tosnap);
if (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0) {
- (void) fprintf(stderr, "WARNING: "
- "could not send %s@%s: does not exist\n",
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ "WARNING: could not send %s@%s: does not exist\n"),
zhp->zfs_name, sdd->tosnap);
sdd->err = B_TRUE;
return (0);
@@ -1131,23 +1224,24 @@ dump_filesystem(zfs_handle_t *zhp, void *arg)
rv = zfs_iter_snapshots_sorted(zhp, dump_snapshot, arg);
if (!sdd->seenfrom) {
- (void) fprintf(stderr,
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
"WARNING: could not send %s@%s:\n"
- "incremental source (%s@%s) does not exist\n",
+ "incremental source (%s@%s) does not exist\n"),
zhp->zfs_name, sdd->tosnap,
zhp->zfs_name, sdd->fromsnap);
sdd->err = B_TRUE;
} else if (!sdd->seento) {
if (sdd->fromsnap) {
- (void) fprintf(stderr,
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
"WARNING: could not send %s@%s:\n"
"incremental source (%s@%s) "
- "is not earlier than it\n",
+ "is not earlier than it\n"),
zhp->zfs_name, sdd->tosnap,
zhp->zfs_name, sdd->fromsnap);
} else {
- (void) fprintf(stderr, "WARNING: "
- "could not send %s@%s: does not exist\n",
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ "WARNING: "
+ "could not send %s@%s: does not exist\n"),
zhp->zfs_name, sdd->tosnap);
}
sdd->err = B_TRUE;
@@ -1193,11 +1287,12 @@ again:
needagain = progress = B_FALSE;
for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair;
fspair = nvlist_next_nvpair(sdd->fss, fspair)) {
- nvlist_t *fslist;
+ nvlist_t *fslist, *parent_nv;
char *fsname;
zfs_handle_t *zhp;
int err;
uint64_t origin_guid = 0;
+ uint64_t parent_guid = 0;
VERIFY(nvpair_value_nvlist(fspair, &fslist) == 0);
if (nvlist_lookup_boolean(fslist, "sent") == 0)
@@ -1205,13 +1300,23 @@ again:
VERIFY(nvlist_lookup_string(fslist, "name", &fsname) == 0);
(void) nvlist_lookup_uint64(fslist, "origin", &origin_guid);
+ (void) nvlist_lookup_uint64(fslist, "parentfromsnap",
+ &parent_guid);
+
+ if (parent_guid != 0) {
+ parent_nv = fsavl_find(sdd->fsavl, parent_guid, NULL);
+ if (!nvlist_exists(parent_nv, "sent")) {
+ /* parent has not been sent; skip this one */
+ needagain = B_TRUE;
+ continue;
+ }
+ }
if (origin_guid != 0) {
nvlist_t *origin_nv = fsavl_find(sdd->fsavl,
origin_guid, NULL);
if (origin_nv != NULL &&
- nvlist_lookup_boolean(origin_nv,
- "sent") == ENOENT) {
+ !nvlist_exists(origin_nv, "sent")) {
/*
* origin has not been sent yet;
* skip this clone.
@@ -1235,6 +1340,16 @@ again:
assert(progress);
goto again;
}
+
+ /* clean out the sent flags in case we reuse this fss */
+ for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair;
+ fspair = nvlist_next_nvpair(sdd->fss, fspair)) {
+ nvlist_t *fslist;
+
+ VERIFY(nvpair_value_nvlist(fspair, &fslist) == 0);
+ (void) nvlist_remove_all(fslist, "sent");
+ }
+
return (0);
}
@@ -1256,12 +1371,12 @@ again:
*/
int
zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
- sendflags_t flags, int outfd, snapfilter_cb_t filter_func,
+ sendflags_t *flags, int outfd, snapfilter_cb_t filter_func,
void *cb_arg, nvlist_t **debugnvp)
{
char errbuf[1024];
send_dump_data_t sdd = { 0 };
- int err;
+ int err = 0;
nvlist_t *fss = NULL;
avl_tree_t *fsavl = NULL;
static uint64_t holdseq;
@@ -1289,12 +1404,12 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
}
}
- if (zfs_spa_version(zhp, &spa_version) == 0 &&
+ if (!flags->dryrun && zfs_spa_version(zhp, &spa_version) == 0 &&
spa_version >= SPA_VERSION_USERREFS &&
- (flags.doall || flags.replicate))
+ (flags->doall || flags->replicate))
holdsnaps = B_TRUE;
- if (flags.dedup) {
+ if (flags->dedup && !flags->dryrun) {
featureflags |= (DMU_BACKUP_FEATURE_DEDUP |
DMU_BACKUP_FEATURE_DEDUPPROPS);
if (err = pipe(pipefd)) {
@@ -1314,13 +1429,13 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
}
}
- if (flags.replicate || flags.doall || flags.props) {
+ if (flags->replicate || flags->doall || flags->props) {
dmu_replay_record_t drr = { 0 };
char *packbuf = NULL;
size_t buflen = 0;
zio_cksum_t zc = { 0 };
- if (flags.replicate || flags.props) {
+ if (flags->replicate || flags->props) {
nvlist_t *hdrnv;
VERIFY(0 == nvlist_alloc(&hdrnv, NV_UNIQUE_NAME, 0));
@@ -1329,13 +1444,13 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
"fromsnap", fromsnap));
}
VERIFY(0 == nvlist_add_string(hdrnv, "tosnap", tosnap));
- if (!flags.replicate) {
+ if (!flags->replicate) {
VERIFY(0 == nvlist_add_boolean(hdrnv,
"not_recursive"));
}
err = gather_nvlist(zhp->zfs_hdl, zhp->zfs_name,
- fromsnap, tosnap, flags.replicate, &fss, &fsavl);
+ fromsnap, tosnap, flags->replicate, &fss, &fsavl);
if (err)
goto err_out;
VERIFY(0 == nvlist_add_nvlist(hdrnv, "fss", fss));
@@ -1352,33 +1467,34 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
}
}
- /* write first begin record */
- drr.drr_type = DRR_BEGIN;
- drr.drr_u.drr_begin.drr_magic = DMU_BACKUP_MAGIC;
- DMU_SET_STREAM_HDRTYPE(drr.drr_u.drr_begin.drr_versioninfo,
- DMU_COMPOUNDSTREAM);
- DMU_SET_FEATUREFLAGS(drr.drr_u.drr_begin.drr_versioninfo,
- featureflags);
- (void) snprintf(drr.drr_u.drr_begin.drr_toname,
- sizeof (drr.drr_u.drr_begin.drr_toname),
- "%s@%s", zhp->zfs_name, tosnap);
- drr.drr_payloadlen = buflen;
- err = cksum_and_write(&drr, sizeof (drr), &zc, outfd);
-
- /* write header nvlist */
- if (err != -1 && packbuf != NULL) {
- err = cksum_and_write(packbuf, buflen, &zc, outfd);
- }
- free(packbuf);
- if (err == -1) {
- fsavl_destroy(fsavl);
- nvlist_free(fss);
- err = errno;
- goto stderr_out;
- }
+ if (!flags->dryrun) {
+ /* write first begin record */
+ drr.drr_type = DRR_BEGIN;
+ drr.drr_u.drr_begin.drr_magic = DMU_BACKUP_MAGIC;
+ DMU_SET_STREAM_HDRTYPE(drr.drr_u.drr_begin.
+ drr_versioninfo, DMU_COMPOUNDSTREAM);
+ DMU_SET_FEATUREFLAGS(drr.drr_u.drr_begin.
+ drr_versioninfo, featureflags);
+ (void) snprintf(drr.drr_u.drr_begin.drr_toname,
+ sizeof (drr.drr_u.drr_begin.drr_toname),
+ "%s@%s", zhp->zfs_name, tosnap);
+ drr.drr_payloadlen = buflen;
+ err = cksum_and_write(&drr, sizeof (drr), &zc, outfd);
+
+ /* write header nvlist */
+ if (err != -1 && packbuf != NULL) {
+ err = cksum_and_write(packbuf, buflen, &zc,
+ outfd);
+ }
+ free(packbuf);
+ if (err == -1) {
+ fsavl_destroy(fsavl);
+ nvlist_free(fss);
+ err = errno;
+ goto stderr_out;
+ }
- /* write end record */
- if (err != -1) {
+ /* write end record */
bzero(&drr, sizeof (drr));
drr.drr_type = DRR_END;
drr.drr_u.drr_end.drr_checksum = zc;
@@ -1389,27 +1505,32 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
err = errno;
goto stderr_out;
}
+
+ err = 0;
}
}
/* dump each stream */
sdd.fromsnap = fromsnap;
sdd.tosnap = tosnap;
- if (flags.dedup)
+ if (flags->dedup)
sdd.outfd = pipefd[0];
else
sdd.outfd = outfd;
- sdd.replicate = flags.replicate;
- sdd.doall = flags.doall;
- sdd.fromorigin = flags.fromorigin;
+ sdd.replicate = flags->replicate;
+ sdd.doall = flags->doall;
+ sdd.fromorigin = flags->fromorigin;
sdd.fss = fss;
sdd.fsavl = fsavl;
- sdd.verbose = flags.verbose;
+ sdd.verbose = flags->verbose;
+ sdd.parsable = flags->parsable;
+ sdd.progress = flags->progress;
+ sdd.dryrun = flags->dryrun;
sdd.filter_cb = filter_func;
sdd.filter_cb_arg = cb_arg;
if (debugnvp)
sdd.debugnv = *debugnvp;
- if (holdsnaps) {
+ if (holdsnaps || flags->progress) {
++holdseq;
(void) snprintf(sdd.holdtag, sizeof (sdd.holdtag),
".send-%d-%llu", getpid(), (u_longlong_t)holdseq);
@@ -1421,11 +1542,31 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
} else {
sdd.cleanup_fd = -1;
}
+ if (flags->verbose) {
+ /*
+ * Do a verbose no-op dry run to get all the verbose output
+ * before generating any data. Then do a non-verbose real
+ * run to generate the streams.
+ */
+ sdd.dryrun = B_TRUE;
+ err = dump_filesystems(zhp, &sdd);
+ sdd.dryrun = flags->dryrun;
+ sdd.verbose = B_FALSE;
+ if (flags->parsable) {
+ (void) fprintf(stderr, "size\t%llu\n",
+ (longlong_t)sdd.size);
+ } else {
+ char buf[16];
+ zfs_nicenum(sdd.size, buf, sizeof (buf));
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ "total estimated size is %s\n"), buf);
+ }
+ }
err = dump_filesystems(zhp, &sdd);
fsavl_destroy(fsavl);
nvlist_free(fss);
- if (flags.dedup) {
+ if (flags->dedup) {
(void) close(pipefd[0]);
(void) pthread_join(tid, NULL);
}
@@ -1435,7 +1576,8 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
sdd.cleanup_fd = -1;
}
- if (flags.replicate || flags.doall || flags.props) {
+ if (!flags->dryrun && (flags->replicate || flags->doall ||
+ flags->props)) {
/*
* write final end record. NB: want to do this even if
* there was some error, because it might not be totally
@@ -1456,7 +1598,7 @@ stderr_out:
err_out:
if (sdd.cleanup_fd != -1)
VERIFY(0 == close(sdd.cleanup_fd));
- if (flags.dedup) {
+ if (flags->dedup) {
(void) pthread_cancel(tid);
(void) pthread_join(tid, NULL);
(void) close(pipefd[0]);
@@ -1527,7 +1669,7 @@ recv_read_nvlist(libzfs_handle_t *hdl, int fd, int len, nvlist_t **nvp,
static int
recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
- int baselen, char *newname, recvflags_t flags)
+ int baselen, char *newname, recvflags_t *flags)
{
static int seq;
zfs_cmd_t zc = { 0 };
@@ -1539,7 +1681,7 @@ recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
if (zhp == NULL)
return (-1);
clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
- flags.force ? MS_FORCE : 0);
+ flags->force ? MS_FORCE : 0);
zfs_close(zhp);
if (clp == NULL)
return (-1);
@@ -1555,7 +1697,7 @@ recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
(void) strlcpy(zc.zc_value, tryname, sizeof (zc.zc_value));
- if (flags.verbose) {
+ if (flags->verbose) {
(void) printf("attempting rename %s to %s\n",
zc.zc_name, zc.zc_value);
}
@@ -1574,19 +1716,19 @@ recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
"recv-%u-%u", getpid(), seq);
(void) strlcpy(zc.zc_value, newname, sizeof (zc.zc_value));
- if (flags.verbose) {
+ if (flags->verbose) {
(void) printf("failed - trying rename %s to %s\n",
zc.zc_name, zc.zc_value);
}
err = ioctl(hdl->libzfs_fd, ZFS_IOC_RENAME, &zc);
if (err == 0)
changelist_rename(clp, name, newname);
- if (err && flags.verbose) {
+ if (err && flags->verbose) {
(void) printf("failed (%u) - "
"will try again on next pass\n", errno);
}
err = EAGAIN;
- } else if (flags.verbose) {
+ } else if (flags->verbose) {
if (err == 0)
(void) printf("success\n");
else
@@ -1601,7 +1743,7 @@ recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
static int
recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen,
- char *newname, recvflags_t flags)
+ char *newname, recvflags_t *flags)
{
zfs_cmd_t zc = { 0 };
int err = 0;
@@ -1614,7 +1756,7 @@ recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen,
if (zhp == NULL)
return (-1);
clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
- flags.force ? MS_FORCE : 0);
+ flags->force ? MS_FORCE : 0);
if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
zfs_spa_version(zhp, &spa_version) == 0 &&
spa_version >= SPA_VERSION_USERREFS)
@@ -1630,11 +1772,11 @@ recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen,
zc.zc_defer_destroy = defer;
(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
- if (flags.verbose)
+ if (flags->verbose)
(void) printf("attempting destroy %s\n", zc.zc_name);
err = ioctl(hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc);
if (err == 0) {
- if (flags.verbose)
+ if (flags->verbose)
(void) printf("success\n");
changelist_remove(clp, zc.zc_name);
}
@@ -1657,6 +1799,7 @@ recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen,
typedef struct guid_to_name_data {
uint64_t guid;
char *name;
+ char *skip;
} guid_to_name_data_t;
static int
@@ -1665,21 +1808,35 @@ guid_to_name_cb(zfs_handle_t *zhp, void *arg)
guid_to_name_data_t *gtnd = arg;
int err;
+ if (gtnd->skip != NULL &&
+ strcmp(zhp->zfs_name, gtnd->skip) == 0) {
+ return (0);
+ }
+
if (zhp->zfs_dmustats.dds_guid == gtnd->guid) {
(void) strcpy(gtnd->name, zhp->zfs_name);
zfs_close(zhp);
return (EEXIST);
}
+
err = zfs_iter_children(zhp, guid_to_name_cb, gtnd);
zfs_close(zhp);
return (err);
}
+/*
+ * Attempt to find the local dataset associated with this guid. In the case of
+ * multiple matches, we attempt to find the "best" match by searching
+ * progressively larger portions of the hierarchy. This allows one to send a
+ * tree of datasets individually and guarantee that we will find the source
+ * guid within that hierarchy, even if there are multiple matches elsewhere.
+ */
static int
guid_to_name(libzfs_handle_t *hdl, const char *parent, uint64_t guid,
char *name)
{
/* exhaustive search all local snapshots */
+ char pname[ZFS_MAXNAMELEN];
guid_to_name_data_t gtnd;
int err = 0;
zfs_handle_t *zhp;
@@ -1687,35 +1844,42 @@ guid_to_name(libzfs_handle_t *hdl, const char *parent, uint64_t guid,
gtnd.guid = guid;
gtnd.name = name;
+ gtnd.skip = NULL;
- if (strchr(parent, '@') == NULL) {
- zhp = make_dataset_handle(hdl, parent);
- if (zhp != NULL) {
- err = zfs_iter_children(zhp, guid_to_name_cb, &gtnd);
- zfs_close(zhp);
- if (err == EEXIST)
- return (0);
- }
- }
+ (void) strlcpy(pname, parent, sizeof (pname));
+
+ /*
+ * Search progressively larger portions of the hierarchy. This will
+ * select the "most local" version of the origin snapshot in the case
+ * that there are multiple matching snapshots in the system.
+ */
+ while ((cp = strrchr(pname, '/')) != NULL) {
- cp = strchr(parent, '/');
- if (cp)
+ /* Chop off the last component and open the parent */
*cp = '\0';
- zhp = make_dataset_handle(hdl, parent);
- if (cp)
- *cp = '/';
+ zhp = make_dataset_handle(hdl, pname);
+
+ if (zhp == NULL)
+ continue;
- if (zhp) {
err = zfs_iter_children(zhp, guid_to_name_cb, &gtnd);
zfs_close(zhp);
- }
+ if (err == EEXIST)
+ return (0);
- return (err == EEXIST ? 0 : ENOENT);
+ /*
+ * Remember the dataset that we already searched, so we
+ * skip it next time through.
+ */
+ gtnd.skip = pname;
+ }
+ return (ENOENT);
}
/*
- * Return true if dataset guid1 is created before guid2.
+ * Return +1 if guid1 is before guid2, 0 if they are the same, and -1 if
+ * guid1 is after guid2.
*/
static int
created_before(libzfs_handle_t *hdl, avl_tree_t *avl,
@@ -1725,7 +1889,8 @@ created_before(libzfs_handle_t *hdl, avl_tree_t *avl,
char *fsname, *snapname;
char buf[ZFS_MAXNAMELEN];
int rv;
- zfs_node_t zn1, zn2;
+ zfs_handle_t *guid1hdl, *guid2hdl;
+ uint64_t create1, create2;
if (guid2 == 0)
return (0);
@@ -1735,30 +1900,38 @@ created_before(libzfs_handle_t *hdl, avl_tree_t *avl,
nvfs = fsavl_find(avl, guid1, &snapname);
VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
(void) snprintf(buf, sizeof (buf), "%s@%s", fsname, snapname);
- zn1.zn_handle = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
- if (zn1.zn_handle == NULL)
+ guid1hdl = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
+ if (guid1hdl == NULL)
return (-1);
nvfs = fsavl_find(avl, guid2, &snapname);
VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
(void) snprintf(buf, sizeof (buf), "%s@%s", fsname, snapname);
- zn2.zn_handle = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
- if (zn2.zn_handle == NULL) {
- zfs_close(zn2.zn_handle);
+ guid2hdl = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
+ if (guid2hdl == NULL) {
+ zfs_close(guid1hdl);
return (-1);
}
- rv = (zfs_snapshot_compare(&zn1, &zn2) == -1);
+ create1 = zfs_prop_get_int(guid1hdl, ZFS_PROP_CREATETXG);
+ create2 = zfs_prop_get_int(guid2hdl, ZFS_PROP_CREATETXG);
- zfs_close(zn1.zn_handle);
- zfs_close(zn2.zn_handle);
+ if (create1 < create2)
+ rv = -1;
+ else if (create1 > create2)
+ rv = +1;
+ else
+ rv = 0;
+
+ zfs_close(guid1hdl);
+ zfs_close(guid2hdl);
return (rv);
}
static int
recv_incremental_replication(libzfs_handle_t *hdl, const char *tofs,
- recvflags_t flags, nvlist_t *stream_nv, avl_tree_t *stream_avl,
+ recvflags_t *flags, nvlist_t *stream_nv, avl_tree_t *stream_avl,
nvlist_t *renamed)
{
nvlist_t *local_nv;
@@ -1775,7 +1948,7 @@ recv_incremental_replication(libzfs_handle_t *hdl, const char *tofs,
recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") ==
ENOENT);
- if (flags.dryrun)
+ if (flags->dryrun)
return (0);
again:
@@ -1835,7 +2008,7 @@ again:
nvlist_t *origin_nvfs;
char *origin_fsname;
- if (flags.verbose)
+ if (flags->verbose)
(void) printf("promoting %s\n", fsname);
origin_nvfs = fsavl_find(local_avl, originguid,
@@ -1883,7 +2056,7 @@ again:
if (found == NULL) {
char name[ZFS_MAXNAMELEN];
- if (!flags.force)
+ if (!flags->force)
continue;
(void) snprintf(name, sizeof (name), "%s@%s",
@@ -1941,7 +2114,7 @@ again:
/* check for delete */
if (stream_nvfs == NULL) {
- if (!flags.force)
+ if (!flags->force)
continue;
error = recv_destroy(hdl, fsname, strlen(tofs)+1,
@@ -1954,7 +2127,7 @@ again:
}
if (fromguid == 0) {
- if (flags.verbose) {
+ if (flags->verbose) {
(void) printf("local fs %s does not have "
"fromsnap (%s in stream); must have "
"been deleted locally; ignoring\n",
@@ -1979,7 +2152,7 @@ again:
if ((stream_parent_fromsnap_guid != 0 &&
parent_fromsnap_guid != 0 &&
stream_parent_fromsnap_guid != parent_fromsnap_guid) ||
- ((flags.isprefix || strcmp(tofs, fsname) != 0) &&
+ ((flags->isprefix || strcmp(tofs, fsname) != 0) &&
(s1 != NULL) && (s2 != NULL) && strcmp(s1, s2) != 0)) {
nvlist_t *parent;
char tryname[ZFS_MAXNAMELEN];
@@ -2002,7 +2175,7 @@ again:
"%s%s", pname, strrchr(stream_fsname, '/'));
} else {
tryname[0] = '\0';
- if (flags.verbose) {
+ if (flags->verbose) {
(void) printf("local fs %s new parent "
"not found\n", fsname);
}
@@ -2030,7 +2203,7 @@ again:
if (needagain && progress) {
/* do another pass to fix up temporary names */
- if (flags.verbose)
+ if (flags->verbose)
(void) printf("another pass:\n");
goto again;
}
@@ -2040,7 +2213,7 @@ again:
static int
zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
- recvflags_t flags, dmu_replay_record_t *drr, zio_cksum_t *zc,
+ recvflags_t *flags, dmu_replay_record_t *drr, zio_cksum_t *zc,
char **top_zfs, int cleanup_fd, uint64_t *action_handlep)
{
nvlist_t *stream_nv = NULL;
@@ -2069,7 +2242,7 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
*/
if (drr->drr_payloadlen != 0) {
error = recv_read_nvlist(hdl, fd, drr->drr_payloadlen,
- &stream_nv, flags.byteswap, zc);
+ &stream_nv, flags->byteswap, zc);
if (error) {
error = zfs_error(hdl, EZFS_BADSTREAM, errbuf);
goto out;
@@ -2090,9 +2263,9 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
* Read in the end record and verify checksum.
*/
if (0 != (error = recv_read(hdl, fd, &drre, sizeof (drre),
- flags.byteswap, NULL)))
+ flags->byteswap, NULL)))
goto out;
- if (flags.byteswap) {
+ if (flags->byteswap) {
drre.drr_type = BSWAP_32(drre.drr_type);
drre.drr_u.drr_end.drr_checksum.zc_word[0] =
BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[0]);
@@ -2133,11 +2306,11 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
nvpair_t *pair = NULL;
(void) strlcpy(tofs, destname, ZFS_MAXNAMELEN);
- if (flags.isprefix) {
+ if (flags->isprefix) {
struct drr_begin *drrb = &drr->drr_u.drr_begin;
int i;
- if (flags.istail) {
+ if (flags->istail) {
cp = strrchr(drrb->drr_toname, '/');
if (cp == NULL) {
(void) strlcat(tofs, "/",
@@ -2155,7 +2328,7 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
*strchr(tofs, '@') = '\0';
}
- if (recursive && !flags.dryrun && !flags.nomount) {
+ if (recursive && !flags->dryrun && !flags->nomount) {
VERIFY(0 == nvlist_alloc(&renamed,
NV_UNIQUE_NAME, 0));
}
@@ -2329,7 +2502,7 @@ recv_skip(libzfs_handle_t *hdl, int fd, boolean_t byteswap)
*/
static int
zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
- recvflags_t flags, dmu_replay_record_t *drr,
+ recvflags_t *flags, dmu_replay_record_t *drr,
dmu_replay_record_t *drr_noswap, const char *sendfs,
nvlist_t *stream_nv, avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
uint64_t *action_handlep)
@@ -2371,7 +2544,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
if (err)
VERIFY(0 == nvlist_alloc(&props, NV_UNIQUE_NAME, 0));
- if (flags.canmountoff) {
+ if (flags->canmountoff) {
VERIFY(0 == nvlist_add_uint64(props,
zfs_prop_to_name(ZFS_PROP_CANMOUNT), 0));
}
@@ -2398,7 +2571,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
* If they specified a snapshot, chop the entire name stored in
* the stream.
*/
- if (flags.istail) {
+ if (flags->istail) {
/*
* A filesystem was specified with -e. We want to tack on only
* the tail of the sent snapshot path.
@@ -2424,7 +2597,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
} else {
chopprefix = drrb->drr_toname + (chopprefix - sendfs);
}
- } else if (flags.isprefix) {
+ } else if (flags->isprefix) {
/*
* A filesystem was specified with -d. We want to tack on
* everything but the first element of the sent snapshot path
@@ -2478,7 +2651,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
* Determine the name of the origin snapshot, store in zc_string.
*/
if (drrb->drr_flags & DRR_FLAG_CLONE) {
- if (guid_to_name(hdl, tosnap,
+ if (guid_to_name(hdl, zc.zc_value,
drrb->drr_fromguid, zc.zc_string) != 0) {
zcmd_free_nvlists(&zc);
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
@@ -2486,7 +2659,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
zc.zc_value);
return (zfs_error(hdl, EZFS_NOENT, errbuf));
}
- if (flags.verbose)
+ if (flags->verbose)
(void) printf("found clone origin %s\n", zc.zc_string);
}
@@ -2509,7 +2682,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
!zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
char suffix[ZFS_MAXNAMELEN];
(void) strcpy(suffix, strrchr(zc.zc_value, '/'));
- if (guid_to_name(hdl, tosnap, parent_snapguid,
+ if (guid_to_name(hdl, zc.zc_name, parent_snapguid,
zc.zc_value) == 0) {
*strchr(zc.zc_value, '@') = '\0';
(void) strcat(zc.zc_value, suffix);
@@ -2531,12 +2704,12 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
* topmost path in the stream, then if the fs does not exist we
* should look no further.
*/
- if ((flags.isprefix || (*(chopprefix = drrb->drr_toname +
+ if ((flags->isprefix || (*(chopprefix = drrb->drr_toname +
strlen(sendfs)) != '\0' && *chopprefix != '@')) &&
!zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
char snap[ZFS_MAXNAMELEN];
(void) strcpy(snap, strchr(zc.zc_value, '@'));
- if (guid_to_name(hdl, tosnap, drrb->drr_fromguid,
+ if (guid_to_name(hdl, zc.zc_name, drrb->drr_fromguid,
zc.zc_value) == 0) {
*strchr(zc.zc_value, '@') = '\0';
(void) strcat(zc.zc_value, snap);
@@ -2558,7 +2731,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
* snapshots).
*/
if (stream_wantsnewfs) {
- if (!flags.force) {
+ if (!flags->force) {
zcmd_free_nvlists(&zc);
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"destination '%s' exists\n"
@@ -2594,7 +2767,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
return (zfs_error(hdl, EZFS_EXISTS, errbuf));
}
- if (!flags.dryrun && zhp->zfs_type == ZFS_TYPE_FILESYSTEM &&
+ if (!flags->dryrun && zhp->zfs_type == ZFS_TYPE_FILESYSTEM &&
stream_wantsnewfs) {
/* We can't do online recv in this case */
clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0);
@@ -2633,7 +2806,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
*/
*cp = '\0';
- if (flags.isprefix && !flags.istail && !flags.dryrun &&
+ if (flags->isprefix && !flags->istail && !flags->dryrun &&
create_parents(hdl, zc.zc_value, strlen(tosnap)) != 0) {
zcmd_free_nvlists(&zc);
return (zfs_error(hdl, EZFS_BADRESTORE, errbuf));
@@ -2644,18 +2817,18 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
zc.zc_begin_record = drr_noswap->drr_u.drr_begin;
zc.zc_cookie = infd;
- zc.zc_guid = flags.force;
- if (flags.verbose) {
+ zc.zc_guid = flags->force;
+ if (flags->verbose) {
(void) printf("%s %s stream of %s into %s\n",
- flags.dryrun ? "would receive" : "receiving",
+ flags->dryrun ? "would receive" : "receiving",
drrb->drr_fromguid ? "incremental" : "full",
drrb->drr_toname, zc.zc_value);
(void) fflush(stdout);
}
- if (flags.dryrun) {
+ if (flags->dryrun) {
zcmd_free_nvlists(&zc);
- return (recv_skip(hdl, infd, flags.byteswap));
+ return (recv_skip(hdl, infd, flags->byteswap));
}
zc.zc_nvlist_dst = (uint64_t)(uintptr_t)prop_errbuf;
@@ -2736,12 +2909,12 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
nvlist_free(local_nv);
if (fs != NULL) {
- if (flags.verbose) {
+ if (flags->verbose) {
(void) printf("snap %s already exists; "
"ignoring\n", zc.zc_value);
}
err = ioctl_err = recv_skip(hdl, infd,
- flags.byteswap);
+ flags->byteswap);
}
}
*cp = '@';
@@ -2793,7 +2966,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
case EDQUOT:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"destination %s space quota exceeded"), zc.zc_name);
- (void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
+ (void) zfs_error(hdl, EZFS_NOSPC, errbuf);
break;
default:
(void) zfs_standard_error(hdl, ioctl_errno, errbuf);
@@ -2851,7 +3024,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
*action_handlep = zc.zc_action_handle;
- if (flags.verbose) {
+ if (flags->verbose) {
char buf1[64];
char buf2[64];
uint64_t bytes = zc.zc_cookie;
@@ -2869,7 +3042,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
}
static int
-zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags,
+zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t *flags,
int infd, const char *sendfs, nvlist_t *stream_nv, avl_tree_t *stream_avl,
char **top_zfs, int cleanup_fd, uint64_t *action_handlep)
{
@@ -2884,7 +3057,7 @@ zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags,
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"cannot receive"));
- if (flags.isprefix &&
+ if (flags->isprefix &&
!zfs_dataset_exists(hdl, tosnap, ZFS_TYPE_DATASET)) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "specified fs "
"(%s) does not exist"), tosnap);
@@ -2904,7 +3077,7 @@ zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags,
/* the kernel needs the non-byteswapped begin record */
drr_noswap = drr;
- flags.byteswap = B_FALSE;
+ flags->byteswap = B_FALSE;
if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) {
/*
* We computed the checksum in the wrong byteorder in
@@ -2912,7 +3085,7 @@ zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags,
*/
bzero(&zcksum, sizeof (zio_cksum_t));
fletcher_4_incremental_byteswap(&drr, sizeof (drr), &zcksum);
- flags.byteswap = B_TRUE;
+ flags->byteswap = B_TRUE;
drr.drr_type = BSWAP_32(drr.drr_type);
drr.drr_payloadlen = BSWAP_32(drr.drr_payloadlen);
@@ -2980,7 +3153,7 @@ zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags,
* (-1 will override -2).
*/
int
-zfs_receive(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags,
+zfs_receive(libzfs_handle_t *hdl, const char *tosnap, recvflags_t *flags,
int infd, avl_tree_t *stream_avl)
{
char *top_zfs = NULL;
@@ -2996,7 +3169,7 @@ zfs_receive(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags,
VERIFY(0 == close(cleanup_fd));
- if (err == 0 && !flags.nomount && top_zfs) {
+ if (err == 0 && !flags->nomount && top_zfs) {
zfs_handle_t *zhp;
prop_changelist_t *clp;
diff --git a/lib/libzfs/common/libzfs_status.c b/lib/libzfs/common/libzfs_status.c
index 24725ec044ec..af0707a62058 100644
--- a/lib/libzfs/common/libzfs_status.c
+++ b/lib/libzfs/common/libzfs_status.c
@@ -18,8 +18,10 @@
*
* CDDL HEADER END
*/
+
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
*/
/*
@@ -214,6 +216,20 @@ check_status(nvlist_t *config, boolean_t isimport)
return (ZPOOL_STATUS_VERSION_NEWER);
/*
+ * Unsupported feature(s).
+ */
+ if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
+ vs->vs_aux == VDEV_AUX_UNSUP_FEAT) {
+ nvlist_t *nvinfo;
+
+ verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO,
+ &nvinfo) == 0);
+ if (nvlist_exists(nvinfo, ZPOOL_CONFIG_CAN_RDONLY))
+ return (ZPOOL_STATUS_UNSUP_FEAT_WRITE);
+ return (ZPOOL_STATUS_UNSUP_FEAT_READ);
+ }
+
+ /*
* Check that the config is complete.
*/
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
@@ -300,7 +316,7 @@ check_status(nvlist_t *config, boolean_t isimport)
/*
* Outdated, but usable, version
*/
- if (version < SPA_VERSION)
+ if (SPA_VERSION_IS_SUPPORTED(version) && version != SPA_VERSION)
return (ZPOOL_STATUS_VERSION_OLDER);
return (ZPOOL_STATUS_OK);
diff --git a/lib/libzfs/common/libzfs_util.c b/lib/libzfs/common/libzfs_util.c
index 01b7c8732efd..41db2fdd81fa 100644
--- a/lib/libzfs/common/libzfs_util.c
+++ b/lib/libzfs/common/libzfs_util.c
@@ -18,8 +18,10 @@
*
* CDDL HEADER END
*/
+
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
*/
/*
@@ -44,6 +46,7 @@
#include "libzfs_impl.h"
#include "zfs_prop.h"
+#include "zfeature_common.h"
int
libzfs_errno(libzfs_handle_t *hdl)
@@ -111,7 +114,8 @@ libzfs_error_description(libzfs_handle_t *hdl)
case EZFS_RESILVERING:
return (dgettext(TEXT_DOMAIN, "currently resilvering"));
case EZFS_BADVERSION:
- return (dgettext(TEXT_DOMAIN, "unsupported version"));
+ return (dgettext(TEXT_DOMAIN, "unsupported version or "
+ "feature"));
case EZFS_POOLUNAVAIL:
return (dgettext(TEXT_DOMAIN, "pool is unavailable"));
case EZFS_DEVOVERFLOW:
@@ -344,6 +348,7 @@ zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
switch (error) {
case ENXIO:
case ENODEV:
+ case EPIPE:
zfs_verror(hdl, EZFS_IO, fmt, ap);
break;
@@ -627,6 +632,7 @@ libzfs_init(void)
zfs_prop_init();
zpool_prop_init();
+ zpool_feature_init();
libzfs_mnttab_init(hdl);
return (hdl);
@@ -1280,8 +1286,11 @@ addlist(libzfs_handle_t *hdl, char *propname, zprop_list_t **listp,
* this is a pool property or if this isn't a user-defined
* dataset property,
*/
- if (prop == ZPROP_INVAL && (type == ZFS_TYPE_POOL ||
- (!zfs_prop_user(propname) && !zfs_prop_userquota(propname)))) {
+ if (prop == ZPROP_INVAL && ((type == ZFS_TYPE_POOL &&
+ !zpool_prop_feature(propname) &&
+ !zpool_prop_unsupported(propname)) ||
+ (type == ZFS_TYPE_DATASET && !zfs_prop_user(propname) &&
+ !zfs_prop_userquota(propname) && !zfs_prop_written(propname)))) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"invalid property '%s'"), propname);
return (zfs_error(hdl, EZFS_BADPROP,
@@ -1293,7 +1302,8 @@ addlist(libzfs_handle_t *hdl, char *propname, zprop_list_t **listp,
entry->pl_prop = prop;
if (prop == ZPROP_INVAL) {
- if ((entry->pl_user_prop = zfs_strdup(hdl, propname)) == NULL) {
+ if ((entry->pl_user_prop = zfs_strdup(hdl, propname)) ==
+ NULL) {
free(entry);
return (-1);
}
diff --git a/lib/libzpool/common/kernel.c b/lib/libzpool/common/kernel.c
index f323bf60b099..8e1e7f7e649e 100644
--- a/lib/libzpool/common/kernel.c
+++ b/lib/libzpool/common/kernel.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
*/
#include <assert.h>
@@ -45,6 +46,7 @@ int aok;
uint64_t physmem;
vnode_t *rootdir = (vnode_t *)0xabcd1234;
char hw_serial[HW_HOSTID_LEN];
+vmem_t *zio_arena = NULL;
struct utsname utsname = {
"userland", "libzpool", "1", "1", "na"
@@ -424,7 +426,9 @@ vn_rdwr(int uio, vnode_t *vp, void *addr, ssize_t len, offset_t offset,
* To simulate partial disk writes, we split writes into two
* system calls so that the process can be killed in between.
*/
- split = (len > 0 ? rand() % len : 0);
+ int sectors = len >> SPA_MINBLOCKSHIFT;
+ split = (sectors > 0 ? rand() % sectors : 0) <<
+ SPA_MINBLOCKSHIFT;
iolen = pwrite64(vp->v_fd, addr, split, offset);
iolen += pwrite64(vp->v_fd, (char *)addr + split,
len - split, offset + split);
diff --git a/lib/libzpool/common/sys/zfs_context.h b/lib/libzpool/common/sys/zfs_context.h
index 3b0390dca566..1f5e758721b6 100644
--- a/lib/libzpool/common/sys/zfs_context.h
+++ b/lib/libzpool/common/sys/zfs_context.h
@@ -20,6 +20,9 @@
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
*/
#ifndef _SYS_ZFS_CONTEXT_H
@@ -212,6 +215,7 @@ struct proc {
};
extern struct proc p0;
+#define curproc (&p0)
#define PS_NONE -1
@@ -327,9 +331,12 @@ extern void kstat_delete(kstat_t *);
#define kmem_debugging() 0
#define kmem_cache_reap_now(_c) /* nothing */
#define kmem_cache_set_move(_c, _cb) /* nothing */
+#define vmem_qcache_reap(_v) /* nothing */
#define POINTER_INVALIDATE(_pp) /* nothing */
#define POINTER_IS_VALID(_p) 0
+extern vmem_t *zio_arena;
+
typedef umem_cache_t kmem_cache_t;
typedef enum kmem_cbrc {
@@ -347,6 +354,16 @@ typedef struct taskq taskq_t;
typedef uintptr_t taskqid_t;
typedef void (task_func_t)(void *);
+typedef struct taskq_ent {
+ struct taskq_ent *tqent_next;
+ struct taskq_ent *tqent_prev;
+ task_func_t *tqent_func;
+ void *tqent_arg;
+ uintptr_t tqent_flags;
+} taskq_ent_t;
+
+#define TQENT_FLAG_PREALLOC 0x1 /* taskq_dispatch_ent used */
+
#define TASKQ_PREPOPULATE 0x0001
#define TASKQ_CPR_SAFE 0x0002 /* Use CPR safe protocol */
#define TASKQ_DYNAMIC 0x0004 /* Use dynamic thread scheduling */
@@ -358,6 +375,7 @@ typedef void (task_func_t)(void *);
#define TQ_NOQUEUE 0x02 /* Do not enqueue if can't dispatch */
#define TQ_FRONT 0x08 /* Queue in front */
+
extern taskq_t *system_taskq;
extern taskq_t *taskq_create(const char *, int, pri_t, int, int, uint_t);
@@ -366,6 +384,8 @@ extern taskq_t *taskq_create(const char *, int, pri_t, int, int, uint_t);
#define taskq_create_sysdc(a, b, d, e, p, dc, f) \
(taskq_create(a, b, maxclsyspri, d, e, f))
extern taskqid_t taskq_dispatch(taskq_t *, task_func_t, void *, uint_t);
+extern void taskq_dispatch_ent(taskq_t *, task_func_t, void *, uint_t,
+ taskq_ent_t *);
extern void taskq_destroy(taskq_t *);
extern void taskq_wait(taskq_t *);
extern int taskq_member(taskq_t *, void *);
diff --git a/lib/libzpool/common/taskq.c b/lib/libzpool/common/taskq.c
index 8db5d11c1327..2c5dfd86dcc0 100644
--- a/lib/libzpool/common/taskq.c
+++ b/lib/libzpool/common/taskq.c
@@ -22,19 +22,16 @@
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
+/*
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved.
+ */
#include <sys/zfs_context.h>
int taskq_now;
taskq_t *system_taskq;
-typedef struct task {
- struct task *task_next;
- struct task *task_prev;
- task_func_t *task_func;
- void *task_arg;
-} task_t;
-
#define TASKQ_ACTIVE 0x00010000
struct taskq {
@@ -51,18 +48,18 @@ struct taskq {
int tq_maxalloc;
kcondvar_t tq_maxalloc_cv;
int tq_maxalloc_wait;
- task_t *tq_freelist;
- task_t tq_task;
+ taskq_ent_t *tq_freelist;
+ taskq_ent_t tq_task;
};
-static task_t *
+static taskq_ent_t *
task_alloc(taskq_t *tq, int tqflags)
{
- task_t *t;
+ taskq_ent_t *t;
int rv;
again: if ((t = tq->tq_freelist) != NULL && tq->tq_nalloc >= tq->tq_minalloc) {
- tq->tq_freelist = t->task_next;
+ tq->tq_freelist = t->tqent_next;
} else {
if (tq->tq_nalloc >= tq->tq_maxalloc) {
if (!(tqflags & KM_SLEEP))
@@ -87,7 +84,7 @@ again: if ((t = tq->tq_freelist) != NULL && tq->tq_nalloc >= tq->tq_minalloc) {
}
mutex_exit(&tq->tq_lock);
- t = kmem_alloc(sizeof (task_t), tqflags);
+ t = kmem_alloc(sizeof (taskq_ent_t), tqflags);
mutex_enter(&tq->tq_lock);
if (t != NULL)
@@ -97,15 +94,15 @@ again: if ((t = tq->tq_freelist) != NULL && tq->tq_nalloc >= tq->tq_minalloc) {
}
static void
-task_free(taskq_t *tq, task_t *t)
+task_free(taskq_t *tq, taskq_ent_t *t)
{
if (tq->tq_nalloc <= tq->tq_minalloc) {
- t->task_next = tq->tq_freelist;
+ t->tqent_next = tq->tq_freelist;
tq->tq_freelist = t;
} else {
tq->tq_nalloc--;
mutex_exit(&tq->tq_lock);
- kmem_free(t, sizeof (task_t));
+ kmem_free(t, sizeof (taskq_ent_t));
mutex_enter(&tq->tq_lock);
}
@@ -116,7 +113,7 @@ task_free(taskq_t *tq, task_t *t)
taskqid_t
taskq_dispatch(taskq_t *tq, task_func_t func, void *arg, uint_t tqflags)
{
- task_t *t;
+ taskq_ent_t *t;
if (taskq_now) {
func(arg);
@@ -130,26 +127,59 @@ taskq_dispatch(taskq_t *tq, task_func_t func, void *arg, uint_t tqflags)
return (0);
}
if (tqflags & TQ_FRONT) {
- t->task_next = tq->tq_task.task_next;
- t->task_prev = &tq->tq_task;
+ t->tqent_next = tq->tq_task.tqent_next;
+ t->tqent_prev = &tq->tq_task;
} else {
- t->task_next = &tq->tq_task;
- t->task_prev = tq->tq_task.task_prev;
+ t->tqent_next = &tq->tq_task;
+ t->tqent_prev = tq->tq_task.tqent_prev;
}
- t->task_next->task_prev = t;
- t->task_prev->task_next = t;
- t->task_func = func;
- t->task_arg = arg;
+ t->tqent_next->tqent_prev = t;
+ t->tqent_prev->tqent_next = t;
+ t->tqent_func = func;
+ t->tqent_arg = arg;
+ t->tqent_flags = 0;
cv_signal(&tq->tq_dispatch_cv);
mutex_exit(&tq->tq_lock);
return (1);
}
void
+taskq_dispatch_ent(taskq_t *tq, task_func_t func, void *arg, uint_t flags,
+ taskq_ent_t *t)
+{
+ ASSERT(func != NULL);
+ ASSERT(!(tq->tq_flags & TASKQ_DYNAMIC));
+
+ /*
+ * Mark it as a prealloc'd task. This is important
+ * to ensure that we don't free it later.
+ */
+ t->tqent_flags |= TQENT_FLAG_PREALLOC;
+ /*
+ * Enqueue the task to the underlying queue.
+ */
+ mutex_enter(&tq->tq_lock);
+
+ if (flags & TQ_FRONT) {
+ t->tqent_next = tq->tq_task.tqent_next;
+ t->tqent_prev = &tq->tq_task;
+ } else {
+ t->tqent_next = &tq->tq_task;
+ t->tqent_prev = tq->tq_task.tqent_prev;
+ }
+ t->tqent_next->tqent_prev = t;
+ t->tqent_prev->tqent_next = t;
+ t->tqent_func = func;
+ t->tqent_arg = arg;
+ cv_signal(&tq->tq_dispatch_cv);
+ mutex_exit(&tq->tq_lock);
+}
+
+void
taskq_wait(taskq_t *tq)
{
mutex_enter(&tq->tq_lock);
- while (tq->tq_task.task_next != &tq->tq_task || tq->tq_active != 0)
+ while (tq->tq_task.tqent_next != &tq->tq_task || tq->tq_active != 0)
cv_wait(&tq->tq_wait_cv, &tq->tq_lock);
mutex_exit(&tq->tq_lock);
}
@@ -158,27 +188,32 @@ static void *
taskq_thread(void *arg)
{
taskq_t *tq = arg;
- task_t *t;
+ taskq_ent_t *t;
+ boolean_t prealloc;
mutex_enter(&tq->tq_lock);
while (tq->tq_flags & TASKQ_ACTIVE) {
- if ((t = tq->tq_task.task_next) == &tq->tq_task) {
+ if ((t = tq->tq_task.tqent_next) == &tq->tq_task) {
if (--tq->tq_active == 0)
cv_broadcast(&tq->tq_wait_cv);
cv_wait(&tq->tq_dispatch_cv, &tq->tq_lock);
tq->tq_active++;
continue;
}
- t->task_prev->task_next = t->task_next;
- t->task_next->task_prev = t->task_prev;
+ t->tqent_prev->tqent_next = t->tqent_next;
+ t->tqent_next->tqent_prev = t->tqent_prev;
+ t->tqent_next = NULL;
+ t->tqent_prev = NULL;
+ prealloc = t->tqent_flags & TQENT_FLAG_PREALLOC;
mutex_exit(&tq->tq_lock);
rw_enter(&tq->tq_threadlock, RW_READER);
- t->task_func(t->task_arg);
+ t->tqent_func(t->tqent_arg);
rw_exit(&tq->tq_threadlock);
mutex_enter(&tq->tq_lock);
- task_free(tq, t);
+ if (!prealloc)
+ task_free(tq, t);
}
tq->tq_nthreads--;
cv_broadcast(&tq->tq_wait_cv);
@@ -217,8 +252,8 @@ taskq_create(const char *name, int nthreads, pri_t pri,
tq->tq_nthreads = nthreads;
tq->tq_minalloc = minalloc;
tq->tq_maxalloc = maxalloc;
- tq->tq_task.task_next = &tq->tq_task;
- tq->tq_task.task_prev = &tq->tq_task;
+ tq->tq_task.tqent_next = &tq->tq_task;
+ tq->tq_task.tqent_prev = &tq->tq_task;
tq->tq_threadlist = kmem_alloc(nthreads * sizeof (thread_t), KM_SLEEP);
if (flags & TASKQ_PREPOPULATE) {
diff --git a/man/man1m/zdb.1m b/man/man1m/zdb.1m
index 661165d5843a..106dcfbb5918 100644
--- a/man/man1m/zdb.1m
+++ b/man/man1m/zdb.1m
@@ -1,87 +1,484 @@
-'\" te
-.\" Copyright (c) 2004, Sun Microsystems, Inc. All Rights Reserved.
-.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
-.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
-.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH zdb 1M "31 Oct 2005" "SunOS 5.11" "System Administration Commands"
-.SH NAME
-zdb \- ZFS debugger
-.SH SYNOPSIS
-.LP
-.nf
-\fBzdb\fR \fIpool\fR
-.fi
+'\" t
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source. A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright 2012, Richard Lowe.
+.\"
+.TH "ZDB" "1M" "February 15, 2012" "" ""
+
+.SH "NAME"
+\fBzdb\fR - Display zpool debugging and consistency information
+
+.SH "SYNOPSIS"
+\fBzdb\fR [-CumdibcsDvhLXFPA] [-e [-p \fIpath\fR...]] [-t \fItxg\fR]
+ \fIpoolname\fR [\fIobject\fR ...]
+
+.P
+\fBzdb\fR [-divPA] [-e [-p \fIpath\fR...]] \fIdataset\fR [\fIobject\fR ...]
+
+.P
+\fBzdb\fR -m [-LXFPA] [-t \fItxg\fR] [-e [-p \fIpath\fR...]] \fIpoolname\fR
+ [\fIvdev\fR [\fImetaslab\fR ...]]
+
+.P
+\fBzdb\fR -R [-A] [-e [-p \fIpath\fR...]] \fIpoolname\fR
+ \fIvdev\fR:\fIoffset\fR:\fIsize\fR[:\fIflags\fR]
+
+.P
+\fBzdb\fR -S [-AP] [-e [-p \fIpath\fR...]] \fIpoolname\fR
+
+.P
+\fBzdb\fR -l [-uA] \fIdevice\fR
+
+.P
+\fBzdb\fR -C [-A] [-U \fIcache\fR]
+
+.SH "DESCRIPTION"
+The \fBzdb\fR utility displays information about a ZFS pool useful for
+debugging and performs some amount of consistency checking. It is a not a
+general purpose tool and options (and facilities) may change. This is neither
+a fsck(1M) nor an fsdb(1M) utility.
+
+.P
+The output of this command in general reflects the on-disk structure of a ZFS
+pool, and is inherently unstable. The precise output of most invocations is
+not documented, a knowledge of ZFS internals is assumed.
+
+.P
+When operating on an imported and active pool it is possible, though unlikely,
+that zdb may interpret inconsistent pool data and behave erratically.
+
+.SH "OPTIONS"
+Display options:
-.SH DESCRIPTION
.sp
-.LP
-The \fBzdb\fR command is used by support engineers to diagnose failures and
-gather statistics. Since the \fBZFS\fR file system is always consistent on disk
-and is self-repairing, \fBzdb\fR should only be run under the direction by a
-support engineer.
+.ne 2
+.na
+\fB-b\fR
+.ad
+.sp .6
+.RS 4n
+Display statistics regarding the number, size (logical, physical and
+allocated) and deduplication of blocks.
+.RE
+
.sp
-.LP
-If no arguments are specified, \fBzdb\fR, performs basic consistency checks on
-the pool and associated datasets, and report any problems detected.
+.ne 2
+.na
+\fB-c\fR
+.ad
+.sp .6
+.RS 4n
+Verify the checksum of all metadata blocks while printing block statistics
+(see \fB-b\fR).
.sp
-.LP
-Any options supported by this command are internal to Sun and subject to change
-at any time.
-.SH EXIT STATUS
+If specified multiple times, verify the checksums of all blocks.
+.RE
+
.sp
-.LP
-The following exit values are returned:
+.ne 2
+.na
+\fB-C\fR
+.ad
+.sp .6
+.RS 4n
+Display information about the configuration. If specified with no other
+options, instead display information about the cache file
+(\fB/etc/zfs/zpool.cache\fR). To specify the cache file to display, see
+\fB-U\fR.
+.P
+If specified multiple times, and a pool name is also specified display both
+the cached configuration and the on-disk configuration. If specified multiple
+times with \fB-e\fR also display the configuration that would be used were the
+pool to be imported.
+.RE
+
+.sp
+.ne 2
+.na
+\fB-d\fR
+.ad
+.sp .6
+.RS 4n
+Display information about datasets. Specified once, displays basic dataset
+information: ID, create transaction, size, and object count.
+.sp
+If specified multiple times provides greater and greater verbosity.
+.sp
+If object IDs are specified, display information about those specific objects only.
+.RE
+
+.sp
+.ne 2
+.na
+\fB-D\fR
+.ad
+.sp .6
+.RS 4n
+Display deduplication statistics, including the deduplication ratio (dedup),
+compression ratio (compress), inflation due to the zfs copies property
+(copies), and an overall effective ratio (dedup * compress / copies).
+.sp
+If specified twice, display a histogram of deduplication statistics, showing
+the allocated (physically present on disk) and referenced (logically
+referenced in the pool) block counts and sizes by reference count.
+.RE
+
+.sp
+.ne 2
+.na
+\fB-h\fR
+.ad
+.sp .6
+.RS 4n
+Display pool history similar to \fBzpool history\fR, but include internal
+changes, transaction, and dataset information.
+.RE
+
+.sp
+.ne 2
+.na
+\fB-i\fR
+.ad
+.sp .6
+.RS 4n
+Display information about intent log (ZIL) entries relating to each
+dataset. If specified multiple times, display counts of each intent log
+transaction type.
+.RE
+
+.sp
+.ne 2
+.na
+\fB-l\fR \fIdevice\fR
+.ad
+.sp .6
+.RS 4n
+Display the vdev labels from the specified device. If the \fB-u\fR option is
+also specified, also display the uberblocks on this device.
+.RE
+
.sp
.ne 2
-.mk
.na
-\fB\fB0\fR\fR
+\fB-L\fR
.ad
-.RS 5n
-.rt
-The pool is consistent.
+.sp .6
+.RS 4n
+Disable leak tracing and the loading of space maps. By default, \fBzdb\fR
+verifies that all non-free blocks are referenced, which can be very expensive.
.RE
.sp
.ne 2
-.mk
.na
-\fB\fB1\fR\fR
+\fB-m\fR
.ad
-.RS 5n
-.rt
-An error was detected.
+.sp .6
+.RS 4n
+Display the offset, spacemap, and free space of each metaslab.
+When specified twice, also display information about the maximum contiguous
+free space and the percentage of free space in each space map. When specified
+three times display every spacemap record.
.RE
.sp
.ne 2
-.mk
.na
-\fB\fB2\fR\fR
+\fB-R\fR \fIpoolname\fR \fIvdev\fR:\fIoffset\fR:\fIsize\fR[:\fIflags\fR]
.ad
-.RS 5n
-.rt
-Invalid command line options were specified.
+.sp .6
+.RS 4n
+Read and display a block from the specified device. By default the block is
+displayed as a hex dump, but see the description of the \'r\' flag, below.
+.sp
+The block is specified in terms of a colon-separated tuple \fIvdev\fR (an
+integer vdev identifier) \fIoffset\fR (the offset within the vdev) \fIsize\fR
+(the size of the block to read) and, optionally, \fIflags\fR (a set of flags,
+described below).
+
+.sp
+.ne 2
+.na
+\fBb\fR \fIoffset\fR
+.ad
+.sp .6
+.RS 4n
+Print block pointer
.RE
-.SH ATTRIBUTES
.sp
+.ne 2
+.na
+\fBd\fR
+.ad
+.sp .6
+.RS 4n
+Decompress the block
+.RE
+
+.sp
+.ne 2
+.na
+\fBe\fR
+.ad
+.sp .6
+.RS 4n
+Byte swap the block
+.RE
+
+.sp
+.ne 2
+.na
+\fBg\fR
+.ad
+.sp .6
+.RS 4n
+Dump gang block header
+.RE
+
+.sp
+.ne 2
+.na
+\fBi\fR
+.ad
+.sp .6
+.RS 4n
+Dump indirect block
+.RE
+
+.sp
+.ne 2
+.na
+\fBr\fR
+.ad
+.sp .6
+.RS 4n
+Dump raw uninterpreted block data
+.RE
+.RE
+
+.sp
+.ne 2
+.na
+\fB-s\fR
+.ad
+.sp .6
+.RS 4n
+Report statistics on \fBzdb\fR\'s I/O. Display operation counts, bandwidth,
+and error counts of I/O to the pool from \fBzdb\fR.
+.RE
+
+.sp
+.ne 2
+.na
+\fB-S\fR
+.ad
+.sp .6
+.RS 4n
+Simulate the effects of deduplication, constructing a DDT and then display
+that DDT as with \fB-DD\fR.
+.RE
+
+.sp
+.ne 2
+.na
+\fB-u\fR
+.ad
+.sp .6
+.RS 4n
+Display the current uberblock.
+.RE
+
+.P
+Other options:
+
+.sp
+.ne 2
+.na
+\fB-A\fR
+.ad
+.sp .6
+.RS 4n
+Do not abort should any assertion fail.
+.RE
+
+.sp
+.ne 2
+.na
+\fB-AA\fR
+.ad
+.sp .6
+.RS 4n
+Enable panic recovery, certain errors which would otherwise be fatal are
+demoted to warnings.
+.RE
+
+.sp
+.ne 2
+.na
+\fB-AAA\fR
+.ad
+.sp .6
+.RS 4n
+Do not abort if asserts fail and also enable panic recovery.
+.RE
+
+.sp
+.ne 2
+.na
+\fB-e\fR [-p \fIpath\fR]...
+.ad
+.sp .6
+.RS 4n
+Operate on an exported pool, not present in \fB/etc/zfs/zpool.cache\fR. The
+\fB-p\fR flag specifies the path under which devices are to be searched.
+.RE
+
+.sp
+.ne 2
+.na
+\fB-F\fR
+.ad
+.sp .6
+.RS 4n
+Attempt to make an unreadable pool readable by trying progressively older
+transactions.
+.RE
+
+.sp
+.ne 2
+.na
+\fB-P\fR
+.ad
+.sp .6
+.RS 4n
+Print numbers in an unscaled form more amenable to parsing, eg. 1000000 rather
+than 1M.
+.RE
+
+.sp
+.ne 2
+.na
+\fB-t\fR \fItransaction\fR
+.ad
+.sp .6
+.RS 4n
+Specify the highest transaction to use when searching for uberblocks. See also
+the \fB-u\fR and \fB-l\fR options for a means to see the available uberblocks
+and their associated transaction numbers.
+.RE
+
+.sp
+.ne 2
+.na
+\fB-U\fR \fIcachefile\fR
+.ad
+.sp .6
+.RS 4n
+Use a cache file other than \fB/etc/zfs/zpool.cache\fR. This option is only
+valid with \fB-C\fR
+.RE
+
+.sp
+.ne 2
+.na
+\fB-v\fR
+.ad
+.sp .6
+.RS 4n
+Enable verbosity. Specify multiple times for increased verbosity.
+.RE
+
+.sp
+.ne 2
+.na
+\fB-X\fR
+.ad
+.sp .6
+.RS 4n
+Attempt \'extreme\' transaction rewind, that is attempt the same recovery as
+\fB-F\fR but read transactions otherwise deemed too old.
+.RE
+
+.P
+Specifying a display option more than once enables verbosity for only that
+option, with more occurrences enabling more verbosity.
+.P
+If no options are specified, all information about the named pool will be
+displayed at default verbosity.
+
+.SH "EXAMPLES"
.LP
-See \fBattributes\fR(5) for descriptions of the following attributes:
+\fBExample 1 \fRDisplay the configuration of imported pool 'rpool'
.sp
+.in +2
+.nf
+# zdb -C rpool
+MOS Configuration:
+ version: 28
+ name: 'rpool'
+ ...
+.fi
+.in -2
.sp
-.TS
-tab() box;
-cw(2.75i) |cw(2.75i)
-lw(2.75i) |lw(2.75i)
-.
-ATTRIBUTE TYPEATTRIBUTE VALUE
-_
-Interface StabilityUnstable
-.TE
-.SH SEE ALSO
+.LP
+\fBExample 2 \fRDisplay basic dataset information about 'rpool'
+.sp
+.in +2
+.nf
+# zdb -d rpool
+Dataset mos [META], ID 0, cr_txg 4, 26.9M, 1051 objects
+Dataset rpool/swap [ZVOL], ID 59, cr_txg 356, 486M, 2 objects
+ ...
+.fi
+.in -2
.sp
+
.LP
-\fBzfs\fR(1M), \fBzpool\fR(1M), \fBattributes\fR(5)
+\fBExample 3 \fRDisplay basic information about object 0 in
+'rpool/export/home'
+.sp
+.in +2
+.nf
+# zdb -d rpool/export/home 0
+Dataset rpool/export/home [ZPL], ID 137, cr_txg 1546, 32K, 8 objects
+
+ Object lvl iblk dblk dsize lsize %full type
+ 0 7 16K 16K 15.0K 16K 25.00 DMU dnode
+.fi
+.in -2
+.sp
+
+.LP
+\fBExample 4 \fRDisplay the predicted effect of enabling deduplication on 'rpool'
+.sp
+.in +2
+.nf
+# zdb -S rpool
+Simulated DDT histogram:
+
+bucket allocated referenced
+______ ______________________________ ______________________________
+refcnt blocks LSIZE PSIZE DSIZE blocks LSIZE PSIZE DSIZE
+------ ------ ----- ----- ----- ------ ----- ----- -----
+ 1 694K 27.1G 15.0G 15.0G 694K 27.1G 15.0G 15.0G
+ 2 35.0K 1.33G 699M 699M 74.7K 2.79G 1.45G 1.45G
+ ...
+dedup = 1.11, compress = 1.80, copies = 1.00, dedup * compress / copies = 2.00
+.fi
+.in -2
+.sp
+
+.SH "SEE ALSO"
+zfs(1M), zpool(1M)
diff --git a/man/man1m/zfs.1m b/man/man1m/zfs.1m
index 68244c710793..e713566ba2ff 100644
--- a/man/man1m/zfs.1m
+++ b/man/man1m/zfs.1m
@@ -1,12 +1,13 @@
'\" te
.\" Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved.
+.\" Copyright (c) 2012 by Delphix. All rights reserved.
+.\" Copyright (c) 2012 Nexenta Systems, Inc. All Rights Reserved.
+.\" Copyright (c) 2012, Joyent, Inc. All rights reserved.
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License. You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.
.\" See the License for the specific language governing permissions and limitations under the License. When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with
.\" the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License. You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.
-.\" See the License for the specific language governing permissions and limitations under the License. When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with
-.\" the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH zfs 1M "24 Sep 2009" "SunOS 5.11" "System Administration Commands"
+.\" Copyright 2011 Joshua M. Clulow <josh@sysmgr.org>
+.TH ZFS 1M "28 Jul 2011"
.SH NAME
zfs \- configures ZFS file systems
.SH SYNOPSIS
@@ -27,17 +28,17 @@ zfs \- configures ZFS file systems
.LP
.nf
-\fBzfs\fR \fBdestroy\fR [\fB-rRf\fR] \fIfilesystem\fR|\fIvolume\fR
+\fBzfs\fR \fBdestroy\fR [\fB-fnpRrv\fR] \fIfilesystem\fR|\fIvolume\fR
.fi
.LP
.nf
-\fBzfs\fR \fBdestroy\fR [\fB-rRd\fR] \fIsnapshot\fR
+\fBzfs\fR \fBdestroy\fR [\fB-dnpRrv\fR] \fIfilesystem\fR|\fIvolume\fR@\fIsnap\fR[%\fIsnap\fR][,...]
.fi
.LP
.nf
-\fBzfs\fR \fBsnapshot\fR [\fB-r\fR] [\fB-o\fR \fIproperty\fR=\fIvalue\fR]...
+\fBzfs\fR \fBsnapshot\fR [\fB-r\fR] [\fB-o\fR \fIproperty\fR=\fIvalue\fR]...
\fIfilesystem@snapname\fR|\fIvolume@snapname\fR
.fi
@@ -58,13 +59,13 @@ zfs \- configures ZFS file systems
.LP
.nf
-\fBzfs\fR \fBrename\fR \fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR
+\fBzfs\fR \fBrename\fR [\fB-f\fR] \fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR
\fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR
.fi
.LP
.nf
-\fBzfs\fR \fBrename\fR [\fB-p\fR] \fIfilesystem\fR|\fIvolume\fR \fIfilesystem\fR|\fIvolume\fR
+\fBzfs\fR \fBrename\fR [\fB-fp\fR] \fIfilesystem\fR|\fIvolume\fR \fIfilesystem\fR|\fIvolume\fR
.fi
.LP
@@ -85,8 +86,8 @@ zfs \- configures ZFS file systems
.LP
.nf
-\fBzfs\fR \fBget\fR [\fB-r\fR|\fB-d\fR \fIdepth\fR][\fB-Hp\fR][\fB-o\fR \fIfield\fR[,...]] [\fB-s\fR \fIsource\fR[,...]]
- "\fIall\fR" | \fIproperty\fR[,...] \fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR ...
+\fBzfs\fR \fBget\fR [\fB-r\fR|\fB-d\fR \fIdepth\fR][\fB-Hp\fR][\fB-o\fR \fIfield\fR[,...]] [\fB-t\fR \fItype\fR[,...]]
+ [\fB-s\fR \fIsource\fR[,...]] "\fIall\fR" | \fIproperty\fR[,...] \fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR ...
.fi
.LP
@@ -107,18 +108,18 @@ zfs \- configures ZFS file systems
.LP
.nf
\fBzfs\fR \fBuserspace\fR [\fB-niHp\fR] [\fB-o\fR \fIfield\fR[,...]] [\fB-sS\fR \fIfield\fR] ...
- [\fB-t\fR \fItype\fR [,...]] \fIfilesystem\fR|\fIsnapshot\fR
+ [\fB-t\fR \fItype\fR[,...]] \fIfilesystem\fR|\fIsnapshot\fR
.fi
.LP
.nf
\fBzfs\fR \fBgroupspace\fR [\fB-niHp\fR] [\fB-o\fR \fIfield\fR[,...]] [\fB-sS\fR \fIfield\fR] ...
- [\fB-t\fR \fItype\fR [,...]] \fIfilesystem\fR|\fIsnapshot\fR
+ [\fB-t\fR \fItype\fR[,...]] \fIfilesystem\fR|\fIsnapshot\fR
.fi
.LP
.nf
-\fBzfs\fR \fBmount\fR
+\fBzfs\fR \fBmount\fR
.fi
.LP
@@ -143,7 +144,7 @@ zfs \- configures ZFS file systems
.LP
.nf
-\fBzfs\fR \fBsend\fR [\fB-vR\fR] [\fB-\fR[\fBiI\fR] \fIsnapshot\fR] \fIsnapshot\fR
+\fBzfs\fR \fBsend\fR [\fB-DnPpRrv\fR] [\fB-\fR[\fBiI\fR] \fIsnapshot\fR] \fIsnapshot\fR
.fi
.LP
@@ -153,7 +154,7 @@ zfs \- configures ZFS file systems
.LP
.nf
-\fBzfs\fR \fBreceive\fR [\fB-vnFu\fR] \fB-d\fR \fIfilesystem\fR
+\fBzfs\fR \fBreceive\fR [\fB-vnFu\fR] [\fB-d\fR|\fB-e\fR] \fIfilesystem\fR
.fi
.LP
@@ -163,7 +164,7 @@ zfs \- configures ZFS file systems
.LP
.nf
-\fBzfs\fR \fBallow\fR [\fB-ldug\fR] "\fIeveryone\fR"|\fIuser\fR|\fIgroup\fR[,...] \fIperm\fR|\fI@setname\fR[,...]
+\fBzfs\fR \fBallow\fR [\fB-ldug\fR] "\fIeveryone\fR"|\fIuser\fR|\fIgroup\fR[,...] \fIperm\fR|\fI@setname\fR[,...]
\fIfilesystem\fR|\fIvolume\fR
.fi
@@ -184,7 +185,7 @@ zfs \- configures ZFS file systems
.LP
.nf
-\fBzfs\fR \fBunallow\fR [\fB-rldug\fR] "\fIeveryone\fR"|\fIuser\fR|\fIgroup\fR[,...] [\fIperm\fR|@\fIsetname\fR[,... ]]
+\fBzfs\fR \fBunallow\fR [\fB-rldug\fR] "\fIeveryone\fR"|\fIuser\fR|\fIgroup\fR[,...] [\fIperm\fR|@\fIsetname\fR[,... ]]
\fIfilesystem\fR|\fIvolume\fR
.fi
@@ -240,7 +241,6 @@ where the maximum length of a dataset name is \fBMAXNAMELEN\fR (256 bytes).
A dataset can be one of the following:
.sp
.ne 2
-.mk
.na
\fB\fIfile system\fR\fR
.ad
@@ -256,7 +256,6 @@ free space.
.sp
.ne 2
-.mk
.na
\fB\fIvolume\fR\fR
.ad
@@ -269,7 +268,6 @@ most environments.
.sp
.ne 2
-.mk
.na
\fB\fIsnapshot\fR\fR
.ad
@@ -441,7 +439,6 @@ dataset. These properties can be neither set, nor inherited. Native properties
apply to all dataset types unless otherwise noted.
.sp
.ne 2
-.mk
.na
\fB\fBavailable\fR\fR
.ad
@@ -458,20 +455,22 @@ This property can also be referred to by its shortened column name,
.sp
.ne 2
-.mk
.na
\fB\fBcompressratio\fR\fR
.ad
.sp .6
.RS 4n
-The compression ratio achieved for this dataset, expressed as a multiplier.
+For non-snapshots, the compression ratio achieved for the \fBused\fR
+space of this dataset, expressed as a multiplier. The \fBused\fR
+property includes descendant datasets, and, for clones, does not include
+the space shared with the origin snapshot. For snapshots, the
+\fBcompressratio\fR is the same as the \fBrefcompressratio\fR property.
Compression can be turned on by running: \fBzfs set compression=on
\fIdataset\fR\fR. The default value is \fBoff\fR.
.RE
.sp
.ne 2
-.mk
.na
\fB\fBcreation\fR\fR
.ad
@@ -482,7 +481,19 @@ The time this dataset was created.
.sp
.ne 2
-.mk
+.na
+\fB\fBclones\fR\fR
+.ad
+.sp .6
+.RS 4n
+For snapshots, this property is a comma-separated list of filesystems or
+volumes which are clones of this snapshot. The clones' \fBorigin\fR property
+is this snapshot. If the \fBclones\fR property is not empty, then this
+snapshot can not be destroyed (even with the \fB-r\fR or \fB-f\fR options).
+.RE
+
+.sp
+.ne 2
.na
\fB\fBdefer_destroy\fR\fR
.ad
@@ -495,7 +506,6 @@ by using the \fBzfs destroy\fR \fB-d\fR command. Otherwise, the property is
.sp
.ne 2
-.mk
.na
\fB\fBmounted\fR\fR
.ad
@@ -507,20 +517,17 @@ property can be either \fByes\fR or \fBno\fR.
.sp
.ne 2
-.mk
.na
\fB\fBorigin\fR\fR
.ad
.sp .6
.RS 4n
For cloned file systems or volumes, the snapshot from which the clone was
-created. The origin cannot be destroyed (even with the \fB-r\fR or \fB-f\fR
-options) so long as a clone exists.
+created. See also the \fBclones\fR property.
.RE
.sp
.ne 2
-.mk
.na
\fB\fBreferenced\fR\fR
.ad
@@ -537,7 +544,18 @@ This property can also be referred to by its shortened column name,
.sp
.ne 2
-.mk
+.na
+\fB\fBrefcompressratio\fR\fR
+.ad
+.sp .6
+.RS 4n
+The compression ratio achieved for the \fBreferenced\fR space of this
+dataset, expressed as a multiplier. See also the \fBcompressratio\fR
+property.
+.RE
+
+.sp
+.ne 2
.na
\fB\fBtype\fR\fR
.ad
@@ -548,7 +566,6 @@ The type of dataset: \fBfilesystem\fR, \fBvolume\fR, or \fBsnapshot\fR.
.sp
.ne 2
-.mk
.na
\fB\fBused\fR\fR
.ad
@@ -578,7 +595,6 @@ immediately.
.sp
.ne 2
-.mk
.na
\fB\fBusedby*\fR\fR
.ad
@@ -593,7 +609,6 @@ on \fBzpool\fR "version 13" pools.
.sp
.ne 2
-.mk
.na
\fB\fBusedbychildren\fR\fR
.ad
@@ -605,7 +620,6 @@ all the dataset's children were destroyed.
.sp
.ne 2
-.mk
.na
\fB\fBusedbydataset\fR\fR
.ad
@@ -618,7 +632,6 @@ destroying any necessary snapshots or descendents).
.sp
.ne 2
-.mk
.na
\fB\fBusedbyrefreservation\fR\fR
.ad
@@ -630,7 +643,6 @@ would be freed if the \fBrefreservation\fR was removed.
.sp
.ne 2
-.mk
.na
\fB\fBusedbysnapshots\fR\fR
.ad
@@ -644,7 +656,6 @@ properties because space can be shared by multiple snapshots.
.sp
.ne 2
-.mk
.na
\fB\fBuserused@\fR\fIuser\fR\fR
.ad
@@ -690,7 +701,6 @@ following forms:
.sp
.ne 2
-.mk
.na
\fB\fBuserrefs\fR\fR
.ad
@@ -702,7 +712,6 @@ are set by using the \fBzfs hold\fR command.
.sp
.ne 2
-.mk
.na
\fB\fBgroupused@\fR\fIgroup\fR\fR
.ad
@@ -719,7 +728,6 @@ allow\fR, can access all groups' usage.
.sp
.ne 2
-.mk
.na
\fB\fBvolblocksize\fR=\fIblocksize\fR\fR
.ad
@@ -735,12 +743,41 @@ This property can also be referred to by its shortened column name,
.RE
.sp
+.ne 2
+.na
+\fB\fBwritten\fR\fR
+.ad
+.sp .6
+.RS 4n
+The amount of \fBreferenced\fR space written to this dataset since the
+previous snapshot.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBwritten@\fR\fIsnapshot\fR\fR
+.ad
+.sp .6
+.RS 4n
+The amount of \fBreferenced\fR space written to this dataset since the
+specified snapshot. This is the space that is referenced by this dataset
+but was not referenced by the specified snapshot.
+.sp
+The \fIsnapshot\fR may be specified as a short snapshot name (just the part
+after the \fB@\fR), in which case it will be interpreted as a snapshot in
+the same filesystem as this dataset.
+The \fIsnapshot\fR be a full snapshot name (\fIfilesystem\fR@\fIsnapshot\fR),
+which for clones may be a snapshot in the origin's filesystem (or the origin
+of the origin's filesystem, etc).
+.RE
+
+.sp
.LP
The following native properties can be used to change the behavior of a
\fBZFS\fR dataset.
.sp
.ne 2
-.mk
.na
\fB\fBaclinherit\fR=\fBdiscard\fR | \fBnoallow\fR | \fBrestricted\fR |
\fBpassthrough\fR | \fBpassthrough-x\fR\fR
@@ -769,28 +806,24 @@ mode from the application.
.sp
.ne 2
-.mk
.na
\fB\fBaclmode\fR=\fBdiscard\fR | \fBgroupmask\fR | \fBpassthrough\fR\fR
.ad
.sp .6
.RS 4n
Controls how an \fBACL\fR is modified during \fBchmod\fR(2). A file system with
-an \fBaclmode\fR property of \fBdiscard\fR deletes all \fBACL\fR entries that
-do not represent the mode of the file. An \fBaclmode\fR property of
-\fBgroupmask\fR (the default) reduces user or group permissions. The
-permissions are reduced, such that they are no greater than the group
-permission bits, unless it is a user entry that has the same \fBUID\fR as the
-owner of the file or directory. In this case, the \fBACL\fR permissions are
-reduced so that they are no greater than owner permission bits. A file system
-with an \fBaclmode\fR property of \fBpassthrough\fR indicates that no changes
-are made to the \fBACL\fR other than generating the necessary \fBACL\fR entries
-to represent the new mode of the file or directory.
+an \fBaclmode\fR property of \fBdiscard\fR (the default) deletes all \fBACL\fR
+entries that do not represent the mode of the file. An \fBaclmode\fR property
+of \fBgroupmask\fR reduces permissions granted in all \fBALLOW\fR entries found
+in the \fBACL\fR such that they are no greater than the group permissions
+specified by \fBchmod\fR. A file system with an \fBaclmode\fR property of
+\fBpassthrough\fR indicates that no changes are made to the \fBACL\fR other
+than creating or updating the necessary \fBACL\fR entries to
+represent the new mode of the file or directory.
.RE
.sp
.ne 2
-.mk
.na
\fB\fBatime\fR=\fBon\fR | \fBoff\fR\fR
.ad
@@ -804,7 +837,6 @@ and other similar utilities. The default value is \fBon\fR.
.sp
.ne 2
-.mk
.na
\fB\fBcanmount\fR=\fBon\fR | \fBoff\fR | \fBnoauto\fR\fR
.ad
@@ -830,7 +862,6 @@ This property is not inherited.
.sp
.ne 2
-.mk
.na
\fB\fBchecksum\fR=\fBon\fR | \fBoff\fR | \fBfletcher2,\fR| \fBfletcher4\fR |
\fBsha256\fR\fR
@@ -848,10 +879,9 @@ Changing this property affects only newly-written data.
.sp
.ne 2
-.mk
.na
\fB\fBcompression\fR=\fBon\fR | \fBoff\fR | \fBlzjb\fR | \fBgzip\fR |
-\fBgzip-\fR\fIN\fR\fR
+\fBgzip-\fR\fIN\fR | \fBzle\fR\fR
.ad
.sp .6
.RS 4n
@@ -862,7 +892,8 @@ algorithm. The \fBgzip\fR compression algorithm uses the same compression as
the \fBgzip\fR(1) command. You can specify the \fBgzip\fR level by using the
value \fBgzip-\fR\fIN\fR where \fIN\fR is an integer from 1 (fastest) to 9
(best compression ratio). Currently, \fBgzip\fR is equivalent to \fBgzip-6\fR
-(which is also the default for \fBgzip\fR(1)).
+(which is also the default for \fBgzip\fR(1)). The \fBzle\fR compression
+algorithm compresses runs of zeros.
.sp
This property can also be referred to by its shortened column name
\fBcompress\fR. Changing this property affects only newly-written data.
@@ -870,7 +901,6 @@ This property can also be referred to by its shortened column name
.sp
.ne 2
-.mk
.na
\fB\fBcopies\fR=\fB1\fR | \fB2\fR | \fB3\fR\fR
.ad
@@ -889,7 +919,6 @@ property at file system creation time by using the \fB-o\fR
.sp
.ne 2
-.mk
.na
\fB\fBdevices\fR=\fBon\fR | \fBoff\fR\fR
.ad
@@ -901,7 +930,6 @@ value is \fBon\fR.
.sp
.ne 2
-.mk
.na
\fB\fBexec\fR=\fBon\fR | \fBoff\fR\fR
.ad
@@ -913,7 +941,6 @@ default value is \fBon\fR.
.sp
.ne 2
-.mk
.na
\fB\fBmountpoint\fR=\fIpath\fR | \fBnone\fR | \fBlegacy\fR\fR
.ad
@@ -933,7 +960,6 @@ new location.
.sp
.ne 2
-.mk
.na
\fB\fBnbmand\fR=\fBon\fR | \fBoff\fR\fR
.ad
@@ -947,7 +973,6 @@ property only take effect when the file system is umounted and remounted. See
.sp
.ne 2
-.mk
.na
\fB\fBprimarycache\fR=\fBall\fR | \fBnone\fR | \fBmetadata\fR\fR
.ad
@@ -962,7 +987,6 @@ is set to \fBmetadata\fR, then only metadata is cached. The default value is
.sp
.ne 2
-.mk
.na
\fB\fBquota\fR=\fIsize\fR | \fBnone\fR\fR
.ad
@@ -980,7 +1004,6 @@ implicit quota.
.sp
.ne 2
-.mk
.na
\fB\fBuserquota@\fR\fIuser\fR=\fIsize\fR | \fBnone\fR\fR
.ad
@@ -1030,7 +1053,6 @@ displayed by \fBzfs get all\fR. The user's name must be appended after the
.sp
.ne 2
-.mk
.na
\fB\fBgroupquota@\fR\fIgroup\fR=\fIsize\fR | \fBnone\fR\fR
.ad
@@ -1046,7 +1068,6 @@ allow\fR, can get and set all groups' quotas.
.sp
.ne 2
-.mk
.na
\fB\fBreadonly\fR=\fBon\fR | \fBoff\fR\fR
.ad
@@ -1060,7 +1081,6 @@ This property can also be referred to by its shortened column name,
.sp
.ne 2
-.mk
.na
\fB\fBrecordsize\fR=\fIsize\fR\fR
.ad
@@ -1089,7 +1109,6 @@ This property can also be referred to by its shortened column name,
.sp
.ne 2
-.mk
.na
\fB\fBrefquota\fR=\fIsize\fR | \fBnone\fR\fR
.ad
@@ -1102,7 +1121,6 @@ by descendents, including file systems and snapshots.
.sp
.ne 2
-.mk
.na
\fB\fBrefreservation\fR=\fIsize\fR | \fBnone\fR\fR
.ad
@@ -1125,7 +1143,6 @@ This property can also be referred to by its shortened column name,
.sp
.ne 2
-.mk
.na
\fB\fBreservation\fR=\fIsize\fR | \fBnone\fR\fR
.ad
@@ -1143,7 +1160,6 @@ This property can also be referred to by its shortened column name,
.sp
.ne 2
-.mk
.na
\fB\fBsecondarycache\fR=\fBall\fR | \fBnone\fR | \fBmetadata\fR\fR
.ad
@@ -1158,7 +1174,6 @@ value is \fBall\fR.
.sp
.ne 2
-.mk
.na
\fB\fBsetuid\fR=\fBon\fR | \fBoff\fR\fR
.ad
@@ -1170,7 +1185,6 @@ default value is \fBon\fR.
.sp
.ne 2
-.mk
.na
\fB\fBshareiscsi\fR=\fBon\fR | \fBoff\fR\fR
.ad
@@ -1189,7 +1203,6 @@ setting this property on a file system has no direct effect.
.sp
.ne 2
-.mk
.na
\fB\fBsharesmb\fR=\fBon\fR | \fBoff\fR | \fIopts\fR\fR
.ad
@@ -1228,7 +1241,6 @@ are unshared.
.sp
.ne 2
-.mk
.na
\fB\fBsharenfs\fR=\fBon\fR | \fBoff\fR | \fIopts\fR\fR
.ad
@@ -1252,7 +1264,6 @@ unshared.
.sp
.ne 2
-.mk
.na
\fB\fBlogbias\fR = \fBlatency\fR | \fBthroughput\fR\fR
.ad
@@ -1268,7 +1279,6 @@ efficient use of resources.
.sp
.ne 2
-.mk
.na
\fB\fBsnapdir\fR=\fBhidden\fR | \fBvisible\fR\fR
.ad
@@ -1281,7 +1291,26 @@ the file system as discussed in the "Snapshots" section. The default value is
.sp
.ne 2
-.mk
+.na
+\fB\fBsync\fR=\fBdefault\fR | \fBalways\fR | \fBdisabled\fR\fR
+.ad
+.sp .6
+.RS 4n
+Controls the behavior of synchronous requests (e.g. fsync, O_DSYNC).
+\fBdefault\fR is the POSIX specified behavior of ensuring all synchronous
+requests are written to stable storage and all devices are flushed to ensure
+data is not cached by device controllers (this is the default). \fBalways\fR
+causes every file system transaction to be written and flushed before its
+system call returns. This has a large performance penalty. \fBdisabled\fR
+disables synchronous requests. File system transactions are only committed to
+stable storage periodically. This option will give the highest performance.
+However, it is very dangerous as ZFS would be ignoring the synchronous
+transaction demands of applications such as databases or NFS. Administrators
+should only use this option when the risks are understood.
+.RE
+
+.sp
+.ne 2
.na
\fB\fBversion\fR=\fB1\fR | \fB2\fR | \fBcurrent\fR\fR
.ad
@@ -1294,7 +1323,6 @@ version. This property can only be set to later supported versions. See the
.sp
.ne 2
-.mk
.na
\fB\fBvolsize\fR=\fIsize\fR\fR
.ad
@@ -1325,7 +1353,6 @@ reflected in the reservation.
.sp
.ne 2
-.mk
.na
\fB\fBvscan\fR=\fBon\fR | \fBoff\fR\fR
.ad
@@ -1339,7 +1366,6 @@ service must also be enabled for virus scanning to occur. The default value is
.sp
.ne 2
-.mk
.na
\fB\fBxattr\fR=\fBon\fR | \fBoff\fR\fR
.ad
@@ -1351,7 +1377,6 @@ default value is \fBon\fR.
.sp
.ne 2
-.mk
.na
\fB\fBzoned\fR=\fBon\fR | \fBoff\fR\fR
.ad
@@ -1372,7 +1397,6 @@ features being supported, the new file system will have the default values for
these properties.
.sp
.ne 2
-.mk
.na
\fB\fBcasesensitivity\fR=\fBsensitive\fR | \fBinsensitive\fR | \fBmixed\fR\fR
.ad
@@ -1394,7 +1418,6 @@ product. For more information about the \fBmixed\fR value behavior, see the
.sp
.ne 2
-.mk
.na
\fB\fBnormalization\fR = \fBnone\fR | \fBformC\fR | \fBformD\fR | \fBformKC\fR
| \fBformKD\fR\fR
@@ -1413,7 +1436,6 @@ cannot be changed after the file system is created.
.sp
.ne 2
-.mk
.na
\fB\fButf8only\fR=\fBon\fR | \fBoff\fR\fR
.ad
@@ -1517,7 +1539,6 @@ All subcommands that modify state are logged persistently to the pool in their
original form.
.sp
.ne 2
-.mk
.na
\fB\fBzfs ?\fR\fR
.ad
@@ -1528,7 +1549,6 @@ Displays a help message.
.sp
.ne 2
-.mk
.na
\fB\fBzfs create\fR [\fB-p\fR] [\fB-o\fR \fIproperty\fR=\fIvalue\fR] ...
\fIfilesystem\fR\fR
@@ -1539,7 +1559,6 @@ Creates a new \fBZFS\fR file system. The file system is automatically mounted
according to the \fBmountpoint\fR property inherited from the parent.
.sp
.ne 2
-.mk
.na
\fB\fB-p\fR\fR
.ad
@@ -1554,7 +1573,6 @@ operation completes successfully.
.sp
.ne 2
-.mk
.na
\fB\fB-o\fR \fIproperty\fR=\fIvalue\fR\fR
.ad
@@ -1571,7 +1589,6 @@ property is specified in multiple \fB-o\fR options.
.sp
.ne 2
-.mk
.na
\fB\fBzfs create\fR [\fB-ps\fR] [\fB-b\fR \fIblocksize\fR] [\fB-o\fR
\fIproperty\fR=\fIvalue\fR] ... \fB-V\fR \fIsize\fR \fIvolume\fR\fR
@@ -1587,7 +1604,6 @@ exported by the device. By default, a reservation of equal size is created.
the volume has an integral number of blocks regardless of \fIblocksize\fR.
.sp
.ne 2
-.mk
.na
\fB\fB-p\fR\fR
.ad
@@ -1602,7 +1618,6 @@ operation completes successfully.
.sp
.ne 2
-.mk
.na
\fB\fB-s\fR\fR
.ad
@@ -1614,7 +1629,6 @@ Properties section for more information about sparse volumes.
.sp
.ne 2
-.mk
.na
\fB\fB-o\fR \fIproperty\fR=\fIvalue\fR\fR
.ad
@@ -1629,7 +1643,6 @@ multiple \fB-o\fR options.
.sp
.ne 2
-.mk
.na
\fB\fB-b\fR \fIblocksize\fR\fR
.ad
@@ -1644,9 +1657,8 @@ behavior is undefined.
.sp
.ne 2
-.mk
.na
-\fB\fBzfs destroy\fR [\fB-rRf\fR] \fIfilesystem\fR|\fIvolume\fR\fR
+\fBzfs destroy\fR [\fB-fnpRrv\fR] \fIfilesystem\fR|\fIvolume\fR
.ad
.sp .6
.RS 4n
@@ -1656,7 +1668,6 @@ mounted, and refuses to destroy a dataset that has active dependents (children
or clones).
.sp
.ne 2
-.mk
.na
\fB\fB-r\fR\fR
.ad
@@ -1667,7 +1678,6 @@ Recursively destroy all children.
.sp
.ne 2
-.mk
.na
\fB\fB-R\fR\fR
.ad
@@ -1679,7 +1689,6 @@ target hierarchy.
.sp
.ne 2
-.mk
.na
\fB\fB-f\fR\fR
.ad
@@ -1689,30 +1698,72 @@ Force an unmount of any file systems using the \fBunmount -f\fR command. This
option has no effect on non-file systems or unmounted file systems.
.RE
-Extreme care should be taken when applying either the \fB-r\fR or the \fB-f\fR
+.sp
+.ne 2
+.na
+\fB\fB-n\fR\fR
+.ad
+.sp .6
+.RS 4n
+Do a dry-run ("No-op") deletion. No data will be deleted. This is
+useful in conjunction with the \fB-v\fR or \fB-p\fR flags to determine what
+data would be deleted.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-p\fR\fR
+.ad
+.sp .6
+.RS 4n
+Print machine-parsable verbose information about the deleted data.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-v\fR\fR
+.ad
+.sp .6
+.RS 4n
+Print verbose information about the deleted data.
+.RE
+.sp
+Extreme care should be taken when applying either the \fB-r\fR or the \fB-R\fR
options, as they can destroy large portions of a pool and cause unexpected
behavior for mounted file systems in use.
.RE
.sp
.ne 2
-.mk
.na
-\fB\fBzfs destroy\fR [\fB-rRd\fR] \fIsnapshot\fR\fR
+\fBzfs destroy\fR [\fB-dnpRrv\fR] \fIfilesystem\fR|\fIvolume\fR@\fIsnap\fR[%\fIsnap\fR][,...]
.ad
.sp .6
.RS 4n
-The given snapshot is destroyed immediately if and only if the \fBzfs
+The given snapshots are destroyed immediately if and only if the \fBzfs
destroy\fR command without the \fB-d\fR option would have destroyed it. Such
immediate destruction would occur, for example, if the snapshot had no clones
and the user-initiated reference count were zero.
.sp
-If the snapshot does not qualify for immediate destruction, it is marked for
+If a snapshot does not qualify for immediate destruction, it is marked for
deferred deletion. In this state, it exists as a usable, visible snapshot until
both of the preconditions listed above are met, at which point it is destroyed.
.sp
+An inclusive range of snapshots may be specified by separating the
+first and last snapshots with a percent sign.
+The first and/or last snapshots may be left blank, in which case the
+filesystem's oldest or newest snapshot will be implied.
+.sp
+Multiple snapshots
+(or ranges of snapshots) of the same filesystem or volume may be specified
+in a comma-separated list of snapshots.
+Only the snapshot's short name (the
+part after the \fB@\fR) should be specified when using a range or
+comma-separated list to identify multiple snapshots.
+.sp
.ne 2
-.mk
.na
\fB\fB-d\fR\fR
.ad
@@ -1723,7 +1774,6 @@ Defer snapshot deletion.
.sp
.ne 2
-.mk
.na
\fB\fB-r\fR\fR
.ad
@@ -1735,7 +1785,6 @@ descendent file systems.
.sp
.ne 2
-.mk
.na
\fB\fB-R\fR\fR
.ad
@@ -1744,11 +1793,48 @@ descendent file systems.
Recursively destroy all dependents.
.RE
+.sp
+.ne 2
+.na
+\fB\fB-n\fR\fR
+.ad
+.sp .6
+.RS 4n
+Do a dry-run ("No-op") deletion. No data will be deleted. This is
+useful in conjunction with the \fB-v\fR or \fB-p\fR flags to determine what
+data would be deleted.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-p\fR\fR
+.ad
+.sp .6
+.RS 4n
+Print machine-parsable verbose information about the deleted data.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-v\fR\fR
+.ad
+.sp .6
+.RS 4n
+Print verbose information about the deleted data.
+.RE
+
+.sp
+Extreme care should be taken when applying either the \fB-r\fR or the \fB-f\fR
+options, as they can destroy large portions of a pool and cause unexpected
+behavior for mounted file systems in use.
+.RE
+
.RE
.sp
.ne 2
-.mk
.na
\fB\fBzfs snapshot\fR [\fB-r\fR] [\fB-o\fR \fIproperty\fR=\fIvalue\fR] ...
\fIfilesystem@snapname\fR|\fIvolume@snapname\fR\fR
@@ -1760,7 +1846,6 @@ successful system calls to the file system are part of the snapshot. See the
"Snapshots" section for details.
.sp
.ne 2
-.mk
.na
\fB\fB-r\fR\fR
.ad
@@ -1773,7 +1858,6 @@ time.
.sp
.ne 2
-.mk
.na
\fB\fB-o\fR \fIproperty\fR=\fIvalue\fR\fR
.ad
@@ -1786,7 +1870,6 @@ Sets the specified property; see \fBzfs create\fR for details.
.sp
.ne 2
-.mk
.na
\fB\fBzfs rollback\fR [\fB-rRf\fR] \fIsnapshot\fR\fR
.ad
@@ -1805,7 +1888,6 @@ either of these options. To completely roll back a recursive snapshot, you must
rollback the individual child snapshots.
.sp
.ne 2
-.mk
.na
\fB\fB-r\fR\fR
.ad
@@ -1816,7 +1898,6 @@ Recursively destroy any snapshots more recent than the one specified.
.sp
.ne 2
-.mk
.na
\fB\fB-R\fR\fR
.ad
@@ -1828,7 +1909,6 @@ snapshots.
.sp
.ne 2
-.mk
.na
\fB\fB-f\fR\fR
.ad
@@ -1842,7 +1922,6 @@ that are to be destroyed.
.sp
.ne 2
-.mk
.na
\fB\fBzfs clone\fR [\fB-p\fR] [\fB-o\fR \fIproperty\fR=\fIvalue\fR] ...
\fIsnapshot\fR \fIfilesystem\fR|\fIvolume\fR\fR
@@ -1854,7 +1933,6 @@ The target dataset can be located anywhere in the \fBZFS\fR hierarchy, and is
created as the same type as the original.
.sp
.ne 2
-.mk
.na
\fB\fB-p\fR\fR
.ad
@@ -1868,7 +1946,6 @@ operation completes successfully.
.sp
.ne 2
-.mk
.na
\fB\fB-o\fR \fIproperty\fR=\fIvalue\fR\fR
.ad
@@ -1881,7 +1958,6 @@ Sets the specified property; see \fBzfs create\fR for details.
.sp
.ne 2
-.mk
.na
\fB\fBzfs promote\fR \fIclone-filesystem\fR\fR
.ad
@@ -1903,9 +1979,8 @@ any conflicting snapshots.
.sp
.ne 2
-.mk
.na
-\fB\fBzfs rename\fR \fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR\fR
+\fB\fBzfs rename\fR [\fB-f\fR] \fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR\fR
.ad
.br
.na
@@ -1913,7 +1988,7 @@ any conflicting snapshots.
.ad
.br
.na
-\fB\fBzfs rename\fR [\fB-p\fR] \fIfilesystem\fR|\fIvolume\fR
+\fB\fBzfs rename\fR [\fB-fp\fR] \fIfilesystem\fR|\fIvolume\fR
\fIfilesystem\fR|\fIvolume\fR\fR
.ad
.sp .6
@@ -1926,7 +2001,6 @@ second argument. Renamed file systems can inherit new mount points, in which
case they are unmounted and remounted at the new mount point.
.sp
.ne 2
-.mk
.na
\fB\fB-p\fR\fR
.ad
@@ -1937,11 +2011,20 @@ are automatically mounted according to the \fBmountpoint\fR property inherited
from their parent.
.RE
+.sp
+.ne 2
+.na
+\fB\fB-f\fR\fR
+.ad
+.sp .6
+.RS 4n
+Force unmount any filesystems that need to be unmounted in the process.
+.RE
+
.RE
.sp
.ne 2
-.mk
.na
\fB\fBzfs rename\fR \fB-r\fR \fIsnapshot\fR \fIsnapshot\fR\fR
.ad
@@ -1953,7 +2036,6 @@ only dataset that can be renamed recursively.
.sp
.ne 2
-.mk
.na
\fB\fBzfs\fR \fBlist\fR [\fB-r\fR|\fB-d\fR \fIdepth\fR] [\fB-H\fR] [\fB-o\fR
\fIproperty\fR[,\fI\&...\fR]] [ \fB-t\fR \fItype\fR[,\fI\&...\fR]] [ \fB-s\fR
@@ -1970,7 +2052,6 @@ default is \fBoff\fR) . The following fields are displayed,
\fBname,used,available,referenced,mountpoint\fR.
.sp
.ne 2
-.mk
.na
\fB\fB-H\fR\fR
.ad
@@ -1982,7 +2063,6 @@ tab instead of arbitrary white space.
.sp
.ne 2
-.mk
.na
\fB\fB-r\fR\fR
.ad
@@ -1993,7 +2073,6 @@ Recursively display any children of the dataset on the command line.
.sp
.ne 2
-.mk
.na
\fB\fB-d\fR \fIdepth\fR\fR
.ad
@@ -2006,7 +2085,6 @@ children.
.sp
.ne 2
-.mk
.na
\fB\fB-o\fR \fIproperty\fR\fR
.ad
@@ -2044,7 +2122,6 @@ filesystem,volume\fR syntax.
.sp
.ne 2
-.mk
.na
\fB\fB-s\fR \fIproperty\fR\fR
.ad
@@ -2088,7 +2165,6 @@ preserved.
.sp
.ne 2
-.mk
.na
\fB\fB-S\fR \fIproperty\fR\fR
.ad
@@ -2099,7 +2175,6 @@ Same as the \fB-s\fR option, but sorts by property in descending order.
.sp
.ne 2
-.mk
.na
\fB\fB-t\fR \fItype\fR\fR
.ad
@@ -2114,7 +2189,6 @@ specifying \fB-t snapshot\fR displays only snapshots.
.sp
.ne 2
-.mk
.na
\fB\fBzfs set\fR \fIproperty\fR=\fIvalue\fR
\fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR ...\fR
@@ -2133,10 +2207,9 @@ Properties" section.
.sp
.ne 2
-.mk
.na
\fB\fBzfs get\fR [\fB-r\fR|\fB-d\fR \fIdepth\fR] [\fB-Hp\fR] [\fB-o\fR
-\fIfield\fR[,...] [\fB-s\fR \fIsource\fR[,...] "\fIall\fR" |
+\fIfield\fR[,...] [\fB-t\fR \fItype\fR[,...]] [\fB-s\fR \fIsource\fR[,...] "\fIall\fR" |
\fIproperty\fR[,...] \fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR ...\fR
.ad
.sp .6
@@ -2164,7 +2237,6 @@ The special value \fBall\fR can be used to display all properties that apply to
the given dataset's type (filesystem, volume, or snapshot).
.sp
.ne 2
-.mk
.na
\fB\fB-r\fR\fR
.ad
@@ -2175,7 +2247,6 @@ Recursively display properties for any children.
.sp
.ne 2
-.mk
.na
\fB\fB-d\fR \fIdepth\fR\fR
.ad
@@ -2188,7 +2259,6 @@ children.
.sp
.ne 2
-.mk
.na
\fB\fB-H\fR\fR
.ad
@@ -2201,7 +2271,6 @@ arbitrary amount of space.
.sp
.ne 2
-.mk
.na
\fB\fB-o\fR \fIfield\fR\fR
.ad
@@ -2213,7 +2282,6 @@ is the default value.
.sp
.ne 2
-.mk
.na
\fB\fB-s\fR \fIsource\fR\fR
.ad
@@ -2227,7 +2295,6 @@ is all sources.
.sp
.ne 2
-.mk
.na
\fB\fB-p\fR\fR
.ad
@@ -2240,7 +2307,6 @@ Display numbers in parseable (exact) values.
.sp
.ne 2
-.mk
.na
\fB\fBzfs inherit\fR [\fB-r\fR] \fIproperty\fR
\fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR ...\fR
@@ -2253,7 +2319,6 @@ no ancestor has the property set, then the default value is used. See the
properties can be inherited.
.sp
.ne 2
-.mk
.na
\fB\fB-r\fR\fR
.ad
@@ -2266,7 +2331,6 @@ Recursively inherit the given property for all children.
.sp
.ne 2
-.mk
.na
\fB\fBzfs upgrade\fR [\fB-v\fR]\fR
.ad
@@ -2277,7 +2341,6 @@ Displays a list of file systems that are not the most recent version.
.sp
.ne 2
-.mk
.na
\fB\fBzfs upgrade\fR [\fB-r\fR] [\fB-V\fR \fIversion\fR] [\fB-a\fR |
\fIfilesystem\fR]\fR
@@ -2297,7 +2360,6 @@ and the pool version must be upgraded before the file system version can be
upgraded.
.sp
.ne 2
-.mk
.na
\fB\fB-a\fR\fR
.ad
@@ -2308,7 +2370,6 @@ Upgrade all file systems on all imported pools.
.sp
.ne 2
-.mk
.na
\fB\fIfilesystem\fR\fR
.ad
@@ -2319,7 +2380,6 @@ Upgrade the specified file system.
.sp
.ne 2
-.mk
.na
\fB\fB-r\fR\fR
.ad
@@ -2330,7 +2390,6 @@ Upgrade the specified file system and all descendent file systems
.sp
.ne 2
-.mk
.na
\fB\fB-V\fR \fIversion\fR\fR
.ad
@@ -2346,7 +2405,6 @@ supported by this software.
.sp
.ne 2
-.mk
.na
\fB\fBzfs userspace\fR [\fB-niHp\fR] [\fB-o\fR \fIfield\fR[,...]] [\fB-sS\fR
\fIfield\fR]... [\fB-t\fR \fItype\fR [,...]] \fIfilesystem\fR |
@@ -2359,7 +2417,6 @@ filesystem or snapshot. This corresponds to the \fBuserused@\fR\fIuser\fR and
\fBuserquota@\fR\fIuser\fR properties.
.sp
.ne 2
-.mk
.na
\fB\fB-n\fR\fR
.ad
@@ -2370,7 +2427,6 @@ Print numeric ID instead of user/group name.
.sp
.ne 2
-.mk
.na
\fB\fB-H\fR\fR
.ad
@@ -2381,7 +2437,6 @@ Do not print headers, use tab-delimited output.
.sp
.ne 2
-.mk
.na
\fB\fB-p\fR\fR
.ad
@@ -2392,7 +2447,6 @@ Use exact (parseable) numeric output.
.sp
.ne 2
-.mk
.na
\fB\fB-o\fR \fIfield\fR[,...]\fR
.ad
@@ -2404,7 +2458,6 @@ Display only the specified fields from the following set,
.sp
.ne 2
-.mk
.na
\fB\fB-s\fR \fIfield\fR\fR
.ad
@@ -2417,7 +2470,6 @@ multiple times to sort first by one field, then by another. The default is
.sp
.ne 2
-.mk
.na
\fB\fB-S\fR \fIfield\fR\fR
.ad
@@ -2428,7 +2480,6 @@ Sort by this field in reverse order. See \fB-s\fR.
.sp
.ne 2
-.mk
.na
\fB\fB-t\fR \fItype\fR[,...]\fR
.ad
@@ -2444,7 +2495,6 @@ The default can be changed to include group types.
.sp
.ne 2
-.mk
.na
\fB\fB-i\fR\fR
.ad
@@ -2464,7 +2514,6 @@ will report that the POSIX entity has the total usage and quota for both.
.sp
.ne 2
-.mk
.na
\fB\fBzfs groupspace\fR [\fB-niHp\fR] [\fB-o\fR \fIfield\fR[,...]] [\fB-sS\fR
\fIfield\fR]... [\fB-t\fR \fItype\fR [,...]] \fIfilesystem\fR |
@@ -2487,7 +2536,6 @@ except that the default types to display are \fB-t posixgroup,smbgroup\fR.
.sp
.ne 2
-.mk
.na
\fB\fBzfs mount\fR\fR
.ad
@@ -2498,7 +2546,6 @@ Displays all \fBZFS\fR file systems currently mounted.
.sp
.ne 2
-.mk
.na
\fB\fBzfs mount\fR [\fB-vO\fR] [\fB-o\fR \fIoptions\fR] \fB-a\fR |
\fIfilesystem\fR\fR
@@ -2509,7 +2556,6 @@ Mounts \fBZFS\fR file systems. Invoked automatically as part of the boot
process.
.sp
.ne 2
-.mk
.na
\fB\fB-o\fR \fIoptions\fR\fR
.ad
@@ -2522,7 +2568,6 @@ details.
.sp
.ne 2
-.mk
.na
\fB\fB-O\fR\fR
.ad
@@ -2533,7 +2578,6 @@ Perform an overlay mount. See \fBmount\fR(1M) for more information.
.sp
.ne 2
-.mk
.na
\fB\fB-v\fR\fR
.ad
@@ -2544,7 +2588,6 @@ Report mount progress.
.sp
.ne 2
-.mk
.na
\fB\fB-a\fR\fR
.ad
@@ -2556,7 +2599,6 @@ the boot process.
.sp
.ne 2
-.mk
.na
\fB\fIfilesystem\fR\fR
.ad
@@ -2569,7 +2611,6 @@ Mount the specified filesystem.
.sp
.ne 2
-.mk
.na
\fB\fBzfs unmount\fR [\fB-f\fR] \fB-a\fR | \fIfilesystem\fR|\fImountpoint\fR\fR
.ad
@@ -2579,7 +2620,6 @@ Unmounts currently mounted \fBZFS\fR file systems. Invoked automatically as
part of the shutdown process.
.sp
.ne 2
-.mk
.na
\fB\fB-f\fR\fR
.ad
@@ -2590,7 +2630,6 @@ Forcefully unmount the file system, even if it is currently in use.
.sp
.ne 2
-.mk
.na
\fB\fB-a\fR\fR
.ad
@@ -2602,7 +2641,6 @@ the boot process.
.sp
.ne 2
-.mk
.na
\fB\fIfilesystem\fR|\fImountpoint\fR\fR
.ad
@@ -2616,7 +2654,6 @@ Unmount the specified filesystem. The command can also be given a path to a
.sp
.ne 2
-.mk
.na
\fB\fBzfs share\fR \fB-a\fR | \fIfilesystem\fR\fR
.ad
@@ -2625,7 +2662,6 @@ Unmount the specified filesystem. The command can also be given a path to a
Shares available \fBZFS\fR file systems.
.sp
.ne 2
-.mk
.na
\fB\fB-a\fR\fR
.ad
@@ -2637,7 +2673,6 @@ the boot process.
.sp
.ne 2
-.mk
.na
\fB\fIfilesystem\fR\fR
.ad
@@ -2652,7 +2687,6 @@ Share the specified filesystem according to the \fBsharenfs\fR and
.sp
.ne 2
-.mk
.na
\fB\fBzfs unshare\fR \fB-a\fR | \fIfilesystem\fR|\fImountpoint\fR\fR
.ad
@@ -2662,7 +2696,6 @@ Unshares currently shared \fBZFS\fR file systems. This is invoked automatically
as part of the shutdown process.
.sp
.ne 2
-.mk
.na
\fB\fB-a\fR\fR
.ad
@@ -2674,7 +2707,6 @@ the boot process.
.sp
.ne 2
-.mk
.na
\fB\fIfilesystem\fR|\fImountpoint\fR\fR
.ad
@@ -2688,10 +2720,8 @@ Unshare the specified filesystem. The command can also be given a path to a
.sp
.ne 2
-.mk
.na
-\fB\fBzfs send\fR [\fB-vR\fR] [\fB-\fR[\fBiI\fR] \fIsnapshot\fR]
-\fIsnapshot\fR\fR
+\fBzfs send\fR [\fB-DnPpRrv\fR] [\fB-\fR[\fBiI\fR] \fIsnapshot\fR] \fIsnapshot\fR
.ad
.sp .6
.RS 4n
@@ -2701,7 +2731,6 @@ system (for example, using \fBssh\fR(1). By default, a full stream is
generated.
.sp
.ne 2
-.mk
.na
\fB\fB-i\fR \fIsnapshot\fR\fR
.ad
@@ -2720,7 +2749,6 @@ must be fully specified (for example, \fBpool/fs@origin\fR, not just
.sp
.ne 2
-.mk
.na
\fB\fB-I\fR \fIsnapshot\fR\fR
.ad
@@ -2734,7 +2762,6 @@ be specified as with the \fB-i\fR option.
.sp
.ne 2
-.mk
.na
\fB\fB-R\fR\fR
.ad
@@ -2754,13 +2781,73 @@ snapshots and file systems that do not exist on the sending side are destroyed.
.sp
.ne 2
-.mk
+.na
+\fB\fB-D\fR\fR
+.ad
+.sp .6
+.RS 4n
+Generate a deduplicated stream. Blocks which would have been sent multiple
+times in the send stream will only be sent once. The receiving system must
+also support this feature to recieve a deduplicated stream. This flag can
+be used regardless of the dataset's \fBdedup\fR property, but performance
+will be much better if the filesystem uses a dedup-capable checksum (eg.
+\fBsha256\fR).
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-r\fR\fR
+.ad
+.sp .6
+.RS 4n
+Recursively send all descendant snapshots. This is similar to the \fB-R\fR
+flag, but information about deleted and renamed datasets is not included, and
+property information is only included if the \fB-p\fR flag is specified.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-p\fR\fR
+.ad
+.sp .6
+.RS 4n
+Include the dataset's properties in the stream. This flag is implicit when
+\fB-R\fR is specified. The receiving system must also support this feature.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-n\fR\fR
+.ad
+.sp .6
+.RS 4n
+Do a dry-run ("No-op") send. Do not generate any actual send data. This is
+useful in conjunction with the \fB-v\fR or \fB-P\fR flags to determine what
+data will be sent.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-P\fR\fR
+.ad
+.sp .6
+.RS 4n
+Print machine-parsable verbose information about the stream package generated.
+.RE
+
+.sp
+.ne 2
.na
\fB\fB-v\fR\fR
.ad
.sp .6
.RS 4n
-Print verbose information about the stream package generated.
+Print verbose information about the stream package generated. This information
+includes a per-second report of how much data has been sent.
.RE
The format of the stream is committed. You will be able to receive your streams
@@ -2769,14 +2856,13 @@ on future versions of \fBZFS\fR.
.sp
.ne 2
-.mk
.na
\fB\fBzfs receive\fR [\fB-vnFu\fR]
\fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR\fR
.ad
.br
.na
-\fB\fBzfs receive\fR [\fB-vnFu\fR] \fB-d\fR \fIfilesystem\fR\fR
+\fB\fBzfs receive\fR [\fB-vnFu\fR] [\fB-d\fR|\fB-e\fR] \fIfilesystem\fR\fR
.ad
.sp .6
.RS 4n
@@ -2798,30 +2884,49 @@ on the sending location are destroyed by using the \fBzfs destroy\fR \fB-d\fR
command.
.sp
The name of the snapshot (and file system, if a full stream is received) that
-this subcommand creates depends on the argument type and the \fB-d\fR option.
+this subcommand creates depends on the argument type and the use of the
+\fB-d\fR or \fB-e\fR options.
.sp
If the argument is a snapshot name, the specified \fIsnapshot\fR is created. If
the argument is a file system or volume name, a snapshot with the same name as
the sent snapshot is created within the specified \fIfilesystem\fR or
-\fIvolume\fR. If the \fB-d\fR option is specified, the snapshot name is
-determined by appending the sent snapshot's name to the specified
-\fIfilesystem\fR. If the \fB-d\fR option is specified, any required file
-systems within the specified one are created.
+\fIvolume\fR. If neither of the \fB-d\fR or \fB-e\fR options are specified,
+the provided target snapshot name is used exactly as provided.
+.sp
+The \fB-d\fR and \fB-e\fR options cause the file system name of the target
+snapshot to be determined by appending a portion of the sent snapshot's name to
+the specified target \fIfilesystem\fR. If the \fB-d\fR option is specified, all
+but the first element of the sent snapshot's file system path (usually the
+pool name) is used and any required intermediate file systems within the
+specified one are created. If the \fB-e\fR option is specified, then only the
+last element of the sent snapshot's file system name (i.e. the name of the
+source file system itself) is used as the target file system name.
.sp
.ne 2
-.mk
.na
\fB\fB-d\fR\fR
.ad
.sp .6
.RS 4n
-Use the name of the sent snapshot to determine the name of the new snapshot as
-described in the paragraph above.
+Discard the first element of the sent snapshot's file system name, using
+the remaining elements to determine the name of the target file system for
+the new snapshot as described in the paragraph above.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-e\fR\fR
+.ad
+.sp .6
+.RS 4n
+Discard all but the last element of the sent snapshot's file system name,
+using that element to determine the name of the target file system for
+the new snapshot as described in the paragraph above.
.RE
.sp
.ne 2
-.mk
.na
\fB\fB-u\fR\fR
.ad
@@ -2832,7 +2937,6 @@ File system that is associated with the received stream is not mounted.
.sp
.ne 2
-.mk
.na
\fB\fB-v\fR\fR
.ad
@@ -2844,7 +2948,6 @@ receive operation.
.sp
.ne 2
-.mk
.na
\fB\fB-n\fR\fR
.ad
@@ -2856,7 +2959,6 @@ Do not actually receive the stream. This can be useful in conjunction with the
.sp
.ne 2
-.mk
.na
\fB\fB-F\fR\fR
.ad
@@ -2872,7 +2974,6 @@ snapshots and file systems that do not exist on the sending side.
.sp
.ne 2
-.mk
.na
\fB\fBzfs allow\fR \fIfilesystem\fR | \fIvolume\fR\fR
.ad
@@ -2884,7 +2985,6 @@ volume. See the other forms of \fBzfs allow\fR for more information.
.sp
.ne 2
-.mk
.na
\fB\fBzfs allow\fR [\fB-ldug\fR] "\fIeveryone\fR"|\fIuser\fR|\fIgroup\fR[,...]
\fIperm\fR|@\fIsetname\fR[,...] \fIfilesystem\fR| \fIvolume\fR\fR
@@ -2900,7 +3000,6 @@ Delegates \fBZFS\fR administration permission for the file systems to
non-privileged users.
.sp
.ne 2
-.mk
.na
\fB[\fB-ug\fR] "\fIeveryone\fR"|\fIuser\fR|\fIgroup\fR[,...]\fR
.ad
@@ -2916,7 +3015,6 @@ group with the same name as a user, use the \fB-g\fR options.
.sp
.ne 2
-.mk
.na
\fB[\fB-e\fR] \fIperm\fR|@\fIsetname\fR[,...]\fR
.ad
@@ -2931,7 +3029,6 @@ set names, which begin with an at sign (\fB@\fR) , may be specified. See the
.sp
.ne 2
-.mk
.na
\fB[\fB-ld\fR] \fIfilesystem\fR|\fIvolume\fR\fR
.ad
@@ -2968,7 +3065,7 @@ receive subcommand Must also have the 'mount' and 'create' ability
rename subcommand Must also have the 'mount' and 'create'
ability in the new parent
rollback subcommand Must also have the 'mount' ability
-send subcommand
+send subcommand
share subcommand Allows sharing file systems over NFS or SMB
protocols
snapshot subcommand Must also have the 'mount' ability
@@ -2978,46 +3075,45 @@ userprop other Allows changing any user property
userquota other Allows accessing any userquota@... property
userused other Allows reading any userused@... property
-aclinherit property
-aclmode property
-atime property
-canmount property
-casesensitivity property
-checksum property
-compression property
-copies property
-devices property
-exec property
-mountpoint property
-nbmand property
-normalization property
-primarycache property
-quota property
-readonly property
-recordsize property
-refquota property
-refreservation property
-reservation property
-secondarycache property
-setuid property
-shareiscsi property
-sharenfs property
-sharesmb property
-snapdir property
-utf8only property
-version property
-volblocksize property
-volsize property
-vscan property
-xattr property
-zoned property
+aclinherit property
+aclmode property
+atime property
+canmount property
+casesensitivity property
+checksum property
+compression property
+copies property
+devices property
+exec property
+mountpoint property
+nbmand property
+normalization property
+primarycache property
+quota property
+readonly property
+recordsize property
+refquota property
+refreservation property
+reservation property
+secondarycache property
+setuid property
+shareiscsi property
+sharenfs property
+sharesmb property
+snapdir property
+utf8only property
+version property
+volblocksize property
+volsize property
+vscan property
+xattr property
+zoned property
.fi
.in -2
.sp
.sp
.ne 2
-.mk
.na
\fB\fBzfs allow\fR \fB-c\fR \fIperm\fR|@\fIsetname\fR[,...]
\fIfilesystem\fR|\fIvolume\fR\fR
@@ -3030,7 +3126,6 @@ creator of any newly-created descendent file system.
.sp
.ne 2
-.mk
.na
\fB\fBzfs allow\fR \fB-s\fR @\fIsetname\fR \fIperm\fR|@\fIsetname\fR[,...]
\fIfilesystem\fR|\fIvolume\fR\fR
@@ -3047,7 +3142,6 @@ characters long.
.sp
.ne 2
-.mk
.na
\fB\fBzfs unallow\fR [\fB-rldug\fR]
"\fIeveryone\fR"|\fIuser\fR|\fIgroup\fR[,...]
@@ -3078,7 +3172,6 @@ not all permissions for every user and group. See the \fBzfs allow\fR command
for a description of the \fB-ldugec\fR options.
.sp
.ne 2
-.mk
.na
\fB\fB-r\fR\fR
.ad
@@ -3091,7 +3184,6 @@ Recursively remove the permissions from this file system and all descendents.
.sp
.ne 2
-.mk
.na
\fB\fBzfs unallow\fR [\fB-r\fR] \fB-s\fR @\fIsetname\fR
[\fIperm\fR|@\fIsetname\fR[,...]]\fR
@@ -3108,7 +3200,6 @@ then all permissions are removed, thus removing the set entirely.
.sp
.ne 2
-.mk
.na
\fB\fBzfs hold\fR [\fB-r\fR] \fItag\fR \fIsnapshot\fR...\fR
.ad
@@ -3122,7 +3213,6 @@ If a hold exists on a snapshot, attempts to destroy that snapshot by using the
\fBzfs destroy\fR command return \fBEBUSY\fR.
.sp
.ne 2
-.mk
.na
\fB\fB-r\fR\fR
.ad
@@ -3136,7 +3226,6 @@ snapshots of all descendent file systems.
.sp
.ne 2
-.mk
.na
\fB\fBzfs holds\fR [\fB-r\fR] \fIsnapshot\fR...\fR
.ad
@@ -3145,7 +3234,6 @@ snapshots of all descendent file systems.
Lists all existing user references for the given snapshot or snapshots.
.sp
.ne 2
-.mk
.na
\fB\fB-r\fR\fR
.ad
@@ -3159,7 +3247,6 @@ listing the holds on the named snapshot.
.sp
.ne 2
-.mk
.na
\fB\fBzfs release\fR [\fB-r\fR] \fItag\fR \fIsnapshot\fR...\fR
.ad
@@ -3172,7 +3259,6 @@ If a hold exists on a snapshot, attempts to destroy that snapshot by using the
\fBzfs destroy\fR command return \fBEBUSY\fR.
.sp
.ne 2
-.mk
.na
\fB\fB-r\fR\fR
.ad
@@ -3323,7 +3409,7 @@ pool/home/bob setuid on default
pool/home/bob readonly off default
pool/home/bob zoned off default
pool/home/bob snapdir hidden default
-pool/home/bob aclmode groupmask default
+pool/home/bob aclmode discard default
pool/home/bob aclinherit restricted default
pool/home/bob canmount on default
pool/home/bob shareiscsi off default
@@ -3517,7 +3603,7 @@ target.
# \fBzfs set shareiscsi=on pool/volumes/vol1\fR
# \fBiscsitadm list target\fR
Target: pool/volumes/vol1
- iSCSI Name:
+ iSCSI Name:
iqn.1986-03.com.sun:02:7b4b02a6-3277-eb1b-e686-a24762c52a8c
Connections: 0
.fi
@@ -3629,7 +3715,7 @@ Create time permissions on (tank/users)
create,destroy
Local+Descendent permissions on (tank/users)
group staff create,mount
--------------------------------------------------------------
+-------------------------------------------------------------
.fi
.in -2
.sp
@@ -3680,7 +3766,7 @@ Local+Descendent permissions on (users/home)
cindys% \fBzfs set quota=10G users/home/marks\fR
cindys% \fBzfs get quota users/home/marks\fR
NAME PROPERTY VALUE SOURCE
-users/home/marks quota 10G local
+users/home/marks quota 10G local
.fi
.in -2
.sp
@@ -3705,7 +3791,7 @@ Create time permissions on (tank/users)
create,destroy
Local+Descendent permissions on (tank/users)
group staff @pset,create,mount
--------------------------------------------------------------
+-------------------------------------------------------------
.fi
.in -2
.sp
@@ -3716,7 +3802,6 @@ Local+Descendent permissions on (tank/users)
The following exit values are returned:
.sp
.ne 2
-.mk
.na
\fB\fB0\fR\fR
.ad
@@ -3727,7 +3812,6 @@ Successful completion.
.sp
.ne 2
-.mk
.na
\fB\fB1\fR\fR
.ad
@@ -3738,7 +3822,6 @@ An error occurred.
.sp
.ne 2
-.mk
.na
\fB\fB2\fR\fR
.ad
@@ -3755,13 +3838,12 @@ See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
.TS
-tab() box;
-cw(2.75i) |cw(2.75i)
-lw(2.75i) |lw(2.75i)
-.
-ATTRIBUTE TYPEATTRIBUTE VALUE
+box;
+c | c
+l | l .
+ATTRIBUTE TYPE ATTRIBUTE VALUE
_
-Interface StabilityCommitted
+Interface Stability Committed
.TE
.SH SEE ALSO
diff --git a/man/man1m/zpool.1m b/man/man1m/zpool.1m
index 7a67781be25a..a2a9b723ccb7 100644
--- a/man/man1m/zpool.1m
+++ b/man/man1m/zpool.1m
@@ -1,9 +1,20 @@
'\" te
.\" Copyright (c) 2007, Sun Microsystems, Inc. All Rights Reserved.
-.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License. You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.
-.\" See the License for the specific language governing permissions and limitations under the License. When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the
-.\" fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH zpool 1M "21 Sep 2009" "SunOS 5.11" "System Administration Commands"
+.\" Copyright 2011, Nexenta Systems, Inc. All Rights Reserved.
+.\" Copyright (c) 2012 by Delphix. All rights reserved.
+.\" The contents of this file are subject to the terms of the Common Development
+.\" and Distribution License (the "License"). You may not use this file except
+.\" in compliance with the License. You can obtain a copy of the license at
+.\" usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.
+.\"
+.\" See the License for the specific language governing permissions and
+.\" limitations under the License. When distributing Covered Code, include this
+.\" CDDL HEADER in each file and include the License file at
+.\" usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this
+.\" CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your
+.\" own identifying information:
+.\" Portions Copyright [yyyy] [name of copyright owner]
+.TH ZPOOL 1M "Mar 16, 2012"
.SH NAME
zpool \- configures ZFS storage pools
.SH SYNOPSIS
@@ -29,7 +40,7 @@ zpool \- configures ZFS storage pools
.LP
.nf
-\fBzpool create\fR [\fB-fn\fR] [\fB-o\fR \fIproperty=value\fR] ... [\fB-O\fR \fIfile-system-property=value\fR]
+\fBzpool create\fR [\fB-fnd\fR] [\fB-o\fR \fIproperty=value\fR] ... [\fB-O\fR \fIfile-system-property=value\fR]
... [\fB-m\fR \fImountpoint\fR] [\fB-R\fR \fIroot\fR] \fIpool\fR \fIvdev\fR ...
.fi
@@ -65,7 +76,7 @@ zpool \- configures ZFS storage pools
.LP
.nf
-\fBzpool import\fR [\fB-o \fImntopts\fR\fR] [\fB-o\fR \fIproperty=value\fR] ... [\fB-d\fR \fIdir\fR | \fB-c\fR \fIcachefile\fR]
+\fBzpool import\fR [\fB-o \fImntopts\fR\fR] [\fB-o\fR \fIproperty=value\fR] ... [\fB-d\fR \fIdir\fR | \fB-c\fR \fIcachefile\fR]
[\fB-D\fR] [\fB-f\fR] [\fB-R\fR \fIroot\fR] \fB-a\fR
.fi
@@ -82,7 +93,7 @@ zpool \- configures ZFS storage pools
.LP
.nf
-\fBzpool list\fR [\fB-H\fR] [\fB-o\fR \fIproperty\fR[,...]] [\fIpool\fR] ...
+\fBzpool list\fR [\fB-Hv\fR] [\fB-o\fR \fIproperty\fR[,...]] [\fIpool\fR] ...
.fi
.LP
@@ -97,6 +108,11 @@ zpool \- configures ZFS storage pools
.LP
.nf
+\fBzpool reguid\fR \fIpool\fR
+.fi
+
+.LP
+.nf
\fBzpool remove\fR \fIpool\fR \fIdevice\fR ...
.fi
@@ -122,7 +138,7 @@ zpool \- configures ZFS storage pools
.LP
.nf
-\fBzpool upgrade\fR
+\fBzpool upgrade\fR
.fi
.LP
@@ -153,12 +169,10 @@ organized according to certain performance and fault characteristics. The
following virtual devices are supported:
.sp
.ne 2
-.mk
.na
\fB\fBdisk\fR\fR
.ad
.RS 10n
-.rt
A block device, typically located under \fB/dev/dsk\fR. \fBZFS\fR can use
individual slices or partitions, though the recommended mode of operation is to
use whole disks. A disk can be specified by a full path, or it can be a
@@ -170,12 +184,10 @@ disk, \fBZFS\fR automatically labels the disk, if necessary.
.sp
.ne 2
-.mk
.na
\fB\fBfile\fR\fR
.ad
.RS 10n
-.rt
A regular file. The use of files as a backing store is strongly discouraged. It
is designed primarily for experimental purposes, as the fault tolerance of a
file is only as good as the file system of which it is a part. A file must be
@@ -184,12 +196,10 @@ specified by a full path.
.sp
.ne 2
-.mk
.na
\fB\fBmirror\fR\fR
.ad
.RS 10n
-.rt
A mirror of two or more devices. Data is replicated in an identical fashion
across all components of a mirror. A mirror with \fIN\fR disks of size \fIX\fR
can hold \fIX\fR bytes and can withstand (\fIN-1\fR) devices failing before
@@ -198,7 +208,6 @@ data integrity is compromised.
.sp
.ne 2
-.mk
.na
\fB\fBraidz\fR\fR
.ad
@@ -215,7 +224,6 @@ data integrity is compromised.
\fB\fBraidz3\fR\fR
.ad
.RS 10n
-.rt
A variation on \fBRAID-5\fR that allows for better distribution of parity and
eliminates the "\fBRAID-5\fR write hole" (in which data and parity become
inconsistent after a power loss). Data and parity is striped across all disks
@@ -238,24 +246,20 @@ disks. The recommended number is between 3 and 9 to help increase performance.
.sp
.ne 2
-.mk
.na
\fB\fBspare\fR\fR
.ad
.RS 10n
-.rt
A special pseudo-\fBvdev\fR which keeps track of available hot spares for a
pool. For more information, see the "Hot Spares" section.
.RE
.sp
.ne 2
-.mk
.na
\fB\fBlog\fR\fR
.ad
.RS 10n
-.rt
A separate-intent log device. If more than one log device is specified, then
writes are load-balanced between devices. Log devices can be mirrored. However,
\fBraidz\fR \fBvdev\fR types are not supported for the intent log. For more
@@ -264,12 +268,10 @@ information, see the "Intent Log" section.
.sp
.ne 2
-.mk
.na
\fB\fBcache\fR\fR
.ad
.RS 10n
-.rt
A device used to cache storage pool data. A cache device cannot be cannot be
configured as a mirror or \fBraidz\fR group. For more information, see the
"Cache Devices" section.
@@ -329,12 +331,10 @@ devices. A top-level vdev or component device is in one of the following
states:
.sp
.ne 2
-.mk
.na
\fB\fBDEGRADED\fR\fR
.ad
.RS 12n
-.rt
One or more top-level vdevs is in the degraded state because one or more
component devices are offline. Sufficient replicas exist to continue
functioning.
@@ -362,12 +362,10 @@ functioning.
.sp
.ne 2
-.mk
.na
\fB\fBFAULTED\fR\fR
.ad
.RS 12n
-.rt
One or more top-level vdevs is in the faulted state because one or more
component devices are offline. Insufficient replicas exist to continue
functioning.
@@ -392,46 +390,38 @@ prevent further use of the device.
.sp
.ne 2
-.mk
.na
\fB\fBOFFLINE\fR\fR
.ad
.RS 12n
-.rt
The device was explicitly taken offline by the "\fBzpool offline\fR" command.
.RE
.sp
.ne 2
-.mk
.na
\fB\fBONLINE\fR\fR
.ad
.RS 12n
-.rt
The device is online and functioning.
.RE
.sp
.ne 2
-.mk
.na
\fB\fBREMOVED\fR\fR
.ad
.RS 12n
-.rt
The device was physically removed while the system was running. Device removal
detection is hardware-dependent and may not be supported on all platforms.
.RE
.sp
.ne 2
-.mk
.na
\fB\fBUNAVAIL\fR\fR
.ad
.RS 12n
-.rt
The device could not be opened. If a pool is imported when a device was
unavailable, then the device will be identified by a unique identifier instead
of its path since the path was never correct in the first place.
@@ -545,76 +535,107 @@ read-only statistics while others are configurable and change the behavior of
the pool. The following are read-only properties:
.sp
.ne 2
-.mk
.na
\fB\fBavailable\fR\fR
.ad
.RS 20n
-.rt
Amount of storage available within the pool. This property can also be referred
to by its shortened column name, "avail".
.RE
.sp
.ne 2
-.mk
.na
\fB\fBcapacity\fR\fR
.ad
.RS 20n
-.rt
Percentage of pool space used. This property can also be referred to by its
shortened column name, "cap".
.RE
.sp
.ne 2
-.mk
+.na
+\fB\fBexpandsize\fR\fR
+.ad
+.RS 20n
+Amount of uninitialized space within the pool or device that can be used to
+increase the total capacity of the pool. Uninitialized space consists of
+any space on an EFI labeled vdev which has not been brought online
+(i.e. zpool online -e). This space occurs when a LUN is dynamically expanded.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBfree\fR\fR
+.ad
+.RS 20n
+The amount of free space available in the pool.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBfreeing\fR\fR
+.ad
+.RS 20n
+After a file system or snapshot is destroyed, the space it was using is
+returned to the pool asynchronously. \fB\fBfreeing\fR\fR is the amount of
+space remaining to be reclaimed. Over time \fB\fBfreeing\fR\fR will decrease
+while \fB\fBfree\fR\fR increases.
+.RE
+
+.sp
+.ne 2
.na
\fB\fBhealth\fR\fR
.ad
.RS 20n
-.rt
The current health of the pool. Health can be "\fBONLINE\fR", "\fBDEGRADED\fR",
"\fBFAULTED\fR", " \fBOFFLINE\fR", "\fBREMOVED\fR", or "\fBUNAVAIL\fR".
.RE
.sp
.ne 2
-.mk
.na
\fB\fBguid\fR\fR
.ad
.RS 20n
-.rt
A unique identifier for the pool.
.RE
.sp
.ne 2
-.mk
.na
\fB\fBsize\fR\fR
.ad
.RS 20n
-.rt
Total size of the storage pool.
.RE
.sp
.ne 2
-.mk
+.na
+\fB\fBunsupported@\fR\fIfeature_guid\fR\fR
+.ad
+.RS 20n
+Information about unsupported features that are enabled on the pool. See
+\fBzpool-features\fR(5) for details.
+.RE
+
+.sp
+.ne 2
.na
\fB\fBused\fR\fR
.ad
.RS 20n
-.rt
Amount of storage space used within the pool.
.RE
.sp
.LP
-These space usage properties report actual physical space available to the
+The space usage properties report actual physical space available to the
storage pool. The physical space can be different from the total amount of
space that any contained datasets can actually use. The amount of space used in
a \fBraidz\fR configuration depends on the characteristics of the data being
@@ -628,7 +649,6 @@ these discrepancies may become more noticeable.
The following property can be set at creation time and import time:
.sp
.ne 2
-.mk
.na
\fB\fBaltroot\fR\fR
.ad
@@ -639,7 +659,7 @@ points within the pool. This can be used when examining an unknown pool where
the mount points cannot be trusted, or in an alternate boot environment, where
the typical paths are not valid. \fBaltroot\fR is not a persistent property. It
is valid only while the system is up. Setting \fBaltroot\fR defaults to using
-\fBcachefile\fR=none, though this may be overridden using an explicit setting.
+\fBcachefile\fR=none, though this may be overridden using an explicit setting.
.RE
.sp
@@ -648,7 +668,6 @@ The following properties can be set at creation time and import time, and later
changed with the \fBzpool set\fR command:
.sp
.ne 2
-.mk
.na
\fB\fBautoexpand\fR=\fBon\fR | \fBoff\fR\fR
.ad
@@ -664,7 +683,6 @@ can also be referred to by its shortened column name, \fBexpand\fR.
.sp
.ne 2
-.mk
.na
\fB\fBautoreplace\fR=\fBon\fR | \fBoff\fR\fR
.ad
@@ -680,7 +698,6 @@ property can also be referred to by its shortened column name, "replace".
.sp
.ne 2
-.mk
.na
\fB\fBbootfs\fR=\fIpool\fR/\fIdataset\fR\fR
.ad
@@ -692,7 +709,6 @@ expected to be set mainly by the installation and upgrade programs.
.sp
.ne 2
-.mk
.na
\fB\fBcachefile\fR=\fIpath\fR | \fBnone\fR\fR
.ad
@@ -717,7 +733,17 @@ exported or destroyed, the file is removed.
.sp
.ne 2
-.mk
+.na
+\fB\fBcomment\fR=\fB\fItext\fR\fR
+.ad
+.RS 4n
+A text string consisting of printable ASCII characters that will be stored
+such that it is available even if the pool becomes faulted. An administrator
+can provide additional information about a pool using this property.
+.RE
+
+.sp
+.ne 2
.na
\fB\fBdelegation\fR=\fBon\fR | \fBoff\fR\fR
.ad
@@ -730,7 +756,6 @@ permissions defined on the dataset. See \fBzfs\fR(1M) for more information on
.sp
.ne 2
-.mk
.na
\fB\fBfailmode\fR=\fBwait\fR | \fBcontinue\fR | \fBpanic\fR\fR
.ad
@@ -742,24 +767,20 @@ storage device(s) or a failure of all devices within the pool. The behavior of
such an event is determined as follows:
.sp
.ne 2
-.mk
.na
\fB\fBwait\fR\fR
.ad
.RS 12n
-.rt
Blocks all \fBI/O\fR access until the device connectivity is recovered and the
errors are cleared. This is the default behavior.
.RE
.sp
.ne 2
-.mk
.na
\fB\fBcontinue\fR\fR
.ad
.RS 12n
-.rt
Returns \fBEIO\fR to any new write \fBI/O\fR requests but allows reads to any
of the remaining healthy devices. Any write requests that have yet to be
committed to disk would be blocked.
@@ -767,12 +788,10 @@ committed to disk would be blocked.
.sp
.ne 2
-.mk
.na
\fB\fBpanic\fR\fR
.ad
.RS 12n
-.rt
Prints out a message to the console and generates a system crash dump.
.RE
@@ -780,7 +799,18 @@ Prints out a message to the console and generates a system crash dump.
.sp
.ne 2
-.mk
+.na
+\fB\fBfeature@\fR\fIfeature_name\fR=\fBenabled\fR\fR
+.ad
+.RS 4n
+The value of this property is the current state of \fIfeature_name\fR. The
+only valid value when setting this property is \fBenabled\fR which moves
+\fIfeature_name\fR to the enabled state. See \fBzpool-features\fR(5) for
+details on feature states.
+.RE
+
+.sp
+.ne 2
.na
\fB\fBlistsnaps\fR=on | off\fR
.ad
@@ -793,7 +823,6 @@ value is "off".
.sp
.ne 2
-.mk
.na
\fB\fBversion\fR=\fIversion\fR\fR
.ad
@@ -802,8 +831,8 @@ value is "off".
The current on-disk version of the pool. This can be increased, but never
decreased. The preferred method of updating pools is with the "\fBzpool
upgrade\fR" command, though this property can be used when a specific version
-is needed for backwards compatibility. This property can be any number between
-1 and the current version reported by "\fBzpool upgrade -v\fR".
+is needed for backwards compatibility. Once feature flags is enabled on a
+pool this property will no longer have a value.
.RE
.SS "Subcommands"
@@ -818,7 +847,6 @@ pools, add capacity to storage pools, and provide information about the storage
pools. The following subcommands are supported:
.sp
.ne 2
-.mk
.na
\fB\fBzpool\fR \fB-?\fR\fR
.ad
@@ -829,7 +857,6 @@ Displays a help message.
.sp
.ne 2
-.mk
.na
\fB\fBzpool add\fR [\fB-fn\fR] \fIpool\fR \fIvdev\fR ...\fR
.ad
@@ -841,24 +868,20 @@ the \fB-f\fR option, and the device checks performed are described in the
"zpool create" subcommand.
.sp
.ne 2
-.mk
.na
\fB\fB-f\fR\fR
.ad
.RS 6n
-.rt
Forces use of \fBvdev\fRs, even if they appear in use or specify a conflicting
replication level. Not all devices can be overridden in this manner.
.RE
.sp
.ne 2
-.mk
.na
\fB\fB-n\fR\fR
.ad
.RS 6n
-.rt
Displays the configuration that would be used without actually adding the
\fBvdev\fRs. The actual pool creation can still fail due to insufficient
privileges or device sharing.
@@ -871,7 +894,6 @@ device.
.sp
.ne 2
-.mk
.na
\fB\fBzpool attach\fR [\fB-f\fR] \fIpool\fR \fIdevice\fR \fInew_device\fR\fR
.ad
@@ -886,12 +908,10 @@ three-way mirror, and so on. In either case, \fInew_device\fR begins to
resilver immediately.
.sp
.ne 2
-.mk
.na
\fB\fB-f\fR\fR
.ad
.RS 6n
-.rt
Forces use of \fInew_device\fR, even if its appears to be in use. Not all
devices can be overridden in this manner.
.RE
@@ -900,7 +920,6 @@ devices can be overridden in this manner.
.sp
.ne 2
-.mk
.na
\fB\fBzpool clear\fR \fIpool\fR [\fIdevice\fR] ...\fR
.ad
@@ -913,9 +932,8 @@ those errors associated with the specified device or devices are cleared.
.sp
.ne 2
-.mk
.na
-\fB\fBzpool create\fR [\fB-fn\fR] [\fB-o\fR \fIproperty=value\fR] ... [\fB-O\fR
+\fB\fBzpool create\fR [\fB-fnd\fR] [\fB-o\fR \fIproperty=value\fR] ... [\fB-O\fR
\fIfile-system-property=value\fR] ... [\fB-m\fR \fImountpoint\fR] [\fB-R\fR
\fIroot\fR] \fIpool\fR \fIvdev\fR ...\fR
.ad
@@ -945,8 +963,10 @@ Unless the \fB-R\fR option is specified, the default mount point is
root dataset cannot be mounted. This can be overridden with the \fB-m\fR
option.
.sp
+By default all supported features are enabled on the new pool unless the
+\fB-d\fR option is specified.
+.sp
.ne 2
-.mk
.na
\fB\fB-f\fR\fR
.ad
@@ -958,7 +978,6 @@ replication level. Not all devices can be overridden in this manner.
.sp
.ne 2
-.mk
.na
\fB\fB-n\fR\fR
.ad
@@ -971,7 +990,18 @@ device sharing.
.sp
.ne 2
-.mk
+.na
+\fB\fB-d\fR\fR
+.ad
+.sp .6
+.RS 4n
+Do not enable any features on the new pool. Individual features can be enabled
+by setting their corresponding properties to \fBenabled\fR with the \fB-o\fR
+option. See \fBzpool-features\fR(5) for details about feature properties.
+.RE
+
+.sp
+.ne 2
.na
\fB\fB-o\fR \fIproperty=value\fR [\fB-o\fR \fIproperty=value\fR] ...\fR
.ad
@@ -983,7 +1013,6 @@ valid properties that can be set.
.sp
.ne 2
-.mk
.na
\fB\fB-O\fR \fIfile-system-property=value\fR\fR
.ad
@@ -1000,7 +1029,6 @@ can be set.
.sp
.ne 2
-.mk
.na
\fB\fB-R\fR \fIroot\fR\fR
.ad
@@ -1011,7 +1039,6 @@ Equivalent to "-o cachefile=none,altroot=\fIroot\fR"
.sp
.ne 2
-.mk
.na
\fB\fB-m\fR \fImountpoint\fR\fR
.ad
@@ -1027,7 +1054,6 @@ information on dataset mount points, see \fBzfs\fR(1M).
.sp
.ne 2
-.mk
.na
\fB\fBzpool destroy\fR [\fB-f\fR] \fIpool\fR\fR
.ad
@@ -1037,12 +1063,10 @@ Destroys the given pool, freeing up any devices for other use. This command
tries to unmount any active datasets before destroying the pool.
.sp
.ne 2
-.mk
.na
\fB\fB-f\fR\fR
.ad
.RS 6n
-.rt
Forces any active datasets contained within the pool to be unmounted.
.RE
@@ -1050,7 +1074,6 @@ Forces any active datasets contained within the pool to be unmounted.
.sp
.ne 2
-.mk
.na
\fB\fBzpool detach\fR \fIpool\fR \fIdevice\fR\fR
.ad
@@ -1062,7 +1085,6 @@ other valid replicas of the data.
.sp
.ne 2
-.mk
.na
\fB\fBzpool export\fR [\fB-f\fR] \fIpool\fR ...\fR
.ad
@@ -1082,12 +1104,10 @@ labels. Otherwise, disk drivers on platforms of different endianness will not
recognize the disks.
.sp
.ne 2
-.mk
.na
\fB\fB-f\fR\fR
.ad
.RS 6n
-.rt
Forcefully unmount all datasets, using the "\fBunmount -f\fR" command.
.sp
This command will forcefully export the pool even if it has a shared spare that
@@ -1098,7 +1118,6 @@ is currently being used. This may lead to potential data corruption.
.sp
.ne 2
-.mk
.na
\fB\fBzpool get\fR "\fIall\fR" | \fIproperty\fR[,...] \fIpool\fR ...\fR
.ad
@@ -1124,7 +1143,6 @@ properties.
.sp
.ne 2
-.mk
.na
\fB\fBzpool history\fR [\fB-il\fR] [\fIpool\fR] ...\fR
.ad
@@ -1134,24 +1152,20 @@ Displays the command history of the specified pools or all pools if no pool is
specified.
.sp
.ne 2
-.mk
.na
\fB\fB-i\fR\fR
.ad
.RS 6n
-.rt
Displays internally logged \fBZFS\fR events in addition to user initiated
events.
.RE
.sp
.ne 2
-.mk
.na
\fB\fB-l\fR\fR
.ad
.RS 6n
-.rt
Displays log records in long format, which in addition to standard format
includes, the user name, the hostname, and the zone in which the operation was
performed.
@@ -1161,7 +1175,6 @@ performed.
.sp
.ne 2
-.mk
.na
\fB\fBzpool import\fR [\fB-d\fR \fIdir\fR | \fB-c\fR \fIcachefile\fR]
[\fB-D\fR]\fR
@@ -1181,12 +1194,10 @@ The numeric identifier is unique, and can be used instead of the pool name when
multiple exported pools of the same name are available.
.sp
.ne 2
-.mk
.na
\fB\fB-c\fR \fIcachefile\fR\fR
.ad
.RS 16n
-.rt
Reads configuration from the given \fBcachefile\fR that was created with the
"\fBcachefile\fR" pool property. This \fBcachefile\fR is used instead of
searching for devices.
@@ -1194,24 +1205,20 @@ searching for devices.
.sp
.ne 2
-.mk
.na
\fB\fB-d\fR \fIdir\fR\fR
.ad
.RS 16n
-.rt
Searches for devices or files in \fIdir\fR. The \fB-d\fR option can be
specified multiple times.
.RE
.sp
.ne 2
-.mk
.na
\fB\fB-D\fR\fR
.ad
.RS 16n
-.rt
Lists destroyed pools only.
.RE
@@ -1219,7 +1226,6 @@ Lists destroyed pools only.
.sp
.ne 2
-.mk
.na
\fB\fBzpool import\fR [\fB-o\fR \fImntopts\fR] [ \fB-o\fR
\fIproperty\fR=\fIvalue\fR] ... [\fB-d\fR \fIdir\fR | \fB-c\fR \fIcachefile\fR]
@@ -1234,12 +1240,10 @@ are imported. Destroyed pools, pools that were previously destroyed with the
is specified.
.sp
.ne 2
-.mk
.na
\fB\fB-o\fR \fImntopts\fR\fR
.ad
.RS 21n
-.rt
Comma-separated list of mount options to use when mounting datasets within the
pool. See \fBzfs\fR(1M) for a description of dataset properties and mount
options.
@@ -1247,24 +1251,20 @@ options.
.sp
.ne 2
-.mk
.na
\fB\fB-o\fR \fIproperty=value\fR\fR
.ad
.RS 21n
-.rt
Sets the specified property on the imported pool. See the "Properties" section
for more information on the available pool properties.
.RE
.sp
.ne 2
-.mk
.na
\fB\fB-c\fR \fIcachefile\fR\fR
.ad
.RS 21n
-.rt
Reads configuration from the given \fBcachefile\fR that was created with the
"\fBcachefile\fR" pool property. This \fBcachefile\fR is used instead of
searching for devices.
@@ -1272,57 +1272,47 @@ searching for devices.
.sp
.ne 2
-.mk
.na
\fB\fB-d\fR \fIdir\fR\fR
.ad
.RS 21n
-.rt
Searches for devices or files in \fIdir\fR. The \fB-d\fR option can be
specified multiple times. This option is incompatible with the \fB-c\fR option.
.RE
.sp
.ne 2
-.mk
.na
\fB\fB-D\fR\fR
.ad
.RS 21n
-.rt
Imports destroyed pools only. The \fB-f\fR option is also required.
.RE
.sp
.ne 2
-.mk
.na
\fB\fB-f\fR\fR
.ad
.RS 21n
-.rt
Forces import, even if the pool appears to be potentially active.
.RE
.sp
.ne 2
-.mk
.na
\fB\fB-a\fR\fR
.ad
.RS 21n
-.rt
Searches for and imports all pools found.
.RE
.sp
.ne 2
-.mk
.na
\fB\fB-R\fR \fIroot\fR\fR
.ad
.RS 21n
-.rt
Sets the "\fBcachefile\fR" property to "\fBnone\fR" and the "\fIaltroot\fR"
property to "\fIroot\fR".
.RE
@@ -1331,7 +1321,6 @@ property to "\fIroot\fR".
.sp
.ne 2
-.mk
.na
\fB\fBzpool import\fR [\fB-o\fR \fImntopts\fR] [ \fB-o\fR
\fIproperty\fR=\fIvalue\fR] ... [\fB-d\fR \fIdir\fR | \fB-c\fR \fIcachefile\fR]
@@ -1351,7 +1340,6 @@ this was a failed export, or whether the device is really in use from another
host. To import a pool in this state, the \fB-f\fR option is required.
.sp
.ne 2
-.mk
.na
\fB\fB-o\fR \fImntopts\fR\fR
.ad
@@ -1364,7 +1352,6 @@ options.
.sp
.ne 2
-.mk
.na
\fB\fB-o\fR \fIproperty=value\fR\fR
.ad
@@ -1376,7 +1363,6 @@ for more information on the available pool properties.
.sp
.ne 2
-.mk
.na
\fB\fB-c\fR \fIcachefile\fR\fR
.ad
@@ -1389,7 +1375,6 @@ searching for devices.
.sp
.ne 2
-.mk
.na
\fB\fB-d\fR \fIdir\fR\fR
.ad
@@ -1401,7 +1386,6 @@ specified multiple times. This option is incompatible with the \fB-c\fR option.
.sp
.ne 2
-.mk
.na
\fB\fB-D\fR\fR
.ad
@@ -1412,7 +1396,6 @@ Imports destroyed pool. The \fB-f\fR option is also required.
.sp
.ne 2
-.mk
.na
\fB\fB-f\fR\fR
.ad
@@ -1423,7 +1406,6 @@ Forces import, even if the pool appears to be potentially active.
.sp
.ne 2
-.mk
.na
\fB\fB-R\fR \fIroot\fR\fR
.ad
@@ -1437,7 +1419,6 @@ property to "\fIroot\fR".
.sp
.ne 2
-.mk
.na
\fB\fBzpool iostat\fR [\fB-T\fR \fBu\fR | \fBd\fR] [\fB-v\fR] [\fIpool\fR] ...
[\fIinterval\fR[\fIcount\fR]]\fR
@@ -1451,12 +1432,10 @@ system is shown. If \fIcount\fR is specified, the command exits after
\fIcount\fR reports are printed.
.sp
.ne 2
-.mk
.na
\fB\fB-T\fR \fBu\fR | \fBd\fR\fR
.ad
.RS 12n
-.rt
Display a time stamp.
.sp
Specify \fBu\fR for a printed representation of the internal representation of
@@ -1466,12 +1445,10 @@ time. See \fBtime\fR(2). Specify \fBd\fR for standard date format. See
.sp
.ne 2
-.mk
.na
\fB\fB-v\fR\fR
.ad
.RS 12n
-.rt
Verbose statistics. Reports usage statistics for individual \fIvdevs\fR within
the pool, in addition to the pool-wide statistics.
.RE
@@ -1480,9 +1457,8 @@ the pool, in addition to the pool-wide statistics.
.sp
.ne 2
-.mk
.na
-\fB\fBzpool list\fR [\fB-H\fR] [\fB-o\fR \fIprops\fR[,...]] [\fIpool\fR] ...\fR
+\fB\fBzpool list\fR [\fB-Hv\fR] [\fB-o\fR \fIprops\fR[,...]] [\fIpool\fR] ...\fR
.ad
.sp .6
.RS 4n
@@ -1490,34 +1466,39 @@ Lists the given pools along with a health status and space usage. When given no
arguments, all pools in the system are listed.
.sp
.ne 2
-.mk
.na
\fB\fB-H\fR\fR
.ad
.RS 12n
-.rt
Scripted mode. Do not display headers, and separate fields by a single tab
instead of arbitrary space.
.RE
.sp
.ne 2
-.mk
.na
\fB\fB-o\fR \fIprops\fR\fR
.ad
.RS 12n
-.rt
Comma-separated list of properties to display. See the "Properties" section for
a list of valid properties. The default list is "name, size, used, available,
-capacity, health, altroot"
+expandsize, capacity, dedupratio, health, altroot"
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-v\fR\fR
+.ad
+.RS 12n
+Verbose statistics. Reports usage statistics for individual \fIvdevs\fR within
+the pool, in addition to the pool-wise statistics.
.RE
.RE
.sp
.ne 2
-.mk
.na
\fB\fBzpool offline\fR [\fB-t\fR] \fIpool\fR \fIdevice\fR ...\fR
.ad
@@ -1529,12 +1510,10 @@ no attempt is made to read or write to the device.
This command is not applicable to spares or cache devices.
.sp
.ne 2
-.mk
.na
\fB\fB-t\fR\fR
.ad
.RS 6n
-.rt
Temporary. Upon reboot, the specified physical device reverts to its previous
state.
.RE
@@ -1543,7 +1522,6 @@ state.
.sp
.ne 2
-.mk
.na
\fB\fBzpool online\fR [\fB-e\fR] \fIpool\fR \fIdevice\fR...\fR
.ad
@@ -1554,12 +1532,10 @@ Brings the specified physical device online.
This command is not applicable to spares or cache devices.
.sp
.ne 2
-.mk
.na
\fB\fB-e\fR\fR
.ad
.RS 6n
-.rt
Expand the device to use all available space. If the device is part of a mirror
or \fBraidz\fR then all devices must be expanded before the new space will
become available to the pool.
@@ -1569,7 +1545,17 @@ become available to the pool.
.sp
.ne 2
-.mk
+.na
+\fB\fBzpool reguid\fR \fIpool\fR
+.ad
+.sp .6
+.RS 4n
+Generates a new unique identifier for the pool. You must ensure that all devices in this pool are online and
+healthy before performing this action.
+.RE
+
+.sp
+.ne 2
.na
\fB\fBzpool remove\fR \fIpool\fR \fIdevice\fR ...\fR
.ad
@@ -1585,7 +1571,6 @@ a pool.
.sp
.ne 2
-.mk
.na
\fB\fBzpool replace\fR [\fB-f\fR] \fIpool\fR \fIold_device\fR
[\fInew_device\fR]\fR
@@ -1606,12 +1591,10 @@ this case, the new disk may have the same \fB/dev/dsk\fR path as the old
device, even though it is actually a different disk. \fBZFS\fR recognizes this.
.sp
.ne 2
-.mk
.na
\fB\fB-f\fR\fR
.ad
.RS 6n
-.rt
Forces use of \fInew_device\fR, even if its appears to be in use. Not all
devices can be overridden in this manner.
.RE
@@ -1620,7 +1603,6 @@ devices can be overridden in this manner.
.sp
.ne 2
-.mk
.na
\fB\fBzpool scrub\fR [\fB-s\fR] \fIpool\fR ...\fR
.ad
@@ -1645,12 +1627,10 @@ progress, \fBZFS\fR does not allow a scrub to be started until the resilver
completes.
.sp
.ne 2
-.mk
.na
\fB\fB-s\fR\fR
.ad
.RS 6n
-.rt
Stop scrubbing.
.RE
@@ -1658,7 +1638,6 @@ Stop scrubbing.
.sp
.ne 2
-.mk
.na
\fB\fBzpool set\fR \fIproperty\fR=\fIvalue\fR \fIpool\fR\fR
.ad
@@ -1670,7 +1649,6 @@ more information on what properties can be set and acceptable values.
.sp
.ne 2
-.mk
.na
\fB\fBzpool status\fR [\fB-xv\fR] [\fIpool\fR] ...\fR
.ad
@@ -1687,24 +1665,20 @@ because the amount of data in the pool and the other workloads on the system
can change.
.sp
.ne 2
-.mk
.na
\fB\fB-x\fR\fR
.ad
.RS 6n
-.rt
Only display status for pools that are exhibiting errors or are otherwise
unavailable.
.RE
.sp
.ne 2
-.mk
.na
\fB\fB-v\fR\fR
.ad
.RS 6n
-.rt
Displays verbose data error information, printing out a complete list of all
data errors since the last complete pool scrub.
.RE
@@ -1713,7 +1687,6 @@ data errors since the last complete pool scrub.
.sp
.ne 2
-.mk
.na
\fB\fBzpool upgrade\fR\fR
.ad
@@ -1728,7 +1701,6 @@ inaccessible on the system.
.sp
.ne 2
-.mk
.na
\fB\fBzpool upgrade\fR \fB-v\fR\fR
.ad
@@ -1741,7 +1713,6 @@ with an explanation of the features provided with each version.
.sp
.ne 2
-.mk
.na
\fB\fBzpool upgrade\fR [\fB-V\fR \fIversion\fR] \fB-a\fR | \fIpool\fR ...\fR
.ad
@@ -1752,23 +1723,19 @@ pool will no longer be accessible on systems running older versions of the
software.
.sp
.ne 2
-.mk
.na
\fB\fB-a\fR\fR
.ad
.RS 14n
-.rt
Upgrades all pools.
.RE
.sp
.ne 2
-.mk
.na
\fB\fB-V\fR \fIversion\fR\fR
.ad
.RS 14n
-.rt
Upgrade to the specified version. If the \fB-V\fR flag is not specified, the
pool is upgraded to the most recent version. This option can only be used to
increase the version number, and only up to the most recent version supported
@@ -1868,10 +1835,10 @@ The results from this command are similar to the following:
.in +2
.nf
# \fBzpool list\fR
- NAME SIZE USED AVAIL CAP HEALTH ALTROOT
- pool 67.5G 2.92M 67.5G 0% ONLINE -
- tank 67.5G 2.92M 67.5G 0% ONLINE -
- zion - - - 0% FAULTED -
+ NAME SIZE ALLOC FREE EXPANDSZ CAP DEDUP HEALTH ALTROOT
+ rpool 19.9G 8.43G 11.4G - 42% 1.00x ONLINE -
+ tank 61.5G 20.0G 41.5G - 32% 1.00x ONLINE -
+ zion - - - - - - FAULTED -
.fi
.in -2
.sp
@@ -2086,40 +2053,57 @@ The command to remove the mirrored log \fBmirror-2\fR is:
.in -2
.sp
+.LP
+\fBExample 15 \fRDisplaying expanded space on a device
+.sp
+.LP
+The following command dipslays the detailed information for the \fIdata\fR
+pool. This pool is comprised of a single \fIraidz\fR vdev where one of its
+devices increased its capacity by 1GB. In this example, the pool will not
+be able to utilized this extra capacity until all the devices under the
+\fIraidz\fR vdev have been expanded.
+
+.sp
+.in +2
+.nf
+# \fBzpool list -v data\fR
+ NAME SIZE ALLOC FREE EXPANDSZ CAP DEDUP HEALTH ALTROOT
+ data 17.9G 174K 17.9G - 0% 1.00x ONLINE -
+ raidz1 17.9G 174K 17.9G -
+ c4t2d0 - - - 1G
+ c4t3d0 - - - -
+ c4t4d0 - - - -
+.fi
+.in -2
+
.SH EXIT STATUS
.sp
.LP
The following exit values are returned:
.sp
.ne 2
-.mk
.na
\fB\fB0\fR\fR
.ad
.RS 5n
-.rt
Successful completion.
.RE
.sp
.ne 2
-.mk
.na
\fB\fB1\fR\fR
.ad
.RS 5n
-.rt
An error occurred.
.RE
.sp
.ne 2
-.mk
.na
\fB\fB2\fR\fR
.ad
.RS 5n
-.rt
Invalid command line options were specified.
.RE
@@ -2131,16 +2115,15 @@ See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
.TS
-tab() box;
-cw(2.75i) |cw(2.75i)
-lw(2.75i) |lw(2.75i)
-.
-ATTRIBUTE TYPEATTRIBUTE VALUE
+box;
+c | c
+l | l .
+ATTRIBUTE TYPE ATTRIBUTE VALUE
_
-Interface StabilityEvolving
+Interface Stability Evolving
.TE
.SH SEE ALSO
.sp
.LP
-\fBzfs\fR(1M), \fBattributes\fR(5)
+\fBzfs\fR(1M), \fBzpool-features\fR(5), \fBattributes\fR(5)
diff --git a/man/man1m/zstreamdump.1m b/man/man1m/zstreamdump.1m
index d8b5e9468181..70f6ee875eb7 100644
--- a/man/man1m/zstreamdump.1m
+++ b/man/man1m/zstreamdump.1m
@@ -3,7 +3,7 @@
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License. You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.
.\" See the License for the specific language governing permissions and limitations under the License. When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with
.\" the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH zstreamdump 1M "21 Sep 2009" "SunOS 5.11" "System Administration Commands"
+.TH ZSTREAMDUMP 1M "Sep 21, 2009"
.SH NAME
zstreamdump \- filter data in zfs send stream
.SH SYNOPSIS
@@ -24,7 +24,6 @@ command, then displays headers and some statistics from that output. See
The following options are supported:
.sp
.ne 2
-.mk
.na
\fB\fB-C\fR\fR
.ad
@@ -35,7 +34,6 @@ Suppress the validation of checksums.
.sp
.ne 2
-.mk
.na
\fB\fB-v\fR\fR
.ad
@@ -52,13 +50,12 @@ See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
.TS
-tab() box;
-cw(2.75i) |cw(2.75i)
-lw(2.75i) |lw(2.75i)
-.
-ATTRIBUTE TYPEATTRIBUTE VALUE
+box;
+c | c
+l | l .
+ATTRIBUTE TYPE ATTRIBUTE VALUE
_
-Interface StabilityUncommitted
+Interface Stability Uncommitted
.TE
.SH SEE ALSO