153089abeschrock/*
253089abeschrock * CDDL HEADER START
353089abeschrock *
453089abeschrock * The contents of this file are subject to the terms of the
553089abeschrock * Common Development and Distribution License (the "License").
653089abeschrock * You may not use this file except in compliance with the License.
753089abeschrock *
853089abeschrock * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
953089abeschrock * or http://www.opensolaris.org/os/licensing.
1053089abeschrock * See the License for the specific language governing permissions
1153089abeschrock * and limitations under the License.
1253089abeschrock *
1353089abeschrock * When distributing Covered Code, include this CDDL HEADER in each
1453089abeschrock * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1553089abeschrock * If applicable, add the following below this CDDL HEADER, with the
1653089abeschrock * fields enclosed by brackets "[]" replaced with your own identifying
1753089abeschrock * information: Portions Copyright [yyyy] [name of copyright owner]
1853089abeschrock *
1953089abeschrock * CDDL HEADER END
2053089abeschrock */
2153089abeschrock
2253089abeschrock/*
23ca0cc39Matthew Ahrens * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
24a7a845eSteven Hartland * Copyright (c) 2013 Steven Hartland. All rights reserved.
2553089abeschrock */
2653089abeschrock
2753089abeschrock/*
2853089abeschrock * zhack is a debugging tool that can write changes to ZFS pool using libzpool
2953089abeschrock * for testing purposes. Altering pools with zhack is unsupported and may
3053089abeschrock * result in corrupted pools.
3153089abeschrock */
3253089abeschrock
3353089abeschrock#include <stdio.h>
3453089abeschrock#include <stdlib.h>
3553089abeschrock#include <ctype.h>
3653089abeschrock#include <sys/zfs_context.h>
3753089abeschrock#include <sys/spa.h>
3853089abeschrock#include <sys/spa_impl.h>
3953089abeschrock#include <sys/dmu.h>
4053089abeschrock#include <sys/zap.h>
4153089abeschrock#include <sys/zfs_znode.h>
4253089abeschrock#include <sys/dsl_synctask.h>
4353089abeschrock#include <sys/vdev.h>
4453089abeschrock#include <sys/fs/zfs.h>
4553089abeschrock#include <sys/dmu_objset.h>
4653089abeschrock#include <sys/dsl_pool.h>
4753089abeschrock#include <sys/zio_checksum.h>
4853089abeschrock#include <sys/zio_compress.h>
4953089abeschrock#include <sys/zfeature.h>
503b2aab1Matthew Ahrens#include <sys/dmu_tx.h>
5153089abeschrock#undef verify
5253089abeschrock#include <libzfs.h>
5353089abeschrock
5453089abeschrockextern boolean_t zfeature_checks_disable;
5553089abeschrock
5653089abeschrockconst char cmdname[] = "zhack";
5753089abeschrocklibzfs_handle_t *g_zfs;
5853089abeschrockstatic importargs_t g_importargs;
5953089abeschrockstatic char *g_pool;
6053089abeschrockstatic boolean_t g_readonly;
6153089abeschrock
6253089abeschrockstatic void
6353089abeschrockusage(void)
6453089abeschrock{
6553089abeschrock	(void) fprintf(stderr,
6653089abeschrock	    "Usage: %s [-c cachefile] [-d dir] <subcommand> <args> ...\n"
6753089abeschrock	    "where <subcommand> <args> is one of the following:\n"
6853089abeschrock	    "\n", cmdname);
6953089abeschrock
7053089abeschrock	(void) fprintf(stderr,
7153089abeschrock	    "    feature stat <pool>\n"
7253089abeschrock	    "        print information about enabled features\n"
7353089abeschrock	    "    feature enable [-d desc] <pool> <feature>\n"
7453089abeschrock	    "        add a new enabled feature to the pool\n"
7553089abeschrock	    "        -d <desc> sets the feature's description\n"
7653089abeschrock	    "    feature ref [-md] <pool> <feature>\n"
7753089abeschrock	    "        change the refcount on the given feature\n"
7853089abeschrock	    "        -d decrease instead of increase the refcount\n"
7953089abeschrock	    "        -m add the feature to the label if increasing refcount\n"
8053089abeschrock	    "\n"
8153089abeschrock	    "    <feature> : should be a feature guid\n");
8253089abeschrock	exit(1);
8353089abeschrock}
8453089abeschrock
8553089abeschrock
8653089abeschrockstatic void
877fdd916George Wilsonfatal(spa_t *spa, void *tag, const char *fmt, ...)
8853089abeschrock{
8953089abeschrock	va_list ap;
9053089abeschrock
917fdd916George Wilson	if (spa != NULL) {
927fdd916George Wilson		spa_close(spa, tag);
937fdd916George Wilson		(void) spa_export(g_pool, NULL, B_TRUE, B_FALSE);
947fdd916George Wilson	}
957fdd916George Wilson
9653089abeschrock	va_start(ap, fmt);
9753089abeschrock	(void) fprintf(stderr, "%s: ", cmdname);
9853089abeschrock	(void) vfprintf(stderr, fmt, ap);
9953089abeschrock	va_end(ap);
10053089abeschrock	(void) fprintf(stderr, "\n");
10153089abeschrock
10253089abeschrock	exit(1);
10353089abeschrock}
10453089abeschrock
10553089abeschrock/* ARGSUSED */
10653089abeschrockstatic int
10753089abeschrockspace_delta_cb(dmu_object_type_t bonustype, void *data,
108f67950bNasf-Fan    uint64_t *userp, uint64_t *groupp, uint64_t *projectp)
10953089abeschrock{
11053089abeschrock	/*
11153089abeschrock	 * Is it a valid type of object to track?
11253089abeschrock	 */
11353089abeschrock	if (bonustype != DMU_OT_ZNODE && bonustype != DMU_OT_SA)
11453089abeschrock		return (ENOENT);
11553089abeschrock	(void) fprintf(stderr, "modifying object that needs user accounting");
11653089abeschrock	abort();
11753089abeschrock	/* NOTREACHED */
11853089abeschrock}
11953089abeschrock
12053089abeschrock/*
12153089abeschrock * Target is the dataset whose pool we want to open.
12253089abeschrock */
12353089abeschrockstatic void
124e0f1c0aOlaf Faalandzhack_import(char *target, boolean_t readonly)
12553089abeschrock{
12653089abeschrock	nvlist_t *config;
12753089abeschrock	nvlist_t *props;
128e0f1c0aOlaf Faaland	int error;
12953089abeschrock
13053089abeschrock	kernel_init(readonly ? FREAD : (FREAD | FWRITE));
13153089abeschrock	g_zfs = libzfs_init();
13253089abeschrock	ASSERT(g_zfs != NULL);
13353089abeschrock
13453089abeschrock	dmu_objset_register_type(DMU_OST_ZFS, space_delta_cb);
13553089abeschrock
13653089abeschrock	g_readonly = readonly;
13753089abeschrock	g_importargs.unique = B_TRUE;
13853089abeschrock	g_importargs.can_be_active = readonly;
13953089abeschrock	g_pool = strdup(target);
14053089abeschrock
141e0f1c0aOlaf Faaland	error = zpool_tryimport(g_zfs, target, &config, &g_importargs);
142e0f1c0aOlaf Faaland	if (error)
143e0f1c0aOlaf Faaland		fatal(NULL, FTAG, "cannot import '%s': %s", target,
144e0f1c0aOlaf Faaland		    libzfs_error_description(g_zfs));
14553089abeschrock
14653089abeschrock	props = NULL;
14753089abeschrock	if (readonly) {
148e0f1c0aOlaf Faaland		VERIFY(nvlist_alloc(&props, NV_UNIQUE_NAME, 0) == 0);
149e0f1c0aOlaf Faaland		VERIFY(nvlist_add_uint64(props,
15053089abeschrock		    zpool_prop_to_name(ZPOOL_PROP_READONLY), 1) == 0);
15153089abeschrock	}
15253089abeschrock
15353089abeschrock	zfeature_checks_disable = B_TRUE;
154e0f1c0aOlaf Faaland	error = spa_import(target, config, props,
155e0f1c0aOlaf Faaland	    (readonly ?  ZFS_IMPORT_SKIP_MMP : ZFS_IMPORT_NORMAL));
15653089abeschrock	zfeature_checks_disable = B_FALSE;
15753089abeschrock	if (error == EEXIST)
15853089abeschrock		error = 0;
15953089abeschrock
16053089abeschrock	if (error)
161e0f1c0aOlaf Faaland		fatal(NULL, FTAG, "can't import '%s': %s", target,
1627fdd916George Wilson		    strerror(error));
16353089abeschrock}
16453089abeschrock
16553089abeschrockstatic void
166e0f1c0aOlaf Faalandzhack_spa_open(char *target, boolean_t readonly, void *tag, spa_t **spa)
16753089abeschrock{
16853089abeschrock	int err;
16953089abeschrock
170e0f1c0aOlaf Faaland	zhack_import(target, readonly);
17153089abeschrock
17253089abeschrock	zfeature_checks_disable = B_TRUE;
17353089abeschrock	err = spa_open(target, spa, tag);
17453089abeschrock	zfeature_checks_disable = B_FALSE;
17553089abeschrock
17653089abeschrock	if (err != 0)
1777fdd916George Wilson		fatal(*spa, FTAG, "cannot open '%s': %s", target,
1787fdd916George Wilson		    strerror(err));
17953089abeschrock	if (spa_version(*spa) < SPA_VERSION_FEATURES) {
1807fdd916George Wilson		fatal(*spa, FTAG, "'%s' has version %d, features not enabled",
1817fdd916George Wilson		    target, (int)spa_version(*spa));
18253089abeschrock	}
18353089abeschrock}
18453089abeschrock
18553089abeschrockstatic void
18653089abeschrockdump_obj(objset_t *os, uint64_t obj, const char *name)
18753089abeschrock{
18853089abeschrock	zap_cursor_t zc;
18953089abeschrock	zap_attribute_t za;
19053089abeschrock
19153089abeschrock	(void) printf("%s_obj:\n", name);
19253089abeschrock
19353089abeschrock	for (zap_cursor_init(&zc, os, obj);
19453089abeschrock	    zap_cursor_retrieve(&zc, &za) == 0;
19553089abeschrock	    zap_cursor_advance(&zc)) {
19653089abeschrock		if (za.za_integer_length == 8) {
19753089abeschrock			ASSERT(za.za_num_integers == 1);
19853089abeschrock			(void) printf("\t%s = %llu\n",
19953089abeschrock			    za.za_name, (u_longlong_t)za.za_first_integer);
20053089abeschrock		} else {
20153089abeschrock			ASSERT(za.za_integer_length == 1);
20253089abeschrock			char val[1024];
20353089abeschrock			VERIFY(zap_lookup(os, obj, za.za_name,
20453089abeschrock			    1, sizeof (val), val) == 0);
20553089abeschrock			(void) printf("\t%s = %s\n", za.za_name, val);
20653089abeschrock		}
20753089abeschrock	}
20853089abeschrock	zap_cursor_fini(&zc);
20953089abeschrock}
21053089abeschrock
21153089abeschrockstatic void
21253089abeschrockdump_mos(spa_t *spa)
21353089abeschrock{
21453089abeschrock	nvlist_t *nv = spa->spa_label_features;
21553089abeschrock
21653089abeschrock	(void) printf("label config:\n");
21753089abeschrock	for (nvpair_t *pair = nvlist_next_nvpair(nv, NULL);
21853089abeschrock	    pair != NULL;
21953089abeschrock	    pair = nvlist_next_nvpair(nv, pair)) {
22053089abeschrock		(void) printf("\t%s\n", nvpair_name(pair));
22153089abeschrock	}
22253089abeschrock}
22353089abeschrock
22453089abeschrockstatic void
22553089abeschrockzhack_do_feature_stat(int argc, char **argv)
22653089abeschrock{
22753089abeschrock	spa_t *spa;
22853089abeschrock	objset_t *os;
22953089abeschrock	char *target;
23053089abeschrock
23153089abeschrock	argc--;
23253089abeschrock	argv++;
23353089abeschrock
23453089abeschrock	if (argc < 1) {
23553089abeschrock		(void) fprintf(stderr, "error: missing pool name\n");
23653089abeschrock		usage();
23753089abeschrock	}
23853089abeschrock	target = argv[0];
23953089abeschrock
24053089abeschrock	zhack_spa_open(target, B_TRUE, FTAG, &spa);
24153089abeschrock	os = spa->spa_meta_objset;
24253089abeschrock
24353089abeschrock	dump_obj(os, spa->spa_feat_for_read_obj, "for_read");
24453089abeschrock	dump_obj(os, spa->spa_feat_for_write_obj, "for_write");
24553089abeschrock	dump_obj(os, spa->spa_feat_desc_obj, "descriptions");
24643466aaMax Grossman	if (spa_feature_is_active(spa, SPA_FEATURE_ENABLED_TXG)) {
24743466aaMax Grossman		dump_obj(os, spa->spa_feat_enabled_txg_obj, "enabled_txg");
24843466aaMax Grossman	}
24953089abeschrock	dump_mos(spa);
25053089abeschrock
25153089abeschrock	spa_close(spa, FTAG);
25253089abeschrock}
25353089abeschrock
25453089abeschrockstatic void
2552acef22Matthew Ahrenszhack_feature_enable_sync(void *arg, dmu_tx_t *tx)
25653089abeschrock{
2573b2aab1Matthew Ahrens	spa_t *spa = dmu_tx_pool(tx)->dp_spa;
2583b2aab1Matthew Ahrens	zfeature_info_t *feature = arg;
25953089abeschrock
2602acef22Matthew Ahrens	feature_enable_sync(spa, feature, tx);
2612acef22Matthew Ahrens
2624445fffMatthew Ahrens	spa_history_log_internal(spa, "zhack enable feature", tx,
263ca0cc39Matthew Ahrens	    "guid=%s flags=%x",
264ca0cc39Matthew Ahrens	    feature->fi_guid, feature->fi_flags);
26553089abeschrock}
26653089abeschrock
26753089abeschrockstatic void
26853089abeschrockzhack_do_feature_enable(int argc, char **argv)
26953089abeschrock{
27053089abeschrock	char c;
27153089abeschrock	char *desc, *target;
27253089abeschrock	spa_t *spa;
27353089abeschrock	objset_t *mos;
27453089abeschrock	zfeature_info_t feature;
2752acef22Matthew Ahrens	spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
27653089abeschrock
27753089abeschrock	/*
27853089abeschrock	 * Features are not added to the pool's label until their refcounts
27953089abeschrock	 * are incremented, so fi_mos can just be left as false for now.
28053089abeschrock	 */
28153089abeschrock	desc = NULL;
28253089abeschrock	feature.fi_uname = "zhack";
283ca0cc39Matthew Ahrens	feature.fi_flags = 0;
28453089abeschrock	feature.fi_depends = nodeps;
28543466aaMax Grossman	feature.fi_feature = SPA_FEATURE_NONE;
28653089abeschrock
28753089abeschrock	optind = 1;
28853089abeschrock	while ((c = getopt(argc, argv, "rmd:")) != -1) {
28953089abeschrock		switch (c) {
29053089abeschrock		case 'r':
291ca0cc39Matthew Ahrens			feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT;
29253089abeschrock			break;
29353089abeschrock		case 'd':
29453089abeschrock			desc = strdup(optarg);
29553089abeschrock			break;
29653089abeschrock		default:
29753089abeschrock			usage();
29853089abeschrock			break;
29953089abeschrock		}
30053089abeschrock	}
30153089abeschrock
30253089abeschrock	if (desc == NULL)
30353089abeschrock		desc = strdup("zhack injected");
30453089abeschrock	feature.fi_desc = desc;
30553089abeschrock
30653089abeschrock	argc -= optind;
30753089abeschrock	argv += optind;
30853089abeschrock
30953089abeschrock	if (argc < 2) {
31053089abeschrock		(void) fprintf(stderr, "error: missing feature or pool name\n");
31153089abeschrock		usage();
31253089abeschrock	}
31353089abeschrock	target = argv[0];
31453089abeschrock	feature.fi_guid = argv[1];
31553089abeschrock
31653089abeschrock	if (!zfeature_is_valid_guid(feature.fi_guid))
3177fdd916George Wilson		fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid);
31853089abeschrock
31953089abeschrock	zhack_spa_open(target, B_FALSE, FTAG, &spa);
32053089abeschrock	mos = spa->spa_meta_objset;
32153089abeschrock
3222acef22Matthew Ahrens	if (zfeature_is_supported(feature.fi_guid))
3237fdd916George Wilson		fatal(spa, FTAG, "'%s' is a real feature, will not enable");
32453089abeschrock	if (0 == zap_contains(mos, spa->spa_feat_desc_obj, feature.fi_guid))
3257fdd916George Wilson		fatal(spa, FTAG, "feature already enabled: %s",
3267fdd916George Wilson		    feature.fi_guid);
32753089abeschrock
3283b2aab1Matthew Ahrens	VERIFY0(dsl_sync_task(spa_name(spa), NULL,
3297d46dc6Matthew Ahrens	    zhack_feature_enable_sync, &feature, 5, ZFS_SPACE_CHECK_NORMAL));
33053089abeschrock
33153089abeschrock	spa_close(spa, FTAG);
33253089abeschrock
33353089abeschrock	free(desc);
33453089abeschrock}
33553089abeschrock
33653089abeschrockstatic void
3373b2aab1Matthew Ahrensfeature_incr_sync(void *arg, dmu_tx_t *tx)
33853089abeschrock{
3393b2aab1Matthew Ahrens	spa_t *spa = dmu_tx_pool(tx)->dp_spa;
3403b2aab1Matthew Ahrens	zfeature_info_t *feature = arg;
3412acef22Matthew Ahrens	uint64_t refcount;
34253089abeschrock
34343466aaMax Grossman	VERIFY0(feature_get_refcount_from_disk(spa, feature, &refcount));
3442acef22Matthew Ahrens	feature_sync(spa, feature, refcount + 1, tx);
3454445fffMatthew Ahrens	spa_history_log_internal(spa, "zhack feature incr", tx,
3465722177Christopher Siden	    "guid=%s", feature->fi_guid);
34753089abeschrock}
34853089abeschrock
34953089abeschrockstatic void
3503b2aab1Matthew Ahrensfeature_decr_sync(void *arg, dmu_tx_t *tx)
35153089abeschrock{
3523b2aab1Matthew Ahrens	spa_t *spa = dmu_tx_pool(tx)->dp_spa;
3533b2aab1Matthew Ahrens	zfeature_info_t *feature = arg;
3542acef22Matthew Ahrens	uint64_t refcount;
35553089abeschrock
35643466aaMax Grossman	VERIFY0(feature_get_refcount_from_disk(spa, feature, &refcount));
3572acef22Matthew Ahrens	feature_sync(spa, feature, refcount - 1, tx);
3584445fffMatthew Ahrens	spa_history_log_internal(spa, "zhack feature decr", tx,
3595722177Christopher Siden	    "guid=%s", feature->fi_guid);
36053089abeschrock}
36153089abeschrock
36253089abeschrockstatic void
36353089abeschrockzhack_do_feature_ref(int argc, char **argv)
36453089abeschrock{
36553089abeschrock	char c;
36653089abeschrock	char *target;
36753089abeschrock	boolean_t decr = B_FALSE;
36853089abeschrock	spa_t *spa;
36953089abeschrock	objset_t *mos;
37053089abeschrock	zfeature_info_t feature;
3712acef22Matthew Ahrens	spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
37253089abeschrock
37353089abeschrock	/*
37453089abeschrock	 * fi_desc does not matter here because it was written to disk
37553089abeschrock	 * when the feature was enabled, but we need to properly set the
37653089abeschrock	 * feature for read or write based on the information we read off
37753089abeschrock	 * disk later.
37853089abeschrock	 */
37953089abeschrock	feature.fi_uname = "zhack";
380ca0cc39Matthew Ahrens	feature.fi_flags = 0;
38153089abeschrock	feature.fi_desc = NULL;
38253089abeschrock	feature.fi_depends = nodeps;
38343466aaMax Grossman	feature.fi_feature = SPA_FEATURE_NONE;
38453089abeschrock
38553089abeschrock	optind = 1;
38653089abeschrock	while ((c = getopt(argc, argv, "md")) != -1) {
38753089abeschrock		switch (c) {
38853089abeschrock		case 'm':
389ca0cc39Matthew Ahrens			feature.fi_flags |= ZFEATURE_FLAG_MOS;
39053089abeschrock			break;
39153089abeschrock		case 'd':
39253089abeschrock			decr = B_TRUE;
39353089abeschrock			break;
39453089abeschrock		default:
39553089abeschrock			usage();
39653089abeschrock			break;
39753089abeschrock		}
39853089abeschrock	}
39953089abeschrock	argc -= optind;
40053089abeschrock	argv += optind;
40153089abeschrock
40253089abeschrock	if (argc < 2) {
40353089abeschrock		(void) fprintf(stderr, "error: missing feature or pool name\n");
40453089abeschrock		usage();
40553089abeschrock	}
40653089abeschrock	target = argv[0];
40753089abeschrock	feature.fi_guid = argv[1];
40853089abeschrock
40953089abeschrock	if (!zfeature_is_valid_guid(feature.fi_guid))
4107fdd916George Wilson		fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid);
41153089abeschrock
41253089abeschrock	zhack_spa_open(target, B_FALSE, FTAG, &spa);
41353089abeschrock	mos = spa->spa_meta_objset;
41453089abeschrock
4152acef22Matthew Ahrens	if (zfeature_is_supported(feature.fi_guid)) {
4162acef22Matthew Ahrens		fatal(spa, FTAG,
4172acef22Matthew Ahrens		    "'%s' is a real feature, will not change refcount");
4182acef22Matthew Ahrens	}
41953089abeschrock
42053089abeschrock	if (0 == zap_contains(mos, spa->spa_feat_for_read_obj,
42153089abeschrock	    feature.fi_guid)) {
422ca0cc39Matthew Ahrens		feature.fi_flags &= ~ZFEATURE_FLAG_READONLY_COMPAT;
42353089abeschrock	} else if (0 == zap_contains(mos, spa->spa_feat_for_write_obj,
42453089abeschrock	    feature.fi_guid)) {
425ca0cc39Matthew Ahrens		feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT;
42653089abeschrock	} else {
4277fdd916George Wilson		fatal(spa, FTAG, "feature is not enabled: %s", feature.fi_guid);
42853089abeschrock	}
42953089abeschrock
4302acef22Matthew Ahrens	if (decr) {
4312acef22Matthew Ahrens		uint64_t count;
43243466aaMax Grossman		if (feature_get_refcount_from_disk(spa, &feature,
43343466aaMax Grossman		    &count) == 0 && count != 0) {
4342acef22Matthew Ahrens			fatal(spa, FTAG, "feature refcount already 0: %s",
4352acef22Matthew Ahrens			    feature.fi_guid);
4362acef22Matthew Ahrens		}
4372acef22Matthew Ahrens	}
43853089abeschrock
4393b2aab1Matthew Ahrens	VERIFY0(dsl_sync_task(spa_name(spa), NULL,
4407d46dc6Matthew Ahrens	    decr ? feature_decr_sync : feature_incr_sync, &feature,
4417d46dc6Matthew Ahrens	    5, ZFS_SPACE_CHECK_NORMAL));
44253089abeschrock
44353089abeschrock	spa_close(spa, FTAG);
44453089abeschrock}
44553089abeschrock
44653089abeschrockstatic int
44753089abeschrockzhack_do_feature(int argc, char **argv)
44853089abeschrock{
44953089abeschrock	char *subcommand;
45053089abeschrock
45153089abeschrock	argc--;
45253089abeschrock	argv++;
45353089abeschrock	if (argc == 0) {
45453089abeschrock		(void) fprintf(stderr,
45553089abeschrock		    "error: no feature operation specified\n");
45653089abeschrock		usage();
45753089abeschrock	}
45853089abeschrock
45953089abeschrock	subcommand = argv[0];
46053089abeschrock	if (strcmp(subcommand, "stat") == 0) {
46153089abeschrock		zhack_do_feature_stat(argc, argv);
46253089abeschrock	} else if (strcmp(subcommand, "enable") == 0) {
46353089abeschrock		zhack_do_feature_enable(argc, argv);
46453089abeschrock	} else if (strcmp(subcommand, "ref") == 0) {
46553089abeschrock		zhack_do_feature_ref(argc, argv);
46653089abeschrock	} else {
46753089abeschrock		(void) fprintf(stderr, "error: unknown subcommand: %s\n",
46853089abeschrock		    subcommand);
46953089abeschrock		usage();
47053089abeschrock	}
47153089abeschrock
47253089abeschrock	return (0);
47353089abeschrock}
47453089abeschrock
47553089abeschrock#define	MAX_NUM_PATHS 1024
47653089abeschrock
47753089abeschrockint
47853089abeschrockmain(int argc, char **argv)
47953089abeschrock{
48053089abeschrock	extern void zfs_prop_init(void);
48153089abeschrock
48253089abeschrock	char *path[MAX_NUM_PATHS];
48353089abeschrock	const char *subcommand;
48453089abeschrock	int rv = 0;
48553089abeschrock	char c;
48653089abeschrock
48753089abeschrock	g_importargs.path = path;
48853089abeschrock
48953089abeschrock	dprintf_setup(&argc, argv);
49053089abeschrock	zfs_prop_init();
49153089abeschrock
49253089abeschrock	while ((c = getopt(argc, argv, "c:d:")) != -1) {
49353089abeschrock		switch (c) {
49453089abeschrock		case 'c':
49553089abeschrock			g_importargs.cachefile = optarg;
49653089abeschrock			break;
49753089abeschrock		case 'd':
49853089abeschrock			assert(g_importargs.paths < MAX_NUM_PATHS);
49953089abeschrock			g_importargs.path[g_importargs.paths++] = optarg;
50053089abeschrock			break;
50153089abeschrock		default:
50253089abeschrock			usage();
50353089abeschrock			break;
50453089abeschrock		}
50553089abeschrock	}
50653089abeschrock
50753089abeschrock	argc -= optind;
50853089abeschrock	argv += optind;
50953089abeschrock	optind = 1;
51053089abeschrock
51153089abeschrock	if (argc == 0) {
51253089abeschrock		(void) fprintf(stderr, "error: no command specified\n");
51353089abeschrock		usage();
51453089abeschrock	}
51553089abeschrock
51653089abeschrock	subcommand = argv[0];
51753089abeschrock
51853089abeschrock	if (strcmp(subcommand, "feature") == 0) {
51953089abeschrock		rv = zhack_do_feature(argc, argv);
52053089abeschrock	} else {
52153089abeschrock		(void) fprintf(stderr, "error: unknown subcommand: %s\n",
52253089abeschrock		    subcommand);
52353089abeschrock		usage();
52453089abeschrock	}
52553089abeschrock
5267fdd916George Wilson	if (!g_readonly && spa_export(g_pool, NULL, B_TRUE, B_FALSE) != 0) {
5277fdd916George Wilson		fatal(NULL, FTAG, "pool export failed; "
52853089abeschrock		    "changes may not be committed to disk\n");
52953089abeschrock	}
53053089abeschrock
53153089abeschrock	libzfs_fini(g_zfs);
53253089abeschrock	kernel_fini();
53353089abeschrock
53453089abeschrock	return (rv);
53553089abeschrock}
536