xref: /illumos-gate/usr/src/cmd/zhack/zhack.c (revision 53089ab7c84db6fb76c16ca50076c147cda11757)
1*53089ab7Seschrock /*
2*53089ab7Seschrock  * CDDL HEADER START
3*53089ab7Seschrock  *
4*53089ab7Seschrock  * The contents of this file are subject to the terms of the
5*53089ab7Seschrock  * Common Development and Distribution License (the "License").
6*53089ab7Seschrock  * You may not use this file except in compliance with the License.
7*53089ab7Seschrock  *
8*53089ab7Seschrock  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*53089ab7Seschrock  * or http://www.opensolaris.org/os/licensing.
10*53089ab7Seschrock  * See the License for the specific language governing permissions
11*53089ab7Seschrock  * and limitations under the License.
12*53089ab7Seschrock  *
13*53089ab7Seschrock  * When distributing Covered Code, include this CDDL HEADER in each
14*53089ab7Seschrock  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*53089ab7Seschrock  * If applicable, add the following below this CDDL HEADER, with the
16*53089ab7Seschrock  * fields enclosed by brackets "[]" replaced with your own identifying
17*53089ab7Seschrock  * information: Portions Copyright [yyyy] [name of copyright owner]
18*53089ab7Seschrock  *
19*53089ab7Seschrock  * CDDL HEADER END
20*53089ab7Seschrock  */
21*53089ab7Seschrock 
22*53089ab7Seschrock /*
23*53089ab7Seschrock  * Copyright (c) 2012 by Delphix. All rights reserved.
24*53089ab7Seschrock  */
25*53089ab7Seschrock 
26*53089ab7Seschrock /*
27*53089ab7Seschrock  * zhack is a debugging tool that can write changes to ZFS pool using libzpool
28*53089ab7Seschrock  * for testing purposes. Altering pools with zhack is unsupported and may
29*53089ab7Seschrock  * result in corrupted pools.
30*53089ab7Seschrock  */
31*53089ab7Seschrock 
32*53089ab7Seschrock #include <stdio.h>
33*53089ab7Seschrock #include <stdlib.h>
34*53089ab7Seschrock #include <ctype.h>
35*53089ab7Seschrock #include <sys/zfs_context.h>
36*53089ab7Seschrock #include <sys/spa.h>
37*53089ab7Seschrock #include <sys/spa_impl.h>
38*53089ab7Seschrock #include <sys/dmu.h>
39*53089ab7Seschrock #include <sys/zap.h>
40*53089ab7Seschrock #include <sys/zfs_znode.h>
41*53089ab7Seschrock #include <sys/dsl_synctask.h>
42*53089ab7Seschrock #include <sys/vdev.h>
43*53089ab7Seschrock #include <sys/fs/zfs.h>
44*53089ab7Seschrock #include <sys/dmu_objset.h>
45*53089ab7Seschrock #include <sys/dsl_pool.h>
46*53089ab7Seschrock #include <sys/zio_checksum.h>
47*53089ab7Seschrock #include <sys/zio_compress.h>
48*53089ab7Seschrock #include <sys/zfeature.h>
49*53089ab7Seschrock #undef ZFS_MAXNAMELEN
50*53089ab7Seschrock #undef verify
51*53089ab7Seschrock #include <libzfs.h>
52*53089ab7Seschrock 
53*53089ab7Seschrock extern boolean_t zfeature_checks_disable;
54*53089ab7Seschrock 
55*53089ab7Seschrock const char cmdname[] = "zhack";
56*53089ab7Seschrock libzfs_handle_t *g_zfs;
57*53089ab7Seschrock static importargs_t g_importargs;
58*53089ab7Seschrock static char *g_pool;
59*53089ab7Seschrock static boolean_t g_readonly;
60*53089ab7Seschrock 
61*53089ab7Seschrock static void
62*53089ab7Seschrock usage(void)
63*53089ab7Seschrock {
64*53089ab7Seschrock 	(void) fprintf(stderr,
65*53089ab7Seschrock 	    "Usage: %s [-c cachefile] [-d dir] <subcommand> <args> ...\n"
66*53089ab7Seschrock 	    "where <subcommand> <args> is one of the following:\n"
67*53089ab7Seschrock 	    "\n", cmdname);
68*53089ab7Seschrock 
69*53089ab7Seschrock 	(void) fprintf(stderr,
70*53089ab7Seschrock 	    "    feature stat <pool>\n"
71*53089ab7Seschrock 	    "        print information about enabled features\n"
72*53089ab7Seschrock 	    "    feature enable [-d desc] <pool> <feature>\n"
73*53089ab7Seschrock 	    "        add a new enabled feature to the pool\n"
74*53089ab7Seschrock 	    "        -d <desc> sets the feature's description\n"
75*53089ab7Seschrock 	    "    feature ref [-md] <pool> <feature>\n"
76*53089ab7Seschrock 	    "        change the refcount on the given feature\n"
77*53089ab7Seschrock 	    "        -d decrease instead of increase the refcount\n"
78*53089ab7Seschrock 	    "        -m add the feature to the label if increasing refcount\n"
79*53089ab7Seschrock 	    "\n"
80*53089ab7Seschrock 	    "    <feature> : should be a feature guid\n");
81*53089ab7Seschrock 	exit(1);
82*53089ab7Seschrock }
83*53089ab7Seschrock 
84*53089ab7Seschrock 
85*53089ab7Seschrock static void
86*53089ab7Seschrock fatal(const char *fmt, ...)
87*53089ab7Seschrock {
88*53089ab7Seschrock 	va_list ap;
89*53089ab7Seschrock 
90*53089ab7Seschrock 	va_start(ap, fmt);
91*53089ab7Seschrock 	(void) fprintf(stderr, "%s: ", cmdname);
92*53089ab7Seschrock 	(void) vfprintf(stderr, fmt, ap);
93*53089ab7Seschrock 	va_end(ap);
94*53089ab7Seschrock 	(void) fprintf(stderr, "\n");
95*53089ab7Seschrock 
96*53089ab7Seschrock 	exit(1);
97*53089ab7Seschrock }
98*53089ab7Seschrock 
99*53089ab7Seschrock /* ARGSUSED */
100*53089ab7Seschrock static int
101*53089ab7Seschrock space_delta_cb(dmu_object_type_t bonustype, void *data,
102*53089ab7Seschrock     uint64_t *userp, uint64_t *groupp)
103*53089ab7Seschrock {
104*53089ab7Seschrock 	/*
105*53089ab7Seschrock 	 * Is it a valid type of object to track?
106*53089ab7Seschrock 	 */
107*53089ab7Seschrock 	if (bonustype != DMU_OT_ZNODE && bonustype != DMU_OT_SA)
108*53089ab7Seschrock 		return (ENOENT);
109*53089ab7Seschrock 	(void) fprintf(stderr, "modifying object that needs user accounting");
110*53089ab7Seschrock 	abort();
111*53089ab7Seschrock 	/* NOTREACHED */
112*53089ab7Seschrock }
113*53089ab7Seschrock 
114*53089ab7Seschrock /*
115*53089ab7Seschrock  * Target is the dataset whose pool we want to open.
116*53089ab7Seschrock  */
117*53089ab7Seschrock static void
118*53089ab7Seschrock import_pool(const char *target, boolean_t readonly)
119*53089ab7Seschrock {
120*53089ab7Seschrock 	nvlist_t *config;
121*53089ab7Seschrock 	nvlist_t *pools;
122*53089ab7Seschrock 	int error;
123*53089ab7Seschrock 	char *sepp;
124*53089ab7Seschrock 	spa_t *spa;
125*53089ab7Seschrock 	nvpair_t *elem;
126*53089ab7Seschrock 	nvlist_t *props;
127*53089ab7Seschrock 	const char *name;
128*53089ab7Seschrock 
129*53089ab7Seschrock 	kernel_init(readonly ? FREAD : (FREAD | FWRITE));
130*53089ab7Seschrock 	g_zfs = libzfs_init();
131*53089ab7Seschrock 	ASSERT(g_zfs != NULL);
132*53089ab7Seschrock 
133*53089ab7Seschrock 	dmu_objset_register_type(DMU_OST_ZFS, space_delta_cb);
134*53089ab7Seschrock 
135*53089ab7Seschrock 	g_readonly = readonly;
136*53089ab7Seschrock 
137*53089ab7Seschrock 	/*
138*53089ab7Seschrock 	 * If we only want readonly access, it's OK if we find
139*53089ab7Seschrock 	 * a potentially-active (ie, imported into the kernel) pool from the
140*53089ab7Seschrock 	 * default cachefile.
141*53089ab7Seschrock 	 */
142*53089ab7Seschrock 	if (readonly && spa_open(target, &spa, FTAG) == 0) {
143*53089ab7Seschrock 		spa_close(spa, FTAG);
144*53089ab7Seschrock 		return;
145*53089ab7Seschrock 	}
146*53089ab7Seschrock 
147*53089ab7Seschrock 	g_importargs.unique = B_TRUE;
148*53089ab7Seschrock 	g_importargs.can_be_active = readonly;
149*53089ab7Seschrock 	g_pool = strdup(target);
150*53089ab7Seschrock 	if ((sepp = strpbrk(g_pool, "/@")) != NULL)
151*53089ab7Seschrock 		*sepp = '\0';
152*53089ab7Seschrock 	g_importargs.poolname = g_pool;
153*53089ab7Seschrock 	pools = zpool_search_import(g_zfs, &g_importargs);
154*53089ab7Seschrock 
155*53089ab7Seschrock 	if (pools == NULL || nvlist_next_nvpair(pools, NULL) == NULL) {
156*53089ab7Seschrock 		if (!g_importargs.can_be_active) {
157*53089ab7Seschrock 			g_importargs.can_be_active = B_TRUE;
158*53089ab7Seschrock 			if (zpool_search_import(g_zfs, &g_importargs) != NULL ||
159*53089ab7Seschrock 			    spa_open(target, &spa, FTAG) == 0) {
160*53089ab7Seschrock 				fatal("cannot import '%s': pool is active; run "
161*53089ab7Seschrock 				    "\"zpool export %s\" first\n",
162*53089ab7Seschrock 				    g_pool, g_pool);
163*53089ab7Seschrock 			}
164*53089ab7Seschrock 		}
165*53089ab7Seschrock 
166*53089ab7Seschrock 		fatal("cannot import '%s': no such pool available\n", g_pool);
167*53089ab7Seschrock 	}
168*53089ab7Seschrock 
169*53089ab7Seschrock 	elem = nvlist_next_nvpair(pools, NULL);
170*53089ab7Seschrock 	name = nvpair_name(elem);
171*53089ab7Seschrock 	verify(nvpair_value_nvlist(elem, &config) == 0);
172*53089ab7Seschrock 
173*53089ab7Seschrock 	props = NULL;
174*53089ab7Seschrock 	if (readonly) {
175*53089ab7Seschrock 		verify(nvlist_alloc(&props, NV_UNIQUE_NAME, 0) == 0);
176*53089ab7Seschrock 		verify(nvlist_add_uint64(props,
177*53089ab7Seschrock 		    zpool_prop_to_name(ZPOOL_PROP_READONLY), 1) == 0);
178*53089ab7Seschrock 	}
179*53089ab7Seschrock 
180*53089ab7Seschrock 	zfeature_checks_disable = B_TRUE;
181*53089ab7Seschrock 	error = spa_import(name, config, props, ZFS_IMPORT_NORMAL);
182*53089ab7Seschrock 	zfeature_checks_disable = B_FALSE;
183*53089ab7Seschrock 	if (error == EEXIST)
184*53089ab7Seschrock 		error = 0;
185*53089ab7Seschrock 
186*53089ab7Seschrock 	if (error)
187*53089ab7Seschrock 		fatal("can't import '%s': %s", name, strerror(error));
188*53089ab7Seschrock }
189*53089ab7Seschrock 
190*53089ab7Seschrock static void
191*53089ab7Seschrock zhack_spa_open(const char *target, boolean_t readonly, void *tag, spa_t **spa)
192*53089ab7Seschrock {
193*53089ab7Seschrock 	int err;
194*53089ab7Seschrock 
195*53089ab7Seschrock 	import_pool(target, readonly);
196*53089ab7Seschrock 
197*53089ab7Seschrock 	zfeature_checks_disable = B_TRUE;
198*53089ab7Seschrock 	err = spa_open(target, spa, tag);
199*53089ab7Seschrock 	zfeature_checks_disable = B_FALSE;
200*53089ab7Seschrock 
201*53089ab7Seschrock 	if (err != 0)
202*53089ab7Seschrock 		fatal("cannot open '%s': %s", target, strerror(err));
203*53089ab7Seschrock 	if (spa_version(*spa) < SPA_VERSION_FEATURES) {
204*53089ab7Seschrock 		fatal("'%s' has version %d, features not enabled", target,
205*53089ab7Seschrock 		    (int)spa_version(*spa));
206*53089ab7Seschrock 	}
207*53089ab7Seschrock }
208*53089ab7Seschrock 
209*53089ab7Seschrock static void
210*53089ab7Seschrock dump_obj(objset_t *os, uint64_t obj, const char *name)
211*53089ab7Seschrock {
212*53089ab7Seschrock 	zap_cursor_t zc;
213*53089ab7Seschrock 	zap_attribute_t za;
214*53089ab7Seschrock 
215*53089ab7Seschrock 	(void) printf("%s_obj:\n", name);
216*53089ab7Seschrock 
217*53089ab7Seschrock 	for (zap_cursor_init(&zc, os, obj);
218*53089ab7Seschrock 	    zap_cursor_retrieve(&zc, &za) == 0;
219*53089ab7Seschrock 	    zap_cursor_advance(&zc)) {
220*53089ab7Seschrock 		if (za.za_integer_length == 8) {
221*53089ab7Seschrock 			ASSERT(za.za_num_integers == 1);
222*53089ab7Seschrock 			(void) printf("\t%s = %llu\n",
223*53089ab7Seschrock 			    za.za_name, (u_longlong_t)za.za_first_integer);
224*53089ab7Seschrock 		} else {
225*53089ab7Seschrock 			ASSERT(za.za_integer_length == 1);
226*53089ab7Seschrock 			char val[1024];
227*53089ab7Seschrock 			VERIFY(zap_lookup(os, obj, za.za_name,
228*53089ab7Seschrock 			    1, sizeof (val), val) == 0);
229*53089ab7Seschrock 			(void) printf("\t%s = %s\n", za.za_name, val);
230*53089ab7Seschrock 		}
231*53089ab7Seschrock 	}
232*53089ab7Seschrock 	zap_cursor_fini(&zc);
233*53089ab7Seschrock }
234*53089ab7Seschrock 
235*53089ab7Seschrock static void
236*53089ab7Seschrock dump_mos(spa_t *spa)
237*53089ab7Seschrock {
238*53089ab7Seschrock 	nvlist_t *nv = spa->spa_label_features;
239*53089ab7Seschrock 
240*53089ab7Seschrock 	(void) printf("label config:\n");
241*53089ab7Seschrock 	for (nvpair_t *pair = nvlist_next_nvpair(nv, NULL);
242*53089ab7Seschrock 	    pair != NULL;
243*53089ab7Seschrock 	    pair = nvlist_next_nvpair(nv, pair)) {
244*53089ab7Seschrock 		(void) printf("\t%s\n", nvpair_name(pair));
245*53089ab7Seschrock 	}
246*53089ab7Seschrock }
247*53089ab7Seschrock 
248*53089ab7Seschrock static void
249*53089ab7Seschrock zhack_do_feature_stat(int argc, char **argv)
250*53089ab7Seschrock {
251*53089ab7Seschrock 	spa_t *spa;
252*53089ab7Seschrock 	objset_t *os;
253*53089ab7Seschrock 	char *target;
254*53089ab7Seschrock 
255*53089ab7Seschrock 	argc--;
256*53089ab7Seschrock 	argv++;
257*53089ab7Seschrock 
258*53089ab7Seschrock 	if (argc < 1) {
259*53089ab7Seschrock 		(void) fprintf(stderr, "error: missing pool name\n");
260*53089ab7Seschrock 		usage();
261*53089ab7Seschrock 	}
262*53089ab7Seschrock 	target = argv[0];
263*53089ab7Seschrock 
264*53089ab7Seschrock 	zhack_spa_open(target, B_TRUE, FTAG, &spa);
265*53089ab7Seschrock 	os = spa->spa_meta_objset;
266*53089ab7Seschrock 
267*53089ab7Seschrock 	dump_obj(os, spa->spa_feat_for_read_obj, "for_read");
268*53089ab7Seschrock 	dump_obj(os, spa->spa_feat_for_write_obj, "for_write");
269*53089ab7Seschrock 	dump_obj(os, spa->spa_feat_desc_obj, "descriptions");
270*53089ab7Seschrock 	dump_mos(spa);
271*53089ab7Seschrock 
272*53089ab7Seschrock 	spa_close(spa, FTAG);
273*53089ab7Seschrock }
274*53089ab7Seschrock 
275*53089ab7Seschrock static void
276*53089ab7Seschrock feature_enable_sync(void *arg1, void *arg2, dmu_tx_t *tx)
277*53089ab7Seschrock {
278*53089ab7Seschrock 	spa_t *spa = arg1;
279*53089ab7Seschrock 	zfeature_info_t *feature = arg2;
280*53089ab7Seschrock 
281*53089ab7Seschrock 	spa_feature_enable(spa, feature, tx);
282*53089ab7Seschrock }
283*53089ab7Seschrock 
284*53089ab7Seschrock static void
285*53089ab7Seschrock zhack_do_feature_enable(int argc, char **argv)
286*53089ab7Seschrock {
287*53089ab7Seschrock 	char c;
288*53089ab7Seschrock 	char *desc, *target;
289*53089ab7Seschrock 	spa_t *spa;
290*53089ab7Seschrock 	objset_t *mos;
291*53089ab7Seschrock 	zfeature_info_t feature;
292*53089ab7Seschrock 	zfeature_info_t *nodeps[] = { NULL };
293*53089ab7Seschrock 
294*53089ab7Seschrock 	/*
295*53089ab7Seschrock 	 * Features are not added to the pool's label until their refcounts
296*53089ab7Seschrock 	 * are incremented, so fi_mos can just be left as false for now.
297*53089ab7Seschrock 	 */
298*53089ab7Seschrock 	desc = NULL;
299*53089ab7Seschrock 	feature.fi_uname = "zhack";
300*53089ab7Seschrock 	feature.fi_mos = B_FALSE;
301*53089ab7Seschrock 	feature.fi_can_readonly = B_FALSE;
302*53089ab7Seschrock 	feature.fi_depends = nodeps;
303*53089ab7Seschrock 
304*53089ab7Seschrock 	optind = 1;
305*53089ab7Seschrock 	while ((c = getopt(argc, argv, "rmd:")) != -1) {
306*53089ab7Seschrock 		switch (c) {
307*53089ab7Seschrock 		case 'r':
308*53089ab7Seschrock 			feature.fi_can_readonly = B_TRUE;
309*53089ab7Seschrock 			break;
310*53089ab7Seschrock 		case 'd':
311*53089ab7Seschrock 			desc = strdup(optarg);
312*53089ab7Seschrock 			break;
313*53089ab7Seschrock 		default:
314*53089ab7Seschrock 			usage();
315*53089ab7Seschrock 			break;
316*53089ab7Seschrock 		}
317*53089ab7Seschrock 	}
318*53089ab7Seschrock 
319*53089ab7Seschrock 	if (desc == NULL)
320*53089ab7Seschrock 		desc = strdup("zhack injected");
321*53089ab7Seschrock 	feature.fi_desc = desc;
322*53089ab7Seschrock 
323*53089ab7Seschrock 	argc -= optind;
324*53089ab7Seschrock 	argv += optind;
325*53089ab7Seschrock 
326*53089ab7Seschrock 	if (argc < 2) {
327*53089ab7Seschrock 		(void) fprintf(stderr, "error: missing feature or pool name\n");
328*53089ab7Seschrock 		usage();
329*53089ab7Seschrock 	}
330*53089ab7Seschrock 	target = argv[0];
331*53089ab7Seschrock 	feature.fi_guid = argv[1];
332*53089ab7Seschrock 
333*53089ab7Seschrock 	if (!zfeature_is_valid_guid(feature.fi_guid))
334*53089ab7Seschrock 		fatal("invalid feature guid: %s", feature.fi_guid);
335*53089ab7Seschrock 
336*53089ab7Seschrock 	zhack_spa_open(target, B_FALSE, FTAG, &spa);
337*53089ab7Seschrock 	mos = spa->spa_meta_objset;
338*53089ab7Seschrock 
339*53089ab7Seschrock 	if (0 == zfeature_lookup_guid(feature.fi_guid, NULL))
340*53089ab7Seschrock 		fatal("'%s' is a real feature, will not enable");
341*53089ab7Seschrock 	if (0 == zap_contains(mos, spa->spa_feat_desc_obj, feature.fi_guid))
342*53089ab7Seschrock 		fatal("feature already enabled: %s", feature.fi_guid);
343*53089ab7Seschrock 
344*53089ab7Seschrock 	VERIFY3U(0, ==, dsl_sync_task_do(spa->spa_dsl_pool, NULL,
345*53089ab7Seschrock 	    feature_enable_sync, spa, &feature, 5));
346*53089ab7Seschrock 
347*53089ab7Seschrock 	spa_close(spa, FTAG);
348*53089ab7Seschrock 
349*53089ab7Seschrock 	free(desc);
350*53089ab7Seschrock }
351*53089ab7Seschrock 
352*53089ab7Seschrock static void
353*53089ab7Seschrock feature_incr_sync(void *arg1, void *arg2, dmu_tx_t *tx)
354*53089ab7Seschrock {
355*53089ab7Seschrock 	spa_t *spa = arg1;
356*53089ab7Seschrock 	zfeature_info_t *feature = arg2;
357*53089ab7Seschrock 
358*53089ab7Seschrock 	spa_feature_incr(spa, feature, tx);
359*53089ab7Seschrock }
360*53089ab7Seschrock 
361*53089ab7Seschrock static void
362*53089ab7Seschrock feature_decr_sync(void *arg1, void *arg2, dmu_tx_t *tx)
363*53089ab7Seschrock {
364*53089ab7Seschrock 	spa_t *spa = arg1;
365*53089ab7Seschrock 	zfeature_info_t *feature = arg2;
366*53089ab7Seschrock 
367*53089ab7Seschrock 	spa_feature_decr(spa, feature, tx);
368*53089ab7Seschrock }
369*53089ab7Seschrock 
370*53089ab7Seschrock static void
371*53089ab7Seschrock zhack_do_feature_ref(int argc, char **argv)
372*53089ab7Seschrock {
373*53089ab7Seschrock 	char c;
374*53089ab7Seschrock 	char *target;
375*53089ab7Seschrock 	boolean_t decr = B_FALSE;
376*53089ab7Seschrock 	spa_t *spa;
377*53089ab7Seschrock 	objset_t *mos;
378*53089ab7Seschrock 	zfeature_info_t feature;
379*53089ab7Seschrock 	zfeature_info_t *nodeps[] = { NULL };
380*53089ab7Seschrock 
381*53089ab7Seschrock 	/*
382*53089ab7Seschrock 	 * fi_desc does not matter here because it was written to disk
383*53089ab7Seschrock 	 * when the feature was enabled, but we need to properly set the
384*53089ab7Seschrock 	 * feature for read or write based on the information we read off
385*53089ab7Seschrock 	 * disk later.
386*53089ab7Seschrock 	 */
387*53089ab7Seschrock 	feature.fi_uname = "zhack";
388*53089ab7Seschrock 	feature.fi_mos = B_FALSE;
389*53089ab7Seschrock 	feature.fi_desc = NULL;
390*53089ab7Seschrock 	feature.fi_depends = nodeps;
391*53089ab7Seschrock 
392*53089ab7Seschrock 	optind = 1;
393*53089ab7Seschrock 	while ((c = getopt(argc, argv, "md")) != -1) {
394*53089ab7Seschrock 		switch (c) {
395*53089ab7Seschrock 		case 'm':
396*53089ab7Seschrock 			feature.fi_mos = B_TRUE;
397*53089ab7Seschrock 			break;
398*53089ab7Seschrock 		case 'd':
399*53089ab7Seschrock 			decr = B_TRUE;
400*53089ab7Seschrock 			break;
401*53089ab7Seschrock 		default:
402*53089ab7Seschrock 			usage();
403*53089ab7Seschrock 			break;
404*53089ab7Seschrock 		}
405*53089ab7Seschrock 	}
406*53089ab7Seschrock 	argc -= optind;
407*53089ab7Seschrock 	argv += optind;
408*53089ab7Seschrock 
409*53089ab7Seschrock 	if (argc < 2) {
410*53089ab7Seschrock 		(void) fprintf(stderr, "error: missing feature or pool name\n");
411*53089ab7Seschrock 		usage();
412*53089ab7Seschrock 	}
413*53089ab7Seschrock 	target = argv[0];
414*53089ab7Seschrock 	feature.fi_guid = argv[1];
415*53089ab7Seschrock 
416*53089ab7Seschrock 	if (!zfeature_is_valid_guid(feature.fi_guid))
417*53089ab7Seschrock 		fatal("invalid feature guid: %s", feature.fi_guid);
418*53089ab7Seschrock 
419*53089ab7Seschrock 	zhack_spa_open(target, B_FALSE, FTAG, &spa);
420*53089ab7Seschrock 	mos = spa->spa_meta_objset;
421*53089ab7Seschrock 
422*53089ab7Seschrock 	if (0 == zfeature_lookup_guid(feature.fi_guid, NULL))
423*53089ab7Seschrock 		fatal("'%s' is a real feature, will not change refcount");
424*53089ab7Seschrock 
425*53089ab7Seschrock 	if (0 == zap_contains(mos, spa->spa_feat_for_read_obj,
426*53089ab7Seschrock 	    feature.fi_guid)) {
427*53089ab7Seschrock 		feature.fi_can_readonly = B_FALSE;
428*53089ab7Seschrock 	} else if (0 == zap_contains(mos, spa->spa_feat_for_write_obj,
429*53089ab7Seschrock 	    feature.fi_guid)) {
430*53089ab7Seschrock 		feature.fi_can_readonly = B_TRUE;
431*53089ab7Seschrock 	} else {
432*53089ab7Seschrock 		fatal("feature is not enabled: %s", feature.fi_guid);
433*53089ab7Seschrock 	}
434*53089ab7Seschrock 
435*53089ab7Seschrock 	if (decr && !spa_feature_is_active(spa, &feature))
436*53089ab7Seschrock 		fatal("feature refcount already 0: %s", feature.fi_guid);
437*53089ab7Seschrock 
438*53089ab7Seschrock 	VERIFY3U(0, ==, dsl_sync_task_do(spa->spa_dsl_pool, NULL,
439*53089ab7Seschrock 	    decr ? feature_decr_sync : feature_incr_sync, spa, &feature, 5));
440*53089ab7Seschrock 
441*53089ab7Seschrock 	spa_close(spa, FTAG);
442*53089ab7Seschrock }
443*53089ab7Seschrock 
444*53089ab7Seschrock static int
445*53089ab7Seschrock zhack_do_feature(int argc, char **argv)
446*53089ab7Seschrock {
447*53089ab7Seschrock 	char *subcommand;
448*53089ab7Seschrock 
449*53089ab7Seschrock 	argc--;
450*53089ab7Seschrock 	argv++;
451*53089ab7Seschrock 	if (argc == 0) {
452*53089ab7Seschrock 		(void) fprintf(stderr,
453*53089ab7Seschrock 		    "error: no feature operation specified\n");
454*53089ab7Seschrock 		usage();
455*53089ab7Seschrock 	}
456*53089ab7Seschrock 
457*53089ab7Seschrock 	subcommand = argv[0];
458*53089ab7Seschrock 	if (strcmp(subcommand, "stat") == 0) {
459*53089ab7Seschrock 		zhack_do_feature_stat(argc, argv);
460*53089ab7Seschrock 	} else if (strcmp(subcommand, "enable") == 0) {
461*53089ab7Seschrock 		zhack_do_feature_enable(argc, argv);
462*53089ab7Seschrock 	} else if (strcmp(subcommand, "ref") == 0) {
463*53089ab7Seschrock 		zhack_do_feature_ref(argc, argv);
464*53089ab7Seschrock 	} else {
465*53089ab7Seschrock 		(void) fprintf(stderr, "error: unknown subcommand: %s\n",
466*53089ab7Seschrock 		    subcommand);
467*53089ab7Seschrock 		usage();
468*53089ab7Seschrock 	}
469*53089ab7Seschrock 
470*53089ab7Seschrock 	return (0);
471*53089ab7Seschrock }
472*53089ab7Seschrock 
473*53089ab7Seschrock #define	MAX_NUM_PATHS 1024
474*53089ab7Seschrock 
475*53089ab7Seschrock int
476*53089ab7Seschrock main(int argc, char **argv)
477*53089ab7Seschrock {
478*53089ab7Seschrock 	extern void zfs_prop_init(void);
479*53089ab7Seschrock 
480*53089ab7Seschrock 	char *path[MAX_NUM_PATHS];
481*53089ab7Seschrock 	const char *subcommand;
482*53089ab7Seschrock 	int rv = 0;
483*53089ab7Seschrock 	char c;
484*53089ab7Seschrock 
485*53089ab7Seschrock 	g_importargs.path = path;
486*53089ab7Seschrock 
487*53089ab7Seschrock 	dprintf_setup(&argc, argv);
488*53089ab7Seschrock 	zfs_prop_init();
489*53089ab7Seschrock 
490*53089ab7Seschrock 	while ((c = getopt(argc, argv, "c:d:")) != -1) {
491*53089ab7Seschrock 		switch (c) {
492*53089ab7Seschrock 		case 'c':
493*53089ab7Seschrock 			g_importargs.cachefile = optarg;
494*53089ab7Seschrock 			break;
495*53089ab7Seschrock 		case 'd':
496*53089ab7Seschrock 			assert(g_importargs.paths < MAX_NUM_PATHS);
497*53089ab7Seschrock 			g_importargs.path[g_importargs.paths++] = optarg;
498*53089ab7Seschrock 			break;
499*53089ab7Seschrock 		default:
500*53089ab7Seschrock 			usage();
501*53089ab7Seschrock 			break;
502*53089ab7Seschrock 		}
503*53089ab7Seschrock 	}
504*53089ab7Seschrock 
505*53089ab7Seschrock 	argc -= optind;
506*53089ab7Seschrock 	argv += optind;
507*53089ab7Seschrock 	optind = 1;
508*53089ab7Seschrock 
509*53089ab7Seschrock 	if (argc == 0) {
510*53089ab7Seschrock 		(void) fprintf(stderr, "error: no command specified\n");
511*53089ab7Seschrock 		usage();
512*53089ab7Seschrock 	}
513*53089ab7Seschrock 
514*53089ab7Seschrock 	subcommand = argv[0];
515*53089ab7Seschrock 
516*53089ab7Seschrock 	if (strcmp(subcommand, "feature") == 0) {
517*53089ab7Seschrock 		rv = zhack_do_feature(argc, argv);
518*53089ab7Seschrock 	} else {
519*53089ab7Seschrock 		(void) fprintf(stderr, "error: unknown subcommand: %s\n",
520*53089ab7Seschrock 		    subcommand);
521*53089ab7Seschrock 		usage();
522*53089ab7Seschrock 	}
523*53089ab7Seschrock 
524*53089ab7Seschrock 	if (!g_readonly && spa_export(g_pool, NULL, B_TRUE, B_TRUE) != 0) {
525*53089ab7Seschrock 		fatal("pool export failed; "
526*53089ab7Seschrock 		    "changes may not be committed to disk\n");
527*53089ab7Seschrock 	}
528*53089ab7Seschrock 
529*53089ab7Seschrock 	libzfs_fini(g_zfs);
530*53089ab7Seschrock 	kernel_fini();
531*53089ab7Seschrock 
532*53089ab7Seschrock 	return (rv);
533*53089ab7Seschrock }
534