xref: /freebsd/sys/contrib/openzfs/cmd/zhack.c (revision 716fd348e01c5f2ba125f878a634a753436c2994)
1*716fd348SMartin Matuska /*
2*716fd348SMartin Matuska  * CDDL HEADER START
3*716fd348SMartin Matuska  *
4*716fd348SMartin Matuska  * The contents of this file are subject to the terms of the
5*716fd348SMartin Matuska  * Common Development and Distribution License (the "License").
6*716fd348SMartin Matuska  * You may not use this file except in compliance with the License.
7*716fd348SMartin Matuska  *
8*716fd348SMartin Matuska  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*716fd348SMartin Matuska  * or http://www.opensolaris.org/os/licensing.
10*716fd348SMartin Matuska  * See the License for the specific language governing permissions
11*716fd348SMartin Matuska  * and limitations under the License.
12*716fd348SMartin Matuska  *
13*716fd348SMartin Matuska  * When distributing Covered Code, include this CDDL HEADER in each
14*716fd348SMartin Matuska  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*716fd348SMartin Matuska  * If applicable, add the following below this CDDL HEADER, with the
16*716fd348SMartin Matuska  * fields enclosed by brackets "[]" replaced with your own identifying
17*716fd348SMartin Matuska  * information: Portions Copyright [yyyy] [name of copyright owner]
18*716fd348SMartin Matuska  *
19*716fd348SMartin Matuska  * CDDL HEADER END
20*716fd348SMartin Matuska  */
21*716fd348SMartin Matuska 
22*716fd348SMartin Matuska /*
23*716fd348SMartin Matuska  * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
24*716fd348SMartin Matuska  * Copyright (c) 2013 Steven Hartland. All rights reserved.
25*716fd348SMartin Matuska  */
26*716fd348SMartin Matuska 
27*716fd348SMartin Matuska /*
28*716fd348SMartin Matuska  * zhack is a debugging tool that can write changes to ZFS pool using libzpool
29*716fd348SMartin Matuska  * for testing purposes. Altering pools with zhack is unsupported and may
30*716fd348SMartin Matuska  * result in corrupted pools.
31*716fd348SMartin Matuska  */
32*716fd348SMartin Matuska 
33*716fd348SMartin Matuska #include <stdio.h>
34*716fd348SMartin Matuska #include <stdlib.h>
35*716fd348SMartin Matuska #include <ctype.h>
36*716fd348SMartin Matuska #include <sys/stat.h>
37*716fd348SMartin Matuska #include <sys/zfs_context.h>
38*716fd348SMartin Matuska #include <sys/spa.h>
39*716fd348SMartin Matuska #include <sys/spa_impl.h>
40*716fd348SMartin Matuska #include <sys/dmu.h>
41*716fd348SMartin Matuska #include <sys/zap.h>
42*716fd348SMartin Matuska #include <sys/zfs_znode.h>
43*716fd348SMartin Matuska #include <sys/dsl_synctask.h>
44*716fd348SMartin Matuska #include <sys/vdev.h>
45*716fd348SMartin Matuska #include <sys/vdev_impl.h>
46*716fd348SMartin Matuska #include <sys/fs/zfs.h>
47*716fd348SMartin Matuska #include <sys/dmu_objset.h>
48*716fd348SMartin Matuska #include <sys/dsl_pool.h>
49*716fd348SMartin Matuska #include <sys/zio_checksum.h>
50*716fd348SMartin Matuska #include <sys/zio_compress.h>
51*716fd348SMartin Matuska #include <sys/zfeature.h>
52*716fd348SMartin Matuska #include <sys/dmu_tx.h>
53*716fd348SMartin Matuska #include <zfeature_common.h>
54*716fd348SMartin Matuska #include <libzutil.h>
55*716fd348SMartin Matuska 
56*716fd348SMartin Matuska static importargs_t g_importargs;
57*716fd348SMartin Matuska static char *g_pool;
58*716fd348SMartin Matuska static boolean_t g_readonly;
59*716fd348SMartin Matuska 
60*716fd348SMartin Matuska static __attribute__((noreturn)) void
61*716fd348SMartin Matuska usage(void)
62*716fd348SMartin Matuska {
63*716fd348SMartin Matuska 	(void) fprintf(stderr,
64*716fd348SMartin Matuska 	    "Usage: zhack [-c cachefile] [-d dir] <subcommand> <args> ...\n"
65*716fd348SMartin Matuska 	    "where <subcommand> <args> is one of the following:\n"
66*716fd348SMartin Matuska 	    "\n");
67*716fd348SMartin Matuska 
68*716fd348SMartin Matuska 	(void) fprintf(stderr,
69*716fd348SMartin Matuska 	    "    feature stat <pool>\n"
70*716fd348SMartin Matuska 	    "        print information about enabled features\n"
71*716fd348SMartin Matuska 	    "    feature enable [-r] [-d desc] <pool> <feature>\n"
72*716fd348SMartin Matuska 	    "        add a new enabled feature to the pool\n"
73*716fd348SMartin Matuska 	    "        -d <desc> sets the feature's description\n"
74*716fd348SMartin Matuska 	    "        -r set read-only compatible flag for feature\n"
75*716fd348SMartin Matuska 	    "    feature ref [-md] <pool> <feature>\n"
76*716fd348SMartin Matuska 	    "        change the refcount on the given feature\n"
77*716fd348SMartin Matuska 	    "        -d decrease instead of increase the refcount\n"
78*716fd348SMartin Matuska 	    "        -m add the feature to the label if increasing refcount\n"
79*716fd348SMartin Matuska 	    "\n"
80*716fd348SMartin Matuska 	    "    <feature> : should be a feature guid\n"
81*716fd348SMartin Matuska 	    "\n"
82*716fd348SMartin Matuska 	    "    label repair <device>\n"
83*716fd348SMartin Matuska 	    "        repair corrupted label checksums\n"
84*716fd348SMartin Matuska 	    "\n"
85*716fd348SMartin Matuska 	    "    <device> : path to vdev\n");
86*716fd348SMartin Matuska 	exit(1);
87*716fd348SMartin Matuska }
88*716fd348SMartin Matuska 
89*716fd348SMartin Matuska 
90*716fd348SMartin Matuska static __attribute__((format(printf, 3, 4))) __attribute__((noreturn)) void
91*716fd348SMartin Matuska fatal(spa_t *spa, void *tag, const char *fmt, ...)
92*716fd348SMartin Matuska {
93*716fd348SMartin Matuska 	va_list ap;
94*716fd348SMartin Matuska 
95*716fd348SMartin Matuska 	if (spa != NULL) {
96*716fd348SMartin Matuska 		spa_close(spa, tag);
97*716fd348SMartin Matuska 		(void) spa_export(g_pool, NULL, B_TRUE, B_FALSE);
98*716fd348SMartin Matuska 	}
99*716fd348SMartin Matuska 
100*716fd348SMartin Matuska 	va_start(ap, fmt);
101*716fd348SMartin Matuska 	(void) fputs("zhack: ", stderr);
102*716fd348SMartin Matuska 	(void) vfprintf(stderr, fmt, ap);
103*716fd348SMartin Matuska 	va_end(ap);
104*716fd348SMartin Matuska 	(void) fputc('\n', stderr);
105*716fd348SMartin Matuska 
106*716fd348SMartin Matuska 	exit(1);
107*716fd348SMartin Matuska }
108*716fd348SMartin Matuska 
109*716fd348SMartin Matuska static int
110*716fd348SMartin Matuska space_delta_cb(dmu_object_type_t bonustype, const void *data,
111*716fd348SMartin Matuska     zfs_file_info_t *zoi)
112*716fd348SMartin Matuska {
113*716fd348SMartin Matuska 	(void) data, (void) zoi;
114*716fd348SMartin Matuska 
115*716fd348SMartin Matuska 	/*
116*716fd348SMartin Matuska 	 * Is it a valid type of object to track?
117*716fd348SMartin Matuska 	 */
118*716fd348SMartin Matuska 	if (bonustype != DMU_OT_ZNODE && bonustype != DMU_OT_SA)
119*716fd348SMartin Matuska 		return (ENOENT);
120*716fd348SMartin Matuska 	(void) fprintf(stderr, "modifying object that needs user accounting");
121*716fd348SMartin Matuska 	abort();
122*716fd348SMartin Matuska }
123*716fd348SMartin Matuska 
124*716fd348SMartin Matuska /*
125*716fd348SMartin Matuska  * Target is the dataset whose pool we want to open.
126*716fd348SMartin Matuska  */
127*716fd348SMartin Matuska static void
128*716fd348SMartin Matuska zhack_import(char *target, boolean_t readonly)
129*716fd348SMartin Matuska {
130*716fd348SMartin Matuska 	nvlist_t *config;
131*716fd348SMartin Matuska 	nvlist_t *props;
132*716fd348SMartin Matuska 	int error;
133*716fd348SMartin Matuska 
134*716fd348SMartin Matuska 	kernel_init(readonly ? SPA_MODE_READ :
135*716fd348SMartin Matuska 	    (SPA_MODE_READ | SPA_MODE_WRITE));
136*716fd348SMartin Matuska 
137*716fd348SMartin Matuska 	dmu_objset_register_type(DMU_OST_ZFS, space_delta_cb);
138*716fd348SMartin Matuska 
139*716fd348SMartin Matuska 	g_readonly = readonly;
140*716fd348SMartin Matuska 	g_importargs.can_be_active = readonly;
141*716fd348SMartin Matuska 	g_pool = strdup(target);
142*716fd348SMartin Matuska 
143*716fd348SMartin Matuska 	error = zpool_find_config(NULL, target, &config, &g_importargs,
144*716fd348SMartin Matuska 	    &libzpool_config_ops);
145*716fd348SMartin Matuska 	if (error)
146*716fd348SMartin Matuska 		fatal(NULL, FTAG, "cannot import '%s'", target);
147*716fd348SMartin Matuska 
148*716fd348SMartin Matuska 	props = NULL;
149*716fd348SMartin Matuska 	if (readonly) {
150*716fd348SMartin Matuska 		VERIFY(nvlist_alloc(&props, NV_UNIQUE_NAME, 0) == 0);
151*716fd348SMartin Matuska 		VERIFY(nvlist_add_uint64(props,
152*716fd348SMartin Matuska 		    zpool_prop_to_name(ZPOOL_PROP_READONLY), 1) == 0);
153*716fd348SMartin Matuska 	}
154*716fd348SMartin Matuska 
155*716fd348SMartin Matuska 	zfeature_checks_disable = B_TRUE;
156*716fd348SMartin Matuska 	error = spa_import(target, config, props,
157*716fd348SMartin Matuska 	    (readonly ?  ZFS_IMPORT_SKIP_MMP : ZFS_IMPORT_NORMAL));
158*716fd348SMartin Matuska 	fnvlist_free(config);
159*716fd348SMartin Matuska 	zfeature_checks_disable = B_FALSE;
160*716fd348SMartin Matuska 	if (error == EEXIST)
161*716fd348SMartin Matuska 		error = 0;
162*716fd348SMartin Matuska 
163*716fd348SMartin Matuska 	if (error)
164*716fd348SMartin Matuska 		fatal(NULL, FTAG, "can't import '%s': %s", target,
165*716fd348SMartin Matuska 		    strerror(error));
166*716fd348SMartin Matuska }
167*716fd348SMartin Matuska 
168*716fd348SMartin Matuska static void
169*716fd348SMartin Matuska zhack_spa_open(char *target, boolean_t readonly, void *tag, spa_t **spa)
170*716fd348SMartin Matuska {
171*716fd348SMartin Matuska 	int err;
172*716fd348SMartin Matuska 
173*716fd348SMartin Matuska 	zhack_import(target, readonly);
174*716fd348SMartin Matuska 
175*716fd348SMartin Matuska 	zfeature_checks_disable = B_TRUE;
176*716fd348SMartin Matuska 	err = spa_open(target, spa, tag);
177*716fd348SMartin Matuska 	zfeature_checks_disable = B_FALSE;
178*716fd348SMartin Matuska 
179*716fd348SMartin Matuska 	if (err != 0)
180*716fd348SMartin Matuska 		fatal(*spa, FTAG, "cannot open '%s': %s", target,
181*716fd348SMartin Matuska 		    strerror(err));
182*716fd348SMartin Matuska 	if (spa_version(*spa) < SPA_VERSION_FEATURES) {
183*716fd348SMartin Matuska 		fatal(*spa, FTAG, "'%s' has version %d, features not enabled",
184*716fd348SMartin Matuska 		    target, (int)spa_version(*spa));
185*716fd348SMartin Matuska 	}
186*716fd348SMartin Matuska }
187*716fd348SMartin Matuska 
188*716fd348SMartin Matuska static void
189*716fd348SMartin Matuska dump_obj(objset_t *os, uint64_t obj, const char *name)
190*716fd348SMartin Matuska {
191*716fd348SMartin Matuska 	zap_cursor_t zc;
192*716fd348SMartin Matuska 	zap_attribute_t za;
193*716fd348SMartin Matuska 
194*716fd348SMartin Matuska 	(void) printf("%s_obj:\n", name);
195*716fd348SMartin Matuska 
196*716fd348SMartin Matuska 	for (zap_cursor_init(&zc, os, obj);
197*716fd348SMartin Matuska 	    zap_cursor_retrieve(&zc, &za) == 0;
198*716fd348SMartin Matuska 	    zap_cursor_advance(&zc)) {
199*716fd348SMartin Matuska 		if (za.za_integer_length == 8) {
200*716fd348SMartin Matuska 			ASSERT(za.za_num_integers == 1);
201*716fd348SMartin Matuska 			(void) printf("\t%s = %llu\n",
202*716fd348SMartin Matuska 			    za.za_name, (u_longlong_t)za.za_first_integer);
203*716fd348SMartin Matuska 		} else {
204*716fd348SMartin Matuska 			ASSERT(za.za_integer_length == 1);
205*716fd348SMartin Matuska 			char val[1024];
206*716fd348SMartin Matuska 			VERIFY(zap_lookup(os, obj, za.za_name,
207*716fd348SMartin Matuska 			    1, sizeof (val), val) == 0);
208*716fd348SMartin Matuska 			(void) printf("\t%s = %s\n", za.za_name, val);
209*716fd348SMartin Matuska 		}
210*716fd348SMartin Matuska 	}
211*716fd348SMartin Matuska 	zap_cursor_fini(&zc);
212*716fd348SMartin Matuska }
213*716fd348SMartin Matuska 
214*716fd348SMartin Matuska static void
215*716fd348SMartin Matuska dump_mos(spa_t *spa)
216*716fd348SMartin Matuska {
217*716fd348SMartin Matuska 	nvlist_t *nv = spa->spa_label_features;
218*716fd348SMartin Matuska 	nvpair_t *pair;
219*716fd348SMartin Matuska 
220*716fd348SMartin Matuska 	(void) printf("label config:\n");
221*716fd348SMartin Matuska 	for (pair = nvlist_next_nvpair(nv, NULL);
222*716fd348SMartin Matuska 	    pair != NULL;
223*716fd348SMartin Matuska 	    pair = nvlist_next_nvpair(nv, pair)) {
224*716fd348SMartin Matuska 		(void) printf("\t%s\n", nvpair_name(pair));
225*716fd348SMartin Matuska 	}
226*716fd348SMartin Matuska }
227*716fd348SMartin Matuska 
228*716fd348SMartin Matuska static void
229*716fd348SMartin Matuska zhack_do_feature_stat(int argc, char **argv)
230*716fd348SMartin Matuska {
231*716fd348SMartin Matuska 	spa_t *spa;
232*716fd348SMartin Matuska 	objset_t *os;
233*716fd348SMartin Matuska 	char *target;
234*716fd348SMartin Matuska 
235*716fd348SMartin Matuska 	argc--;
236*716fd348SMartin Matuska 	argv++;
237*716fd348SMartin Matuska 
238*716fd348SMartin Matuska 	if (argc < 1) {
239*716fd348SMartin Matuska 		(void) fprintf(stderr, "error: missing pool name\n");
240*716fd348SMartin Matuska 		usage();
241*716fd348SMartin Matuska 	}
242*716fd348SMartin Matuska 	target = argv[0];
243*716fd348SMartin Matuska 
244*716fd348SMartin Matuska 	zhack_spa_open(target, B_TRUE, FTAG, &spa);
245*716fd348SMartin Matuska 	os = spa->spa_meta_objset;
246*716fd348SMartin Matuska 
247*716fd348SMartin Matuska 	dump_obj(os, spa->spa_feat_for_read_obj, "for_read");
248*716fd348SMartin Matuska 	dump_obj(os, spa->spa_feat_for_write_obj, "for_write");
249*716fd348SMartin Matuska 	dump_obj(os, spa->spa_feat_desc_obj, "descriptions");
250*716fd348SMartin Matuska 	if (spa_feature_is_active(spa, SPA_FEATURE_ENABLED_TXG)) {
251*716fd348SMartin Matuska 		dump_obj(os, spa->spa_feat_enabled_txg_obj, "enabled_txg");
252*716fd348SMartin Matuska 	}
253*716fd348SMartin Matuska 	dump_mos(spa);
254*716fd348SMartin Matuska 
255*716fd348SMartin Matuska 	spa_close(spa, FTAG);
256*716fd348SMartin Matuska }
257*716fd348SMartin Matuska 
258*716fd348SMartin Matuska static void
259*716fd348SMartin Matuska zhack_feature_enable_sync(void *arg, dmu_tx_t *tx)
260*716fd348SMartin Matuska {
261*716fd348SMartin Matuska 	spa_t *spa = dmu_tx_pool(tx)->dp_spa;
262*716fd348SMartin Matuska 	zfeature_info_t *feature = arg;
263*716fd348SMartin Matuska 
264*716fd348SMartin Matuska 	feature_enable_sync(spa, feature, tx);
265*716fd348SMartin Matuska 
266*716fd348SMartin Matuska 	spa_history_log_internal(spa, "zhack enable feature", tx,
267*716fd348SMartin Matuska 	    "name=%s flags=%u",
268*716fd348SMartin Matuska 	    feature->fi_guid, feature->fi_flags);
269*716fd348SMartin Matuska }
270*716fd348SMartin Matuska 
271*716fd348SMartin Matuska static void
272*716fd348SMartin Matuska zhack_do_feature_enable(int argc, char **argv)
273*716fd348SMartin Matuska {
274*716fd348SMartin Matuska 	int c;
275*716fd348SMartin Matuska 	char *desc, *target;
276*716fd348SMartin Matuska 	spa_t *spa;
277*716fd348SMartin Matuska 	objset_t *mos;
278*716fd348SMartin Matuska 	zfeature_info_t feature;
279*716fd348SMartin Matuska 	const spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
280*716fd348SMartin Matuska 
281*716fd348SMartin Matuska 	/*
282*716fd348SMartin Matuska 	 * Features are not added to the pool's label until their refcounts
283*716fd348SMartin Matuska 	 * are incremented, so fi_mos can just be left as false for now.
284*716fd348SMartin Matuska 	 */
285*716fd348SMartin Matuska 	desc = NULL;
286*716fd348SMartin Matuska 	feature.fi_uname = "zhack";
287*716fd348SMartin Matuska 	feature.fi_flags = 0;
288*716fd348SMartin Matuska 	feature.fi_depends = nodeps;
289*716fd348SMartin Matuska 	feature.fi_feature = SPA_FEATURE_NONE;
290*716fd348SMartin Matuska 
291*716fd348SMartin Matuska 	optind = 1;
292*716fd348SMartin Matuska 	while ((c = getopt(argc, argv, "+rd:")) != -1) {
293*716fd348SMartin Matuska 		switch (c) {
294*716fd348SMartin Matuska 		case 'r':
295*716fd348SMartin Matuska 			feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT;
296*716fd348SMartin Matuska 			break;
297*716fd348SMartin Matuska 		case 'd':
298*716fd348SMartin Matuska 			desc = strdup(optarg);
299*716fd348SMartin Matuska 			break;
300*716fd348SMartin Matuska 		default:
301*716fd348SMartin Matuska 			usage();
302*716fd348SMartin Matuska 			break;
303*716fd348SMartin Matuska 		}
304*716fd348SMartin Matuska 	}
305*716fd348SMartin Matuska 
306*716fd348SMartin Matuska 	if (desc == NULL)
307*716fd348SMartin Matuska 		desc = strdup("zhack injected");
308*716fd348SMartin Matuska 	feature.fi_desc = desc;
309*716fd348SMartin Matuska 
310*716fd348SMartin Matuska 	argc -= optind;
311*716fd348SMartin Matuska 	argv += optind;
312*716fd348SMartin Matuska 
313*716fd348SMartin Matuska 	if (argc < 2) {
314*716fd348SMartin Matuska 		(void) fprintf(stderr, "error: missing feature or pool name\n");
315*716fd348SMartin Matuska 		usage();
316*716fd348SMartin Matuska 	}
317*716fd348SMartin Matuska 	target = argv[0];
318*716fd348SMartin Matuska 	feature.fi_guid = argv[1];
319*716fd348SMartin Matuska 
320*716fd348SMartin Matuska 	if (!zfeature_is_valid_guid(feature.fi_guid))
321*716fd348SMartin Matuska 		fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid);
322*716fd348SMartin Matuska 
323*716fd348SMartin Matuska 	zhack_spa_open(target, B_FALSE, FTAG, &spa);
324*716fd348SMartin Matuska 	mos = spa->spa_meta_objset;
325*716fd348SMartin Matuska 
326*716fd348SMartin Matuska 	if (zfeature_is_supported(feature.fi_guid))
327*716fd348SMartin Matuska 		fatal(spa, FTAG, "'%s' is a real feature, will not enable",
328*716fd348SMartin Matuska 		    feature.fi_guid);
329*716fd348SMartin Matuska 	if (0 == zap_contains(mos, spa->spa_feat_desc_obj, feature.fi_guid))
330*716fd348SMartin Matuska 		fatal(spa, FTAG, "feature already enabled: %s",
331*716fd348SMartin Matuska 		    feature.fi_guid);
332*716fd348SMartin Matuska 
333*716fd348SMartin Matuska 	VERIFY0(dsl_sync_task(spa_name(spa), NULL,
334*716fd348SMartin Matuska 	    zhack_feature_enable_sync, &feature, 5, ZFS_SPACE_CHECK_NORMAL));
335*716fd348SMartin Matuska 
336*716fd348SMartin Matuska 	spa_close(spa, FTAG);
337*716fd348SMartin Matuska 
338*716fd348SMartin Matuska 	free(desc);
339*716fd348SMartin Matuska }
340*716fd348SMartin Matuska 
341*716fd348SMartin Matuska static void
342*716fd348SMartin Matuska feature_incr_sync(void *arg, dmu_tx_t *tx)
343*716fd348SMartin Matuska {
344*716fd348SMartin Matuska 	spa_t *spa = dmu_tx_pool(tx)->dp_spa;
345*716fd348SMartin Matuska 	zfeature_info_t *feature = arg;
346*716fd348SMartin Matuska 	uint64_t refcount;
347*716fd348SMartin Matuska 
348*716fd348SMartin Matuska 	VERIFY0(feature_get_refcount_from_disk(spa, feature, &refcount));
349*716fd348SMartin Matuska 	feature_sync(spa, feature, refcount + 1, tx);
350*716fd348SMartin Matuska 	spa_history_log_internal(spa, "zhack feature incr", tx,
351*716fd348SMartin Matuska 	    "name=%s", feature->fi_guid);
352*716fd348SMartin Matuska }
353*716fd348SMartin Matuska 
354*716fd348SMartin Matuska static void
355*716fd348SMartin Matuska feature_decr_sync(void *arg, dmu_tx_t *tx)
356*716fd348SMartin Matuska {
357*716fd348SMartin Matuska 	spa_t *spa = dmu_tx_pool(tx)->dp_spa;
358*716fd348SMartin Matuska 	zfeature_info_t *feature = arg;
359*716fd348SMartin Matuska 	uint64_t refcount;
360*716fd348SMartin Matuska 
361*716fd348SMartin Matuska 	VERIFY0(feature_get_refcount_from_disk(spa, feature, &refcount));
362*716fd348SMartin Matuska 	feature_sync(spa, feature, refcount - 1, tx);
363*716fd348SMartin Matuska 	spa_history_log_internal(spa, "zhack feature decr", tx,
364*716fd348SMartin Matuska 	    "name=%s", feature->fi_guid);
365*716fd348SMartin Matuska }
366*716fd348SMartin Matuska 
367*716fd348SMartin Matuska static void
368*716fd348SMartin Matuska zhack_do_feature_ref(int argc, char **argv)
369*716fd348SMartin Matuska {
370*716fd348SMartin Matuska 	int c;
371*716fd348SMartin Matuska 	char *target;
372*716fd348SMartin Matuska 	boolean_t decr = B_FALSE;
373*716fd348SMartin Matuska 	spa_t *spa;
374*716fd348SMartin Matuska 	objset_t *mos;
375*716fd348SMartin Matuska 	zfeature_info_t feature;
376*716fd348SMartin Matuska 	const spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
377*716fd348SMartin Matuska 
378*716fd348SMartin Matuska 	/*
379*716fd348SMartin Matuska 	 * fi_desc does not matter here because it was written to disk
380*716fd348SMartin Matuska 	 * when the feature was enabled, but we need to properly set the
381*716fd348SMartin Matuska 	 * feature for read or write based on the information we read off
382*716fd348SMartin Matuska 	 * disk later.
383*716fd348SMartin Matuska 	 */
384*716fd348SMartin Matuska 	feature.fi_uname = "zhack";
385*716fd348SMartin Matuska 	feature.fi_flags = 0;
386*716fd348SMartin Matuska 	feature.fi_desc = NULL;
387*716fd348SMartin Matuska 	feature.fi_depends = nodeps;
388*716fd348SMartin Matuska 	feature.fi_feature = SPA_FEATURE_NONE;
389*716fd348SMartin Matuska 
390*716fd348SMartin Matuska 	optind = 1;
391*716fd348SMartin Matuska 	while ((c = getopt(argc, argv, "+md")) != -1) {
392*716fd348SMartin Matuska 		switch (c) {
393*716fd348SMartin Matuska 		case 'm':
394*716fd348SMartin Matuska 			feature.fi_flags |= ZFEATURE_FLAG_MOS;
395*716fd348SMartin Matuska 			break;
396*716fd348SMartin Matuska 		case 'd':
397*716fd348SMartin Matuska 			decr = B_TRUE;
398*716fd348SMartin Matuska 			break;
399*716fd348SMartin Matuska 		default:
400*716fd348SMartin Matuska 			usage();
401*716fd348SMartin Matuska 			break;
402*716fd348SMartin Matuska 		}
403*716fd348SMartin Matuska 	}
404*716fd348SMartin Matuska 	argc -= optind;
405*716fd348SMartin Matuska 	argv += optind;
406*716fd348SMartin Matuska 
407*716fd348SMartin Matuska 	if (argc < 2) {
408*716fd348SMartin Matuska 		(void) fprintf(stderr, "error: missing feature or pool name\n");
409*716fd348SMartin Matuska 		usage();
410*716fd348SMartin Matuska 	}
411*716fd348SMartin Matuska 	target = argv[0];
412*716fd348SMartin Matuska 	feature.fi_guid = argv[1];
413*716fd348SMartin Matuska 
414*716fd348SMartin Matuska 	if (!zfeature_is_valid_guid(feature.fi_guid))
415*716fd348SMartin Matuska 		fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid);
416*716fd348SMartin Matuska 
417*716fd348SMartin Matuska 	zhack_spa_open(target, B_FALSE, FTAG, &spa);
418*716fd348SMartin Matuska 	mos = spa->spa_meta_objset;
419*716fd348SMartin Matuska 
420*716fd348SMartin Matuska 	if (zfeature_is_supported(feature.fi_guid)) {
421*716fd348SMartin Matuska 		fatal(spa, FTAG,
422*716fd348SMartin Matuska 		    "'%s' is a real feature, will not change refcount",
423*716fd348SMartin Matuska 		    feature.fi_guid);
424*716fd348SMartin Matuska 	}
425*716fd348SMartin Matuska 
426*716fd348SMartin Matuska 	if (0 == zap_contains(mos, spa->spa_feat_for_read_obj,
427*716fd348SMartin Matuska 	    feature.fi_guid)) {
428*716fd348SMartin Matuska 		feature.fi_flags &= ~ZFEATURE_FLAG_READONLY_COMPAT;
429*716fd348SMartin Matuska 	} else if (0 == zap_contains(mos, spa->spa_feat_for_write_obj,
430*716fd348SMartin Matuska 	    feature.fi_guid)) {
431*716fd348SMartin Matuska 		feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT;
432*716fd348SMartin Matuska 	} else {
433*716fd348SMartin Matuska 		fatal(spa, FTAG, "feature is not enabled: %s", feature.fi_guid);
434*716fd348SMartin Matuska 	}
435*716fd348SMartin Matuska 
436*716fd348SMartin Matuska 	if (decr) {
437*716fd348SMartin Matuska 		uint64_t count;
438*716fd348SMartin Matuska 		if (feature_get_refcount_from_disk(spa, &feature,
439*716fd348SMartin Matuska 		    &count) == 0 && count == 0) {
440*716fd348SMartin Matuska 			fatal(spa, FTAG, "feature refcount already 0: %s",
441*716fd348SMartin Matuska 			    feature.fi_guid);
442*716fd348SMartin Matuska 		}
443*716fd348SMartin Matuska 	}
444*716fd348SMartin Matuska 
445*716fd348SMartin Matuska 	VERIFY0(dsl_sync_task(spa_name(spa), NULL,
446*716fd348SMartin Matuska 	    decr ? feature_decr_sync : feature_incr_sync, &feature,
447*716fd348SMartin Matuska 	    5, ZFS_SPACE_CHECK_NORMAL));
448*716fd348SMartin Matuska 
449*716fd348SMartin Matuska 	spa_close(spa, FTAG);
450*716fd348SMartin Matuska }
451*716fd348SMartin Matuska 
452*716fd348SMartin Matuska static int
453*716fd348SMartin Matuska zhack_do_feature(int argc, char **argv)
454*716fd348SMartin Matuska {
455*716fd348SMartin Matuska 	char *subcommand;
456*716fd348SMartin Matuska 
457*716fd348SMartin Matuska 	argc--;
458*716fd348SMartin Matuska 	argv++;
459*716fd348SMartin Matuska 	if (argc == 0) {
460*716fd348SMartin Matuska 		(void) fprintf(stderr,
461*716fd348SMartin Matuska 		    "error: no feature operation specified\n");
462*716fd348SMartin Matuska 		usage();
463*716fd348SMartin Matuska 	}
464*716fd348SMartin Matuska 
465*716fd348SMartin Matuska 	subcommand = argv[0];
466*716fd348SMartin Matuska 	if (strcmp(subcommand, "stat") == 0) {
467*716fd348SMartin Matuska 		zhack_do_feature_stat(argc, argv);
468*716fd348SMartin Matuska 	} else if (strcmp(subcommand, "enable") == 0) {
469*716fd348SMartin Matuska 		zhack_do_feature_enable(argc, argv);
470*716fd348SMartin Matuska 	} else if (strcmp(subcommand, "ref") == 0) {
471*716fd348SMartin Matuska 		zhack_do_feature_ref(argc, argv);
472*716fd348SMartin Matuska 	} else {
473*716fd348SMartin Matuska 		(void) fprintf(stderr, "error: unknown subcommand: %s\n",
474*716fd348SMartin Matuska 		    subcommand);
475*716fd348SMartin Matuska 		usage();
476*716fd348SMartin Matuska 	}
477*716fd348SMartin Matuska 
478*716fd348SMartin Matuska 	return (0);
479*716fd348SMartin Matuska }
480*716fd348SMartin Matuska 
481*716fd348SMartin Matuska static int
482*716fd348SMartin Matuska zhack_repair_label_cksum(int argc, char **argv)
483*716fd348SMartin Matuska {
484*716fd348SMartin Matuska 	zio_checksum_info_t *ci = &zio_checksum_table[ZIO_CHECKSUM_LABEL];
485*716fd348SMartin Matuska 	const char *cfg_keys[] = { ZPOOL_CONFIG_VERSION,
486*716fd348SMartin Matuska 	    ZPOOL_CONFIG_POOL_STATE, ZPOOL_CONFIG_GUID };
487*716fd348SMartin Matuska 	boolean_t labels_repaired[VDEV_LABELS] = {0};
488*716fd348SMartin Matuska 	boolean_t repaired = B_FALSE;
489*716fd348SMartin Matuska 	vdev_label_t labels[VDEV_LABELS] = {{{0}}};
490*716fd348SMartin Matuska 	struct stat st;
491*716fd348SMartin Matuska 	int fd;
492*716fd348SMartin Matuska 
493*716fd348SMartin Matuska 	abd_init();
494*716fd348SMartin Matuska 
495*716fd348SMartin Matuska 	argc -= 1;
496*716fd348SMartin Matuska 	argv += 1;
497*716fd348SMartin Matuska 
498*716fd348SMartin Matuska 	if (argc < 1) {
499*716fd348SMartin Matuska 		(void) fprintf(stderr, "error: missing device\n");
500*716fd348SMartin Matuska 		usage();
501*716fd348SMartin Matuska 	}
502*716fd348SMartin Matuska 
503*716fd348SMartin Matuska 	if ((fd = open(argv[0], O_RDWR)) == -1)
504*716fd348SMartin Matuska 		fatal(NULL, FTAG, "cannot open '%s': %s", argv[0],
505*716fd348SMartin Matuska 		    strerror(errno));
506*716fd348SMartin Matuska 
507*716fd348SMartin Matuska 	if (stat(argv[0], &st) != 0)
508*716fd348SMartin Matuska 		fatal(NULL, FTAG, "cannot stat '%s': %s", argv[0],
509*716fd348SMartin Matuska 		    strerror(errno));
510*716fd348SMartin Matuska 
511*716fd348SMartin Matuska 	for (int l = 0; l < VDEV_LABELS; l++) {
512*716fd348SMartin Matuska 		uint64_t label_offset, offset;
513*716fd348SMartin Matuska 		zio_cksum_t expected_cksum;
514*716fd348SMartin Matuska 		zio_cksum_t actual_cksum;
515*716fd348SMartin Matuska 		zio_cksum_t verifier;
516*716fd348SMartin Matuska 		zio_eck_t *eck;
517*716fd348SMartin Matuska 		nvlist_t *cfg;
518*716fd348SMartin Matuska 		int byteswap;
519*716fd348SMartin Matuska 		uint64_t val;
520*716fd348SMartin Matuska 		ssize_t err;
521*716fd348SMartin Matuska 
522*716fd348SMartin Matuska 		vdev_label_t *vl = &labels[l];
523*716fd348SMartin Matuska 
524*716fd348SMartin Matuska 		label_offset = vdev_label_offset(st.st_size, l, 0);
525*716fd348SMartin Matuska 		err = pread64(fd, vl, sizeof (vdev_label_t), label_offset);
526*716fd348SMartin Matuska 		if (err == -1) {
527*716fd348SMartin Matuska 			(void) fprintf(stderr, "error: cannot read "
528*716fd348SMartin Matuska 			    "label %d: %s\n", l, strerror(errno));
529*716fd348SMartin Matuska 			continue;
530*716fd348SMartin Matuska 		} else if (err != sizeof (vdev_label_t)) {
531*716fd348SMartin Matuska 			(void) fprintf(stderr, "error: bad label %d read size "
532*716fd348SMartin Matuska 			    "\n", l);
533*716fd348SMartin Matuska 			continue;
534*716fd348SMartin Matuska 		}
535*716fd348SMartin Matuska 
536*716fd348SMartin Matuska 		err = nvlist_unpack(vl->vl_vdev_phys.vp_nvlist,
537*716fd348SMartin Matuska 		    VDEV_PHYS_SIZE - sizeof (zio_eck_t), &cfg, 0);
538*716fd348SMartin Matuska 		if (err) {
539*716fd348SMartin Matuska 			(void) fprintf(stderr, "error: cannot unpack nvlist "
540*716fd348SMartin Matuska 			    "label %d\n", l);
541*716fd348SMartin Matuska 			continue;
542*716fd348SMartin Matuska 		}
543*716fd348SMartin Matuska 
544*716fd348SMartin Matuska 		for (int i = 0; i < ARRAY_SIZE(cfg_keys); i++) {
545*716fd348SMartin Matuska 			err = nvlist_lookup_uint64(cfg, cfg_keys[i], &val);
546*716fd348SMartin Matuska 			if (err) {
547*716fd348SMartin Matuska 				(void) fprintf(stderr, "error: label %d: "
548*716fd348SMartin Matuska 				    "cannot find nvlist key %s\n",
549*716fd348SMartin Matuska 				    l, cfg_keys[i]);
550*716fd348SMartin Matuska 				continue;
551*716fd348SMartin Matuska 			}
552*716fd348SMartin Matuska 		}
553*716fd348SMartin Matuska 
554*716fd348SMartin Matuska 		void *data = (char *)vl + offsetof(vdev_label_t, vl_vdev_phys);
555*716fd348SMartin Matuska 		eck = (zio_eck_t *)((char *)(data) + VDEV_PHYS_SIZE) - 1;
556*716fd348SMartin Matuska 
557*716fd348SMartin Matuska 		offset = label_offset + offsetof(vdev_label_t, vl_vdev_phys);
558*716fd348SMartin Matuska 		ZIO_SET_CHECKSUM(&verifier, offset, 0, 0, 0);
559*716fd348SMartin Matuska 
560*716fd348SMartin Matuska 		byteswap = (eck->zec_magic == BSWAP_64(ZEC_MAGIC));
561*716fd348SMartin Matuska 		if (byteswap)
562*716fd348SMartin Matuska 			byteswap_uint64_array(&verifier, sizeof (zio_cksum_t));
563*716fd348SMartin Matuska 
564*716fd348SMartin Matuska 		expected_cksum = eck->zec_cksum;
565*716fd348SMartin Matuska 		eck->zec_cksum = verifier;
566*716fd348SMartin Matuska 
567*716fd348SMartin Matuska 		abd_t *abd = abd_get_from_buf(data, VDEV_PHYS_SIZE);
568*716fd348SMartin Matuska 		ci->ci_func[byteswap](abd, VDEV_PHYS_SIZE, NULL, &actual_cksum);
569*716fd348SMartin Matuska 		abd_free(abd);
570*716fd348SMartin Matuska 
571*716fd348SMartin Matuska 		if (byteswap)
572*716fd348SMartin Matuska 			byteswap_uint64_array(&expected_cksum,
573*716fd348SMartin Matuska 			    sizeof (zio_cksum_t));
574*716fd348SMartin Matuska 
575*716fd348SMartin Matuska 		if (ZIO_CHECKSUM_EQUAL(actual_cksum, expected_cksum))
576*716fd348SMartin Matuska 			continue;
577*716fd348SMartin Matuska 
578*716fd348SMartin Matuska 		eck->zec_cksum = actual_cksum;
579*716fd348SMartin Matuska 
580*716fd348SMartin Matuska 		err = pwrite64(fd, data, VDEV_PHYS_SIZE, offset);
581*716fd348SMartin Matuska 		if (err == -1) {
582*716fd348SMartin Matuska 			(void) fprintf(stderr, "error: cannot write "
583*716fd348SMartin Matuska 			    "label %d: %s\n", l, strerror(errno));
584*716fd348SMartin Matuska 			continue;
585*716fd348SMartin Matuska 		} else if (err != VDEV_PHYS_SIZE) {
586*716fd348SMartin Matuska 			(void) fprintf(stderr, "error: bad write size "
587*716fd348SMartin Matuska 			    "label %d\n", l);
588*716fd348SMartin Matuska 			continue;
589*716fd348SMartin Matuska 		}
590*716fd348SMartin Matuska 
591*716fd348SMartin Matuska 		fsync(fd);
592*716fd348SMartin Matuska 
593*716fd348SMartin Matuska 		labels_repaired[l] = B_TRUE;
594*716fd348SMartin Matuska 	}
595*716fd348SMartin Matuska 
596*716fd348SMartin Matuska 	close(fd);
597*716fd348SMartin Matuska 
598*716fd348SMartin Matuska 	abd_fini();
599*716fd348SMartin Matuska 
600*716fd348SMartin Matuska 	for (int l = 0; l < VDEV_LABELS; l++) {
601*716fd348SMartin Matuska 		(void) printf("label %d: %s\n", l,
602*716fd348SMartin Matuska 		    labels_repaired[l] ? "repaired" : "skipped");
603*716fd348SMartin Matuska 		repaired |= labels_repaired[l];
604*716fd348SMartin Matuska 	}
605*716fd348SMartin Matuska 
606*716fd348SMartin Matuska 	if (repaired)
607*716fd348SMartin Matuska 		return (0);
608*716fd348SMartin Matuska 
609*716fd348SMartin Matuska 	return (1);
610*716fd348SMartin Matuska }
611*716fd348SMartin Matuska 
612*716fd348SMartin Matuska static int
613*716fd348SMartin Matuska zhack_do_label(int argc, char **argv)
614*716fd348SMartin Matuska {
615*716fd348SMartin Matuska 	char *subcommand;
616*716fd348SMartin Matuska 	int err;
617*716fd348SMartin Matuska 
618*716fd348SMartin Matuska 	argc--;
619*716fd348SMartin Matuska 	argv++;
620*716fd348SMartin Matuska 	if (argc == 0) {
621*716fd348SMartin Matuska 		(void) fprintf(stderr,
622*716fd348SMartin Matuska 		    "error: no label operation specified\n");
623*716fd348SMartin Matuska 		usage();
624*716fd348SMartin Matuska 	}
625*716fd348SMartin Matuska 
626*716fd348SMartin Matuska 	subcommand = argv[0];
627*716fd348SMartin Matuska 	if (strcmp(subcommand, "repair") == 0) {
628*716fd348SMartin Matuska 		err = zhack_repair_label_cksum(argc, argv);
629*716fd348SMartin Matuska 	} else {
630*716fd348SMartin Matuska 		(void) fprintf(stderr, "error: unknown subcommand: %s\n",
631*716fd348SMartin Matuska 		    subcommand);
632*716fd348SMartin Matuska 		usage();
633*716fd348SMartin Matuska 	}
634*716fd348SMartin Matuska 
635*716fd348SMartin Matuska 	return (err);
636*716fd348SMartin Matuska }
637*716fd348SMartin Matuska 
638*716fd348SMartin Matuska #define	MAX_NUM_PATHS 1024
639*716fd348SMartin Matuska 
640*716fd348SMartin Matuska int
641*716fd348SMartin Matuska main(int argc, char **argv)
642*716fd348SMartin Matuska {
643*716fd348SMartin Matuska 	extern void zfs_prop_init(void);
644*716fd348SMartin Matuska 
645*716fd348SMartin Matuska 	char *path[MAX_NUM_PATHS];
646*716fd348SMartin Matuska 	const char *subcommand;
647*716fd348SMartin Matuska 	int rv = 0;
648*716fd348SMartin Matuska 	int c;
649*716fd348SMartin Matuska 
650*716fd348SMartin Matuska 	g_importargs.path = path;
651*716fd348SMartin Matuska 
652*716fd348SMartin Matuska 	dprintf_setup(&argc, argv);
653*716fd348SMartin Matuska 	zfs_prop_init();
654*716fd348SMartin Matuska 
655*716fd348SMartin Matuska 	while ((c = getopt(argc, argv, "+c:d:")) != -1) {
656*716fd348SMartin Matuska 		switch (c) {
657*716fd348SMartin Matuska 		case 'c':
658*716fd348SMartin Matuska 			g_importargs.cachefile = optarg;
659*716fd348SMartin Matuska 			break;
660*716fd348SMartin Matuska 		case 'd':
661*716fd348SMartin Matuska 			assert(g_importargs.paths < MAX_NUM_PATHS);
662*716fd348SMartin Matuska 			g_importargs.path[g_importargs.paths++] = optarg;
663*716fd348SMartin Matuska 			break;
664*716fd348SMartin Matuska 		default:
665*716fd348SMartin Matuska 			usage();
666*716fd348SMartin Matuska 			break;
667*716fd348SMartin Matuska 		}
668*716fd348SMartin Matuska 	}
669*716fd348SMartin Matuska 
670*716fd348SMartin Matuska 	argc -= optind;
671*716fd348SMartin Matuska 	argv += optind;
672*716fd348SMartin Matuska 	optind = 1;
673*716fd348SMartin Matuska 
674*716fd348SMartin Matuska 	if (argc == 0) {
675*716fd348SMartin Matuska 		(void) fprintf(stderr, "error: no command specified\n");
676*716fd348SMartin Matuska 		usage();
677*716fd348SMartin Matuska 	}
678*716fd348SMartin Matuska 
679*716fd348SMartin Matuska 	subcommand = argv[0];
680*716fd348SMartin Matuska 
681*716fd348SMartin Matuska 	if (strcmp(subcommand, "feature") == 0) {
682*716fd348SMartin Matuska 		rv = zhack_do_feature(argc, argv);
683*716fd348SMartin Matuska 	} else if (strcmp(subcommand, "label") == 0) {
684*716fd348SMartin Matuska 		return (zhack_do_label(argc, argv));
685*716fd348SMartin Matuska 	} else {
686*716fd348SMartin Matuska 		(void) fprintf(stderr, "error: unknown subcommand: %s\n",
687*716fd348SMartin Matuska 		    subcommand);
688*716fd348SMartin Matuska 		usage();
689*716fd348SMartin Matuska 	}
690*716fd348SMartin Matuska 
691*716fd348SMartin Matuska 	if (!g_readonly && spa_export(g_pool, NULL, B_TRUE, B_FALSE) != 0) {
692*716fd348SMartin Matuska 		fatal(NULL, FTAG, "pool export failed; "
693*716fd348SMartin Matuska 		    "changes may not be committed to disk\n");
694*716fd348SMartin Matuska 	}
695*716fd348SMartin Matuska 
696*716fd348SMartin Matuska 	kernel_fini();
697*716fd348SMartin Matuska 
698*716fd348SMartin Matuska 	return (rv);
699*716fd348SMartin Matuska }
700