xref: /freebsd/sys/contrib/openzfs/cmd/zhack.c (revision 783d3ff6d7fae619db8a7990b8a6387de0c677b5)
1716fd348SMartin Matuska /*
2716fd348SMartin Matuska  * CDDL HEADER START
3716fd348SMartin Matuska  *
4716fd348SMartin Matuska  * The contents of this file are subject to the terms of the
5716fd348SMartin Matuska  * Common Development and Distribution License (the "License").
6716fd348SMartin Matuska  * You may not use this file except in compliance with the License.
7716fd348SMartin Matuska  *
8716fd348SMartin Matuska  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9271171e0SMartin Matuska  * or https://opensource.org/licenses/CDDL-1.0.
10716fd348SMartin Matuska  * See the License for the specific language governing permissions
11716fd348SMartin Matuska  * and limitations under the License.
12716fd348SMartin Matuska  *
13716fd348SMartin Matuska  * When distributing Covered Code, include this CDDL HEADER in each
14716fd348SMartin Matuska  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15716fd348SMartin Matuska  * If applicable, add the following below this CDDL HEADER, with the
16716fd348SMartin Matuska  * fields enclosed by brackets "[]" replaced with your own identifying
17716fd348SMartin Matuska  * information: Portions Copyright [yyyy] [name of copyright owner]
18716fd348SMartin Matuska  *
19716fd348SMartin Matuska  * CDDL HEADER END
20716fd348SMartin Matuska  */
21716fd348SMartin Matuska 
22716fd348SMartin Matuska /*
23716fd348SMartin Matuska  * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
24716fd348SMartin Matuska  * Copyright (c) 2013 Steven Hartland. All rights reserved.
25716fd348SMartin Matuska  */
26716fd348SMartin Matuska 
27716fd348SMartin Matuska /*
28716fd348SMartin Matuska  * zhack is a debugging tool that can write changes to ZFS pool using libzpool
29716fd348SMartin Matuska  * for testing purposes. Altering pools with zhack is unsupported and may
30716fd348SMartin Matuska  * result in corrupted pools.
31716fd348SMartin Matuska  */
32716fd348SMartin Matuska 
3315f0b8c3SMartin Matuska #include <zfs_prop.h>
34716fd348SMartin Matuska #include <stdio.h>
35716fd348SMartin Matuska #include <stdlib.h>
36716fd348SMartin Matuska #include <ctype.h>
37716fd348SMartin Matuska #include <sys/stat.h>
38716fd348SMartin Matuska #include <sys/zfs_context.h>
39716fd348SMartin Matuska #include <sys/spa.h>
40716fd348SMartin Matuska #include <sys/spa_impl.h>
41716fd348SMartin Matuska #include <sys/dmu.h>
42716fd348SMartin Matuska #include <sys/zap.h>
43716fd348SMartin Matuska #include <sys/zfs_znode.h>
44716fd348SMartin Matuska #include <sys/dsl_synctask.h>
45716fd348SMartin Matuska #include <sys/vdev.h>
46716fd348SMartin Matuska #include <sys/vdev_impl.h>
47716fd348SMartin Matuska #include <sys/fs/zfs.h>
48716fd348SMartin Matuska #include <sys/dmu_objset.h>
49716fd348SMartin Matuska #include <sys/dsl_pool.h>
50716fd348SMartin Matuska #include <sys/zio_checksum.h>
51716fd348SMartin Matuska #include <sys/zio_compress.h>
52716fd348SMartin Matuska #include <sys/zfeature.h>
53716fd348SMartin Matuska #include <sys/dmu_tx.h>
54716fd348SMartin Matuska #include <zfeature_common.h>
55716fd348SMartin Matuska #include <libzutil.h>
56716fd348SMartin Matuska 
57716fd348SMartin Matuska static importargs_t g_importargs;
58716fd348SMartin Matuska static char *g_pool;
59716fd348SMartin Matuska static boolean_t g_readonly;
60716fd348SMartin Matuska 
61e639e0d2SMartin Matuska typedef enum {
62e639e0d2SMartin Matuska 	ZHACK_REPAIR_OP_UNKNOWN  = 0,
63e639e0d2SMartin Matuska 	ZHACK_REPAIR_OP_CKSUM    = (1 << 0),
64e639e0d2SMartin Matuska 	ZHACK_REPAIR_OP_UNDETACH = (1 << 1)
65e639e0d2SMartin Matuska } zhack_repair_op_t;
66e639e0d2SMartin Matuska 
67716fd348SMartin Matuska static __attribute__((noreturn)) void
68716fd348SMartin Matuska usage(void)
69716fd348SMartin Matuska {
70716fd348SMartin Matuska 	(void) fprintf(stderr,
71716fd348SMartin Matuska 	    "Usage: zhack [-c cachefile] [-d dir] <subcommand> <args> ...\n"
72716fd348SMartin Matuska 	    "where <subcommand> <args> is one of the following:\n"
73716fd348SMartin Matuska 	    "\n");
74716fd348SMartin Matuska 
75716fd348SMartin Matuska 	(void) fprintf(stderr,
76716fd348SMartin Matuska 	    "    feature stat <pool>\n"
77716fd348SMartin Matuska 	    "        print information about enabled features\n"
78716fd348SMartin Matuska 	    "    feature enable [-r] [-d desc] <pool> <feature>\n"
79716fd348SMartin Matuska 	    "        add a new enabled feature to the pool\n"
80716fd348SMartin Matuska 	    "        -d <desc> sets the feature's description\n"
81716fd348SMartin Matuska 	    "        -r set read-only compatible flag for feature\n"
82716fd348SMartin Matuska 	    "    feature ref [-md] <pool> <feature>\n"
83716fd348SMartin Matuska 	    "        change the refcount on the given feature\n"
84716fd348SMartin Matuska 	    "        -d decrease instead of increase the refcount\n"
85716fd348SMartin Matuska 	    "        -m add the feature to the label if increasing refcount\n"
86716fd348SMartin Matuska 	    "\n"
87716fd348SMartin Matuska 	    "    <feature> : should be a feature guid\n"
88716fd348SMartin Matuska 	    "\n"
89716fd348SMartin Matuska 	    "    label repair <device>\n"
90e639e0d2SMartin Matuska 	    "        repair labels of a specified device according to options\n"
91e639e0d2SMartin Matuska 	    "        which may be combined to do their functions in one call\n"
92e639e0d2SMartin Matuska 	    "        -c repair corrupted label checksums\n"
93e639e0d2SMartin Matuska 	    "        -u restore the label on a detached device\n"
94716fd348SMartin Matuska 	    "\n"
95716fd348SMartin Matuska 	    "    <device> : path to vdev\n");
96716fd348SMartin Matuska 	exit(1);
97716fd348SMartin Matuska }
98716fd348SMartin Matuska 
99716fd348SMartin Matuska 
100716fd348SMartin Matuska static __attribute__((format(printf, 3, 4))) __attribute__((noreturn)) void
101a0b956f5SMartin Matuska fatal(spa_t *spa, const void *tag, const char *fmt, ...)
102716fd348SMartin Matuska {
103716fd348SMartin Matuska 	va_list ap;
104716fd348SMartin Matuska 
105716fd348SMartin Matuska 	if (spa != NULL) {
106716fd348SMartin Matuska 		spa_close(spa, tag);
107716fd348SMartin Matuska 		(void) spa_export(g_pool, NULL, B_TRUE, B_FALSE);
108716fd348SMartin Matuska 	}
109716fd348SMartin Matuska 
110716fd348SMartin Matuska 	va_start(ap, fmt);
111716fd348SMartin Matuska 	(void) fputs("zhack: ", stderr);
112716fd348SMartin Matuska 	(void) vfprintf(stderr, fmt, ap);
113716fd348SMartin Matuska 	va_end(ap);
114716fd348SMartin Matuska 	(void) fputc('\n', stderr);
115716fd348SMartin Matuska 
116716fd348SMartin Matuska 	exit(1);
117716fd348SMartin Matuska }
118716fd348SMartin Matuska 
119716fd348SMartin Matuska static int
120716fd348SMartin Matuska space_delta_cb(dmu_object_type_t bonustype, const void *data,
121716fd348SMartin Matuska     zfs_file_info_t *zoi)
122716fd348SMartin Matuska {
123716fd348SMartin Matuska 	(void) data, (void) zoi;
124716fd348SMartin Matuska 
125716fd348SMartin Matuska 	/*
126716fd348SMartin Matuska 	 * Is it a valid type of object to track?
127716fd348SMartin Matuska 	 */
128716fd348SMartin Matuska 	if (bonustype != DMU_OT_ZNODE && bonustype != DMU_OT_SA)
129716fd348SMartin Matuska 		return (ENOENT);
130716fd348SMartin Matuska 	(void) fprintf(stderr, "modifying object that needs user accounting");
131716fd348SMartin Matuska 	abort();
132716fd348SMartin Matuska }
133716fd348SMartin Matuska 
134716fd348SMartin Matuska /*
135716fd348SMartin Matuska  * Target is the dataset whose pool we want to open.
136716fd348SMartin Matuska  */
137716fd348SMartin Matuska static void
138716fd348SMartin Matuska zhack_import(char *target, boolean_t readonly)
139716fd348SMartin Matuska {
140716fd348SMartin Matuska 	nvlist_t *config;
141716fd348SMartin Matuska 	nvlist_t *props;
142716fd348SMartin Matuska 	int error;
143716fd348SMartin Matuska 
144716fd348SMartin Matuska 	kernel_init(readonly ? SPA_MODE_READ :
145716fd348SMartin Matuska 	    (SPA_MODE_READ | SPA_MODE_WRITE));
146716fd348SMartin Matuska 
147716fd348SMartin Matuska 	dmu_objset_register_type(DMU_OST_ZFS, space_delta_cb);
148716fd348SMartin Matuska 
149716fd348SMartin Matuska 	g_readonly = readonly;
150716fd348SMartin Matuska 	g_importargs.can_be_active = readonly;
151716fd348SMartin Matuska 	g_pool = strdup(target);
152716fd348SMartin Matuska 
153dbd5678dSMartin Matuska 	libpc_handle_t lpch = {
154dbd5678dSMartin Matuska 		.lpc_lib_handle = NULL,
155dbd5678dSMartin Matuska 		.lpc_ops = &libzpool_config_ops,
156dbd5678dSMartin Matuska 		.lpc_printerr = B_TRUE
157dbd5678dSMartin Matuska 	};
158dbd5678dSMartin Matuska 	error = zpool_find_config(&lpch, target, &config, &g_importargs);
159716fd348SMartin Matuska 	if (error)
160716fd348SMartin Matuska 		fatal(NULL, FTAG, "cannot import '%s'", target);
161716fd348SMartin Matuska 
162716fd348SMartin Matuska 	props = NULL;
163716fd348SMartin Matuska 	if (readonly) {
164716fd348SMartin Matuska 		VERIFY(nvlist_alloc(&props, NV_UNIQUE_NAME, 0) == 0);
165716fd348SMartin Matuska 		VERIFY(nvlist_add_uint64(props,
166716fd348SMartin Matuska 		    zpool_prop_to_name(ZPOOL_PROP_READONLY), 1) == 0);
167716fd348SMartin Matuska 	}
168716fd348SMartin Matuska 
169716fd348SMartin Matuska 	zfeature_checks_disable = B_TRUE;
170716fd348SMartin Matuska 	error = spa_import(target, config, props,
171716fd348SMartin Matuska 	    (readonly ?  ZFS_IMPORT_SKIP_MMP : ZFS_IMPORT_NORMAL));
172716fd348SMartin Matuska 	fnvlist_free(config);
173716fd348SMartin Matuska 	zfeature_checks_disable = B_FALSE;
174716fd348SMartin Matuska 	if (error == EEXIST)
175716fd348SMartin Matuska 		error = 0;
176716fd348SMartin Matuska 
177716fd348SMartin Matuska 	if (error)
178716fd348SMartin Matuska 		fatal(NULL, FTAG, "can't import '%s': %s", target,
179716fd348SMartin Matuska 		    strerror(error));
180716fd348SMartin Matuska }
181716fd348SMartin Matuska 
182716fd348SMartin Matuska static void
183a0b956f5SMartin Matuska zhack_spa_open(char *target, boolean_t readonly, const void *tag, spa_t **spa)
184716fd348SMartin Matuska {
185716fd348SMartin Matuska 	int err;
186716fd348SMartin Matuska 
187716fd348SMartin Matuska 	zhack_import(target, readonly);
188716fd348SMartin Matuska 
189716fd348SMartin Matuska 	zfeature_checks_disable = B_TRUE;
190716fd348SMartin Matuska 	err = spa_open(target, spa, tag);
191716fd348SMartin Matuska 	zfeature_checks_disable = B_FALSE;
192716fd348SMartin Matuska 
193716fd348SMartin Matuska 	if (err != 0)
194716fd348SMartin Matuska 		fatal(*spa, FTAG, "cannot open '%s': %s", target,
195716fd348SMartin Matuska 		    strerror(err));
196716fd348SMartin Matuska 	if (spa_version(*spa) < SPA_VERSION_FEATURES) {
197716fd348SMartin Matuska 		fatal(*spa, FTAG, "'%s' has version %d, features not enabled",
198716fd348SMartin Matuska 		    target, (int)spa_version(*spa));
199716fd348SMartin Matuska 	}
200716fd348SMartin Matuska }
201716fd348SMartin Matuska 
202716fd348SMartin Matuska static void
203716fd348SMartin Matuska dump_obj(objset_t *os, uint64_t obj, const char *name)
204716fd348SMartin Matuska {
205716fd348SMartin Matuska 	zap_cursor_t zc;
206716fd348SMartin Matuska 	zap_attribute_t za;
207716fd348SMartin Matuska 
208716fd348SMartin Matuska 	(void) printf("%s_obj:\n", name);
209716fd348SMartin Matuska 
210716fd348SMartin Matuska 	for (zap_cursor_init(&zc, os, obj);
211716fd348SMartin Matuska 	    zap_cursor_retrieve(&zc, &za) == 0;
212716fd348SMartin Matuska 	    zap_cursor_advance(&zc)) {
213716fd348SMartin Matuska 		if (za.za_integer_length == 8) {
214716fd348SMartin Matuska 			ASSERT(za.za_num_integers == 1);
215716fd348SMartin Matuska 			(void) printf("\t%s = %llu\n",
216716fd348SMartin Matuska 			    za.za_name, (u_longlong_t)za.za_first_integer);
217716fd348SMartin Matuska 		} else {
218716fd348SMartin Matuska 			ASSERT(za.za_integer_length == 1);
219716fd348SMartin Matuska 			char val[1024];
220716fd348SMartin Matuska 			VERIFY(zap_lookup(os, obj, za.za_name,
221716fd348SMartin Matuska 			    1, sizeof (val), val) == 0);
222716fd348SMartin Matuska 			(void) printf("\t%s = %s\n", za.za_name, val);
223716fd348SMartin Matuska 		}
224716fd348SMartin Matuska 	}
225716fd348SMartin Matuska 	zap_cursor_fini(&zc);
226716fd348SMartin Matuska }
227716fd348SMartin Matuska 
228716fd348SMartin Matuska static void
229716fd348SMartin Matuska dump_mos(spa_t *spa)
230716fd348SMartin Matuska {
231716fd348SMartin Matuska 	nvlist_t *nv = spa->spa_label_features;
232716fd348SMartin Matuska 	nvpair_t *pair;
233716fd348SMartin Matuska 
234716fd348SMartin Matuska 	(void) printf("label config:\n");
235716fd348SMartin Matuska 	for (pair = nvlist_next_nvpair(nv, NULL);
236716fd348SMartin Matuska 	    pair != NULL;
237716fd348SMartin Matuska 	    pair = nvlist_next_nvpair(nv, pair)) {
238716fd348SMartin Matuska 		(void) printf("\t%s\n", nvpair_name(pair));
239716fd348SMartin Matuska 	}
240716fd348SMartin Matuska }
241716fd348SMartin Matuska 
242716fd348SMartin Matuska static void
243716fd348SMartin Matuska zhack_do_feature_stat(int argc, char **argv)
244716fd348SMartin Matuska {
245716fd348SMartin Matuska 	spa_t *spa;
246716fd348SMartin Matuska 	objset_t *os;
247716fd348SMartin Matuska 	char *target;
248716fd348SMartin Matuska 
249716fd348SMartin Matuska 	argc--;
250716fd348SMartin Matuska 	argv++;
251716fd348SMartin Matuska 
252716fd348SMartin Matuska 	if (argc < 1) {
253716fd348SMartin Matuska 		(void) fprintf(stderr, "error: missing pool name\n");
254716fd348SMartin Matuska 		usage();
255716fd348SMartin Matuska 	}
256716fd348SMartin Matuska 	target = argv[0];
257716fd348SMartin Matuska 
258716fd348SMartin Matuska 	zhack_spa_open(target, B_TRUE, FTAG, &spa);
259716fd348SMartin Matuska 	os = spa->spa_meta_objset;
260716fd348SMartin Matuska 
261716fd348SMartin Matuska 	dump_obj(os, spa->spa_feat_for_read_obj, "for_read");
262716fd348SMartin Matuska 	dump_obj(os, spa->spa_feat_for_write_obj, "for_write");
263716fd348SMartin Matuska 	dump_obj(os, spa->spa_feat_desc_obj, "descriptions");
264716fd348SMartin Matuska 	if (spa_feature_is_active(spa, SPA_FEATURE_ENABLED_TXG)) {
265716fd348SMartin Matuska 		dump_obj(os, spa->spa_feat_enabled_txg_obj, "enabled_txg");
266716fd348SMartin Matuska 	}
267716fd348SMartin Matuska 	dump_mos(spa);
268716fd348SMartin Matuska 
269716fd348SMartin Matuska 	spa_close(spa, FTAG);
270716fd348SMartin Matuska }
271716fd348SMartin Matuska 
272716fd348SMartin Matuska static void
273716fd348SMartin Matuska zhack_feature_enable_sync(void *arg, dmu_tx_t *tx)
274716fd348SMartin Matuska {
275716fd348SMartin Matuska 	spa_t *spa = dmu_tx_pool(tx)->dp_spa;
276716fd348SMartin Matuska 	zfeature_info_t *feature = arg;
277716fd348SMartin Matuska 
278716fd348SMartin Matuska 	feature_enable_sync(spa, feature, tx);
279716fd348SMartin Matuska 
280716fd348SMartin Matuska 	spa_history_log_internal(spa, "zhack enable feature", tx,
281716fd348SMartin Matuska 	    "name=%s flags=%u",
282716fd348SMartin Matuska 	    feature->fi_guid, feature->fi_flags);
283716fd348SMartin Matuska }
284716fd348SMartin Matuska 
285716fd348SMartin Matuska static void
286716fd348SMartin Matuska zhack_do_feature_enable(int argc, char **argv)
287716fd348SMartin Matuska {
288716fd348SMartin Matuska 	int c;
289716fd348SMartin Matuska 	char *desc, *target;
290716fd348SMartin Matuska 	spa_t *spa;
291716fd348SMartin Matuska 	objset_t *mos;
292716fd348SMartin Matuska 	zfeature_info_t feature;
293716fd348SMartin Matuska 	const spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
294716fd348SMartin Matuska 
295716fd348SMartin Matuska 	/*
296716fd348SMartin Matuska 	 * Features are not added to the pool's label until their refcounts
297716fd348SMartin Matuska 	 * are incremented, so fi_mos can just be left as false for now.
298716fd348SMartin Matuska 	 */
299716fd348SMartin Matuska 	desc = NULL;
300716fd348SMartin Matuska 	feature.fi_uname = "zhack";
301716fd348SMartin Matuska 	feature.fi_flags = 0;
302716fd348SMartin Matuska 	feature.fi_depends = nodeps;
303716fd348SMartin Matuska 	feature.fi_feature = SPA_FEATURE_NONE;
304716fd348SMartin Matuska 
305716fd348SMartin Matuska 	optind = 1;
306716fd348SMartin Matuska 	while ((c = getopt(argc, argv, "+rd:")) != -1) {
307716fd348SMartin Matuska 		switch (c) {
308716fd348SMartin Matuska 		case 'r':
309716fd348SMartin Matuska 			feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT;
310716fd348SMartin Matuska 			break;
311716fd348SMartin Matuska 		case 'd':
312be181ee2SMartin Matuska 			if (desc != NULL)
313be181ee2SMartin Matuska 				free(desc);
314716fd348SMartin Matuska 			desc = strdup(optarg);
315716fd348SMartin Matuska 			break;
316716fd348SMartin Matuska 		default:
317716fd348SMartin Matuska 			usage();
318716fd348SMartin Matuska 			break;
319716fd348SMartin Matuska 		}
320716fd348SMartin Matuska 	}
321716fd348SMartin Matuska 
322716fd348SMartin Matuska 	if (desc == NULL)
323716fd348SMartin Matuska 		desc = strdup("zhack injected");
324716fd348SMartin Matuska 	feature.fi_desc = desc;
325716fd348SMartin Matuska 
326716fd348SMartin Matuska 	argc -= optind;
327716fd348SMartin Matuska 	argv += optind;
328716fd348SMartin Matuska 
329716fd348SMartin Matuska 	if (argc < 2) {
330716fd348SMartin Matuska 		(void) fprintf(stderr, "error: missing feature or pool name\n");
331716fd348SMartin Matuska 		usage();
332716fd348SMartin Matuska 	}
333716fd348SMartin Matuska 	target = argv[0];
334716fd348SMartin Matuska 	feature.fi_guid = argv[1];
335716fd348SMartin Matuska 
336716fd348SMartin Matuska 	if (!zfeature_is_valid_guid(feature.fi_guid))
337716fd348SMartin Matuska 		fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid);
338716fd348SMartin Matuska 
339716fd348SMartin Matuska 	zhack_spa_open(target, B_FALSE, FTAG, &spa);
340716fd348SMartin Matuska 	mos = spa->spa_meta_objset;
341716fd348SMartin Matuska 
342716fd348SMartin Matuska 	if (zfeature_is_supported(feature.fi_guid))
343716fd348SMartin Matuska 		fatal(spa, FTAG, "'%s' is a real feature, will not enable",
344716fd348SMartin Matuska 		    feature.fi_guid);
345716fd348SMartin Matuska 	if (0 == zap_contains(mos, spa->spa_feat_desc_obj, feature.fi_guid))
346716fd348SMartin Matuska 		fatal(spa, FTAG, "feature already enabled: %s",
347716fd348SMartin Matuska 		    feature.fi_guid);
348716fd348SMartin Matuska 
349716fd348SMartin Matuska 	VERIFY0(dsl_sync_task(spa_name(spa), NULL,
350716fd348SMartin Matuska 	    zhack_feature_enable_sync, &feature, 5, ZFS_SPACE_CHECK_NORMAL));
351716fd348SMartin Matuska 
352716fd348SMartin Matuska 	spa_close(spa, FTAG);
353716fd348SMartin Matuska 
354716fd348SMartin Matuska 	free(desc);
355716fd348SMartin Matuska }
356716fd348SMartin Matuska 
357716fd348SMartin Matuska static void
358716fd348SMartin Matuska feature_incr_sync(void *arg, dmu_tx_t *tx)
359716fd348SMartin Matuska {
360716fd348SMartin Matuska 	spa_t *spa = dmu_tx_pool(tx)->dp_spa;
361716fd348SMartin Matuska 	zfeature_info_t *feature = arg;
362716fd348SMartin Matuska 	uint64_t refcount;
363716fd348SMartin Matuska 
364716fd348SMartin Matuska 	VERIFY0(feature_get_refcount_from_disk(spa, feature, &refcount));
365716fd348SMartin Matuska 	feature_sync(spa, feature, refcount + 1, tx);
366716fd348SMartin Matuska 	spa_history_log_internal(spa, "zhack feature incr", tx,
367716fd348SMartin Matuska 	    "name=%s", feature->fi_guid);
368716fd348SMartin Matuska }
369716fd348SMartin Matuska 
370716fd348SMartin Matuska static void
371716fd348SMartin Matuska feature_decr_sync(void *arg, dmu_tx_t *tx)
372716fd348SMartin Matuska {
373716fd348SMartin Matuska 	spa_t *spa = dmu_tx_pool(tx)->dp_spa;
374716fd348SMartin Matuska 	zfeature_info_t *feature = arg;
375716fd348SMartin Matuska 	uint64_t refcount;
376716fd348SMartin Matuska 
377716fd348SMartin Matuska 	VERIFY0(feature_get_refcount_from_disk(spa, feature, &refcount));
378716fd348SMartin Matuska 	feature_sync(spa, feature, refcount - 1, tx);
379716fd348SMartin Matuska 	spa_history_log_internal(spa, "zhack feature decr", tx,
380716fd348SMartin Matuska 	    "name=%s", feature->fi_guid);
381716fd348SMartin Matuska }
382716fd348SMartin Matuska 
383716fd348SMartin Matuska static void
384716fd348SMartin Matuska zhack_do_feature_ref(int argc, char **argv)
385716fd348SMartin Matuska {
386716fd348SMartin Matuska 	int c;
387716fd348SMartin Matuska 	char *target;
388716fd348SMartin Matuska 	boolean_t decr = B_FALSE;
389716fd348SMartin Matuska 	spa_t *spa;
390716fd348SMartin Matuska 	objset_t *mos;
391716fd348SMartin Matuska 	zfeature_info_t feature;
392716fd348SMartin Matuska 	const spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
393716fd348SMartin Matuska 
394716fd348SMartin Matuska 	/*
395716fd348SMartin Matuska 	 * fi_desc does not matter here because it was written to disk
396716fd348SMartin Matuska 	 * when the feature was enabled, but we need to properly set the
397716fd348SMartin Matuska 	 * feature for read or write based on the information we read off
398716fd348SMartin Matuska 	 * disk later.
399716fd348SMartin Matuska 	 */
400716fd348SMartin Matuska 	feature.fi_uname = "zhack";
401716fd348SMartin Matuska 	feature.fi_flags = 0;
402716fd348SMartin Matuska 	feature.fi_desc = NULL;
403716fd348SMartin Matuska 	feature.fi_depends = nodeps;
404716fd348SMartin Matuska 	feature.fi_feature = SPA_FEATURE_NONE;
405716fd348SMartin Matuska 
406716fd348SMartin Matuska 	optind = 1;
407716fd348SMartin Matuska 	while ((c = getopt(argc, argv, "+md")) != -1) {
408716fd348SMartin Matuska 		switch (c) {
409716fd348SMartin Matuska 		case 'm':
410716fd348SMartin Matuska 			feature.fi_flags |= ZFEATURE_FLAG_MOS;
411716fd348SMartin Matuska 			break;
412716fd348SMartin Matuska 		case 'd':
413716fd348SMartin Matuska 			decr = B_TRUE;
414716fd348SMartin Matuska 			break;
415716fd348SMartin Matuska 		default:
416716fd348SMartin Matuska 			usage();
417716fd348SMartin Matuska 			break;
418716fd348SMartin Matuska 		}
419716fd348SMartin Matuska 	}
420716fd348SMartin Matuska 	argc -= optind;
421716fd348SMartin Matuska 	argv += optind;
422716fd348SMartin Matuska 
423716fd348SMartin Matuska 	if (argc < 2) {
424716fd348SMartin Matuska 		(void) fprintf(stderr, "error: missing feature or pool name\n");
425716fd348SMartin Matuska 		usage();
426716fd348SMartin Matuska 	}
427716fd348SMartin Matuska 	target = argv[0];
428716fd348SMartin Matuska 	feature.fi_guid = argv[1];
429716fd348SMartin Matuska 
430716fd348SMartin Matuska 	if (!zfeature_is_valid_guid(feature.fi_guid))
431716fd348SMartin Matuska 		fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid);
432716fd348SMartin Matuska 
433716fd348SMartin Matuska 	zhack_spa_open(target, B_FALSE, FTAG, &spa);
434716fd348SMartin Matuska 	mos = spa->spa_meta_objset;
435716fd348SMartin Matuska 
436716fd348SMartin Matuska 	if (zfeature_is_supported(feature.fi_guid)) {
437716fd348SMartin Matuska 		fatal(spa, FTAG,
438716fd348SMartin Matuska 		    "'%s' is a real feature, will not change refcount",
439716fd348SMartin Matuska 		    feature.fi_guid);
440716fd348SMartin Matuska 	}
441716fd348SMartin Matuska 
442716fd348SMartin Matuska 	if (0 == zap_contains(mos, spa->spa_feat_for_read_obj,
443716fd348SMartin Matuska 	    feature.fi_guid)) {
444716fd348SMartin Matuska 		feature.fi_flags &= ~ZFEATURE_FLAG_READONLY_COMPAT;
445716fd348SMartin Matuska 	} else if (0 == zap_contains(mos, spa->spa_feat_for_write_obj,
446716fd348SMartin Matuska 	    feature.fi_guid)) {
447716fd348SMartin Matuska 		feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT;
448716fd348SMartin Matuska 	} else {
449716fd348SMartin Matuska 		fatal(spa, FTAG, "feature is not enabled: %s", feature.fi_guid);
450716fd348SMartin Matuska 	}
451716fd348SMartin Matuska 
452716fd348SMartin Matuska 	if (decr) {
453716fd348SMartin Matuska 		uint64_t count;
454716fd348SMartin Matuska 		if (feature_get_refcount_from_disk(spa, &feature,
455716fd348SMartin Matuska 		    &count) == 0 && count == 0) {
456716fd348SMartin Matuska 			fatal(spa, FTAG, "feature refcount already 0: %s",
457716fd348SMartin Matuska 			    feature.fi_guid);
458716fd348SMartin Matuska 		}
459716fd348SMartin Matuska 	}
460716fd348SMartin Matuska 
461716fd348SMartin Matuska 	VERIFY0(dsl_sync_task(spa_name(spa), NULL,
462716fd348SMartin Matuska 	    decr ? feature_decr_sync : feature_incr_sync, &feature,
463716fd348SMartin Matuska 	    5, ZFS_SPACE_CHECK_NORMAL));
464716fd348SMartin Matuska 
465716fd348SMartin Matuska 	spa_close(spa, FTAG);
466716fd348SMartin Matuska }
467716fd348SMartin Matuska 
468716fd348SMartin Matuska static int
469716fd348SMartin Matuska zhack_do_feature(int argc, char **argv)
470716fd348SMartin Matuska {
471716fd348SMartin Matuska 	char *subcommand;
472716fd348SMartin Matuska 
473716fd348SMartin Matuska 	argc--;
474716fd348SMartin Matuska 	argv++;
475716fd348SMartin Matuska 	if (argc == 0) {
476716fd348SMartin Matuska 		(void) fprintf(stderr,
477716fd348SMartin Matuska 		    "error: no feature operation specified\n");
478716fd348SMartin Matuska 		usage();
479716fd348SMartin Matuska 	}
480716fd348SMartin Matuska 
481716fd348SMartin Matuska 	subcommand = argv[0];
482716fd348SMartin Matuska 	if (strcmp(subcommand, "stat") == 0) {
483716fd348SMartin Matuska 		zhack_do_feature_stat(argc, argv);
484716fd348SMartin Matuska 	} else if (strcmp(subcommand, "enable") == 0) {
485716fd348SMartin Matuska 		zhack_do_feature_enable(argc, argv);
486716fd348SMartin Matuska 	} else if (strcmp(subcommand, "ref") == 0) {
487716fd348SMartin Matuska 		zhack_do_feature_ref(argc, argv);
488716fd348SMartin Matuska 	} else {
489716fd348SMartin Matuska 		(void) fprintf(stderr, "error: unknown subcommand: %s\n",
490716fd348SMartin Matuska 		    subcommand);
491716fd348SMartin Matuska 		usage();
492716fd348SMartin Matuska 	}
493716fd348SMartin Matuska 
494716fd348SMartin Matuska 	return (0);
495716fd348SMartin Matuska }
496716fd348SMartin Matuska 
497e639e0d2SMartin Matuska #define	ASHIFT_UBERBLOCK_SHIFT(ashift)	\
498e639e0d2SMartin Matuska 	MIN(MAX(ashift, UBERBLOCK_SHIFT), \
499e639e0d2SMartin Matuska 	MAX_UBERBLOCK_SHIFT)
500e639e0d2SMartin Matuska #define	ASHIFT_UBERBLOCK_SIZE(ashift) \
501e639e0d2SMartin Matuska 	(1ULL << ASHIFT_UBERBLOCK_SHIFT(ashift))
502e639e0d2SMartin Matuska 
503e639e0d2SMartin Matuska #define	REPAIR_LABEL_STATUS_CKSUM (1 << 0)
504e639e0d2SMartin Matuska #define	REPAIR_LABEL_STATUS_UB    (1 << 1)
505e639e0d2SMartin Matuska 
506716fd348SMartin Matuska static int
507e639e0d2SMartin Matuska zhack_repair_read_label(const int fd, vdev_label_t *vl,
508e639e0d2SMartin Matuska     const uint64_t label_offset, const int l)
509716fd348SMartin Matuska {
510e639e0d2SMartin Matuska 	const int err = pread64(fd, vl, sizeof (vdev_label_t), label_offset);
511e639e0d2SMartin Matuska 
512e639e0d2SMartin Matuska 	if (err == -1) {
513e639e0d2SMartin Matuska 		(void) fprintf(stderr,
514e639e0d2SMartin Matuska 		    "error: cannot read label %d: %s\n",
515e639e0d2SMartin Matuska 		    l, strerror(errno));
516e639e0d2SMartin Matuska 		return (err);
517e639e0d2SMartin Matuska 	} else if (err != sizeof (vdev_label_t)) {
518e639e0d2SMartin Matuska 		(void) fprintf(stderr,
519e639e0d2SMartin Matuska 		    "error: bad label %d read size\n", l);
520e639e0d2SMartin Matuska 		return (err);
521e639e0d2SMartin Matuska 	}
522e639e0d2SMartin Matuska 
523e639e0d2SMartin Matuska 	return (0);
524e639e0d2SMartin Matuska }
525e639e0d2SMartin Matuska 
526e639e0d2SMartin Matuska static void
527e639e0d2SMartin Matuska zhack_repair_calc_cksum(const int byteswap, void *data, const uint64_t offset,
528e639e0d2SMartin Matuska     const uint64_t abdsize, zio_eck_t *eck, zio_cksum_t *cksum)
529e639e0d2SMartin Matuska {
530e639e0d2SMartin Matuska 	zio_cksum_t verifier;
531e639e0d2SMartin Matuska 	zio_cksum_t current_cksum;
532e639e0d2SMartin Matuska 	zio_checksum_info_t *ci;
533e639e0d2SMartin Matuska 	abd_t *abd;
534e639e0d2SMartin Matuska 
535e639e0d2SMartin Matuska 	ZIO_SET_CHECKSUM(&verifier, offset, 0, 0, 0);
536e639e0d2SMartin Matuska 
537e639e0d2SMartin Matuska 	if (byteswap)
538e639e0d2SMartin Matuska 		byteswap_uint64_array(&verifier, sizeof (zio_cksum_t));
539e639e0d2SMartin Matuska 
540e639e0d2SMartin Matuska 	current_cksum = eck->zec_cksum;
541e639e0d2SMartin Matuska 	eck->zec_cksum = verifier;
542e639e0d2SMartin Matuska 
543e639e0d2SMartin Matuska 	ci = &zio_checksum_table[ZIO_CHECKSUM_LABEL];
544e639e0d2SMartin Matuska 	abd = abd_get_from_buf(data, abdsize);
545e639e0d2SMartin Matuska 	ci->ci_func[byteswap](abd, abdsize, NULL, cksum);
546e639e0d2SMartin Matuska 	abd_free(abd);
547e639e0d2SMartin Matuska 
548e639e0d2SMartin Matuska 	eck->zec_cksum = current_cksum;
549e639e0d2SMartin Matuska }
550e639e0d2SMartin Matuska 
551e639e0d2SMartin Matuska static int
552e639e0d2SMartin Matuska zhack_repair_check_label(uberblock_t *ub, const int l, const char **cfg_keys,
553e639e0d2SMartin Matuska     const size_t cfg_keys_len, nvlist_t *cfg, nvlist_t *vdev_tree_cfg,
554e639e0d2SMartin Matuska     uint64_t *ashift)
555e639e0d2SMartin Matuska {
556e639e0d2SMartin Matuska 	int err;
557e639e0d2SMartin Matuska 
558e639e0d2SMartin Matuska 	if (ub->ub_txg != 0) {
559e639e0d2SMartin Matuska 		(void) fprintf(stderr,
560e639e0d2SMartin Matuska 		    "error: label %d: UB TXG of 0 expected, but got %"
561e639e0d2SMartin Matuska 		    PRIu64 "\n",
562e639e0d2SMartin Matuska 		    l, ub->ub_txg);
563e639e0d2SMartin Matuska 		(void) fprintf(stderr, "It would appear the device was not "
564e639e0d2SMartin Matuska 		    "properly removed.\n");
565e639e0d2SMartin Matuska 		return (1);
566e639e0d2SMartin Matuska 	}
567e639e0d2SMartin Matuska 
568e639e0d2SMartin Matuska 	for (int i = 0; i < cfg_keys_len; i++) {
569e639e0d2SMartin Matuska 		uint64_t val;
570e639e0d2SMartin Matuska 		err = nvlist_lookup_uint64(cfg, cfg_keys[i], &val);
571e639e0d2SMartin Matuska 		if (err) {
572e639e0d2SMartin Matuska 			(void) fprintf(stderr,
573e639e0d2SMartin Matuska 			    "error: label %d, %d: "
574e639e0d2SMartin Matuska 			    "cannot find nvlist key %s\n",
575e639e0d2SMartin Matuska 			    l, i, cfg_keys[i]);
576e639e0d2SMartin Matuska 			return (err);
577e639e0d2SMartin Matuska 		}
578e639e0d2SMartin Matuska 	}
579e639e0d2SMartin Matuska 
580e639e0d2SMartin Matuska 	err = nvlist_lookup_nvlist(cfg,
581e639e0d2SMartin Matuska 	    ZPOOL_CONFIG_VDEV_TREE, &vdev_tree_cfg);
582e639e0d2SMartin Matuska 	if (err) {
583e639e0d2SMartin Matuska 		(void) fprintf(stderr,
584e639e0d2SMartin Matuska 		    "error: label %d: cannot find nvlist key %s\n",
585e639e0d2SMartin Matuska 		    l, ZPOOL_CONFIG_VDEV_TREE);
586e639e0d2SMartin Matuska 		return (err);
587e639e0d2SMartin Matuska 	}
588e639e0d2SMartin Matuska 
589e639e0d2SMartin Matuska 	err = nvlist_lookup_uint64(vdev_tree_cfg,
590e639e0d2SMartin Matuska 	    ZPOOL_CONFIG_ASHIFT, ashift);
591e639e0d2SMartin Matuska 	if (err) {
592e639e0d2SMartin Matuska 		(void) fprintf(stderr,
593e639e0d2SMartin Matuska 		    "error: label %d: cannot find nvlist key %s\n",
594e639e0d2SMartin Matuska 		    l, ZPOOL_CONFIG_ASHIFT);
595e639e0d2SMartin Matuska 		return (err);
596e639e0d2SMartin Matuska 	}
597e639e0d2SMartin Matuska 
598e639e0d2SMartin Matuska 	if (*ashift == 0) {
599e639e0d2SMartin Matuska 		(void) fprintf(stderr,
600e639e0d2SMartin Matuska 		    "error: label %d: nvlist key %s is zero\n",
601e639e0d2SMartin Matuska 		    l, ZPOOL_CONFIG_ASHIFT);
602e639e0d2SMartin Matuska 		return (err);
603e639e0d2SMartin Matuska 	}
604e639e0d2SMartin Matuska 
605e639e0d2SMartin Matuska 	return (0);
606e639e0d2SMartin Matuska }
607e639e0d2SMartin Matuska 
608e639e0d2SMartin Matuska static int
609e639e0d2SMartin Matuska zhack_repair_undetach(uberblock_t *ub, nvlist_t *cfg, const int l)
610e639e0d2SMartin Matuska {
611e639e0d2SMartin Matuska 	/*
612e639e0d2SMartin Matuska 	 * Uberblock root block pointer has valid birth TXG.
613e639e0d2SMartin Matuska 	 * Copying it to the label NVlist
614e639e0d2SMartin Matuska 	 */
615*783d3ff6SMartin Matuska 	if (BP_GET_LOGICAL_BIRTH(&ub->ub_rootbp) != 0) {
616*783d3ff6SMartin Matuska 		const uint64_t txg = BP_GET_LOGICAL_BIRTH(&ub->ub_rootbp);
617e639e0d2SMartin Matuska 		ub->ub_txg = txg;
618e639e0d2SMartin Matuska 
619e639e0d2SMartin Matuska 		if (nvlist_remove_all(cfg, ZPOOL_CONFIG_CREATE_TXG) != 0) {
620e639e0d2SMartin Matuska 			(void) fprintf(stderr,
621e639e0d2SMartin Matuska 			    "error: label %d: "
622e639e0d2SMartin Matuska 			    "Failed to remove pool creation TXG\n",
623e639e0d2SMartin Matuska 			    l);
624e639e0d2SMartin Matuska 			return (1);
625e639e0d2SMartin Matuska 		}
626e639e0d2SMartin Matuska 
627e639e0d2SMartin Matuska 		if (nvlist_remove_all(cfg, ZPOOL_CONFIG_POOL_TXG) != 0) {
628e639e0d2SMartin Matuska 			(void) fprintf(stderr,
629e639e0d2SMartin Matuska 			    "error: label %d: Failed to remove pool TXG to "
630e639e0d2SMartin Matuska 			    "be replaced.\n",
631e639e0d2SMartin Matuska 			    l);
632e639e0d2SMartin Matuska 			return (1);
633e639e0d2SMartin Matuska 		}
634e639e0d2SMartin Matuska 
635e639e0d2SMartin Matuska 		if (nvlist_add_uint64(cfg, ZPOOL_CONFIG_POOL_TXG, txg) != 0) {
636e639e0d2SMartin Matuska 			(void) fprintf(stderr,
637e639e0d2SMartin Matuska 			    "error: label %d: "
638e639e0d2SMartin Matuska 			    "Failed to add pool TXG of %" PRIu64 "\n",
639e639e0d2SMartin Matuska 			    l, txg);
640e639e0d2SMartin Matuska 			return (1);
641e639e0d2SMartin Matuska 		}
642e639e0d2SMartin Matuska 	}
643e639e0d2SMartin Matuska 
644e639e0d2SMartin Matuska 	return (0);
645e639e0d2SMartin Matuska }
646e639e0d2SMartin Matuska 
647e639e0d2SMartin Matuska static boolean_t
648e639e0d2SMartin Matuska zhack_repair_write_label(const int l, const int fd, const int byteswap,
649e639e0d2SMartin Matuska     void *data, zio_eck_t *eck, const uint64_t offset, const uint64_t abdsize)
650e639e0d2SMartin Matuska {
651e639e0d2SMartin Matuska 	zio_cksum_t actual_cksum;
652e639e0d2SMartin Matuska 	zhack_repair_calc_cksum(byteswap, data, offset, abdsize, eck,
653e639e0d2SMartin Matuska 	    &actual_cksum);
654e639e0d2SMartin Matuska 	zio_cksum_t expected_cksum = eck->zec_cksum;
655e639e0d2SMartin Matuska 	ssize_t err;
656e639e0d2SMartin Matuska 
657e639e0d2SMartin Matuska 	if (ZIO_CHECKSUM_EQUAL(actual_cksum, expected_cksum))
658e639e0d2SMartin Matuska 		return (B_FALSE);
659e639e0d2SMartin Matuska 
660e639e0d2SMartin Matuska 	eck->zec_cksum = actual_cksum;
661e639e0d2SMartin Matuska 
662e639e0d2SMartin Matuska 	err = pwrite64(fd, data, abdsize, offset);
663e639e0d2SMartin Matuska 	if (err == -1) {
664e639e0d2SMartin Matuska 		(void) fprintf(stderr, "error: cannot write label %d: %s\n",
665e639e0d2SMartin Matuska 		    l, strerror(errno));
666e639e0d2SMartin Matuska 		return (B_FALSE);
667e639e0d2SMartin Matuska 	} else if (err != abdsize) {
668e639e0d2SMartin Matuska 		(void) fprintf(stderr, "error: bad write size label %d\n", l);
669e639e0d2SMartin Matuska 		return (B_FALSE);
670e639e0d2SMartin Matuska 	} else {
671e639e0d2SMartin Matuska 		(void) fprintf(stderr,
672e639e0d2SMartin Matuska 		    "label %d: wrote %" PRIu64 " bytes at offset %" PRIu64 "\n",
673e639e0d2SMartin Matuska 		    l, abdsize, offset);
674e639e0d2SMartin Matuska 	}
675e639e0d2SMartin Matuska 
676e639e0d2SMartin Matuska 	return (B_TRUE);
677e639e0d2SMartin Matuska }
678e639e0d2SMartin Matuska 
679e639e0d2SMartin Matuska static void
680e639e0d2SMartin Matuska zhack_repair_write_uberblock(vdev_label_t *vl, const int l,
681e639e0d2SMartin Matuska     const uint64_t ashift, const int fd, const int byteswap,
682e639e0d2SMartin Matuska     const uint64_t label_offset, uint32_t *labels_repaired)
683e639e0d2SMartin Matuska {
684e639e0d2SMartin Matuska 	void *ub_data =
685e639e0d2SMartin Matuska 	    (char *)vl + offsetof(vdev_label_t, vl_uberblock);
686e639e0d2SMartin Matuska 	zio_eck_t *ub_eck =
687e639e0d2SMartin Matuska 	    (zio_eck_t *)
688e639e0d2SMartin Matuska 	    ((char *)(ub_data) + (ASHIFT_UBERBLOCK_SIZE(ashift))) - 1;
689e639e0d2SMartin Matuska 
690e639e0d2SMartin Matuska 	if (ub_eck->zec_magic != 0) {
691e639e0d2SMartin Matuska 		(void) fprintf(stderr,
692e639e0d2SMartin Matuska 		    "error: label %d: "
693e639e0d2SMartin Matuska 		    "Expected Uberblock checksum magic number to "
694e639e0d2SMartin Matuska 		    "be 0, but got %" PRIu64 "\n",
695e639e0d2SMartin Matuska 		    l, ub_eck->zec_magic);
696e639e0d2SMartin Matuska 		(void) fprintf(stderr, "It would appear there's already "
697e639e0d2SMartin Matuska 		    "a checksum for the uberblock.\n");
698e639e0d2SMartin Matuska 		return;
699e639e0d2SMartin Matuska 	}
700e639e0d2SMartin Matuska 
701e639e0d2SMartin Matuska 
702e639e0d2SMartin Matuska 	ub_eck->zec_magic = byteswap ? BSWAP_64(ZEC_MAGIC) : ZEC_MAGIC;
703e639e0d2SMartin Matuska 
704e639e0d2SMartin Matuska 	if (zhack_repair_write_label(l, fd, byteswap,
705e639e0d2SMartin Matuska 	    ub_data, ub_eck,
706e639e0d2SMartin Matuska 	    label_offset + offsetof(vdev_label_t, vl_uberblock),
707e639e0d2SMartin Matuska 	    ASHIFT_UBERBLOCK_SIZE(ashift)))
708e639e0d2SMartin Matuska 			labels_repaired[l] |= REPAIR_LABEL_STATUS_UB;
709e639e0d2SMartin Matuska }
710e639e0d2SMartin Matuska 
711e639e0d2SMartin Matuska static void
712e639e0d2SMartin Matuska zhack_repair_print_cksum(FILE *stream, const zio_cksum_t *cksum)
713e639e0d2SMartin Matuska {
714e639e0d2SMartin Matuska 	(void) fprintf(stream,
715e639e0d2SMartin Matuska 	    "%016llx:%016llx:%016llx:%016llx",
716e639e0d2SMartin Matuska 	    (u_longlong_t)cksum->zc_word[0],
717e639e0d2SMartin Matuska 	    (u_longlong_t)cksum->zc_word[1],
718e639e0d2SMartin Matuska 	    (u_longlong_t)cksum->zc_word[2],
719e639e0d2SMartin Matuska 	    (u_longlong_t)cksum->zc_word[3]);
720e639e0d2SMartin Matuska }
721e639e0d2SMartin Matuska 
722e639e0d2SMartin Matuska static int
723e639e0d2SMartin Matuska zhack_repair_test_cksum(const int byteswap, void *vdev_data,
724e639e0d2SMartin Matuska     zio_eck_t *vdev_eck, const uint64_t vdev_phys_offset, const int l)
725e639e0d2SMartin Matuska {
726e639e0d2SMartin Matuska 	const zio_cksum_t expected_cksum = vdev_eck->zec_cksum;
727e639e0d2SMartin Matuska 	zio_cksum_t actual_cksum;
728e639e0d2SMartin Matuska 	zhack_repair_calc_cksum(byteswap, vdev_data, vdev_phys_offset,
729e639e0d2SMartin Matuska 	    VDEV_PHYS_SIZE, vdev_eck, &actual_cksum);
730e639e0d2SMartin Matuska 	const uint64_t expected_magic = byteswap ?
731e639e0d2SMartin Matuska 	    BSWAP_64(ZEC_MAGIC) : ZEC_MAGIC;
732e639e0d2SMartin Matuska 	const uint64_t actual_magic = vdev_eck->zec_magic;
733e639e0d2SMartin Matuska 	int err = 0;
734e639e0d2SMartin Matuska 	if (actual_magic != expected_magic) {
735e639e0d2SMartin Matuska 		(void) fprintf(stderr, "error: label %d: "
736e639e0d2SMartin Matuska 		    "Expected "
737e639e0d2SMartin Matuska 		    "the nvlist checksum magic number to not be %"
738e639e0d2SMartin Matuska 		    PRIu64 " not %" PRIu64 "\n",
739e639e0d2SMartin Matuska 		    l, expected_magic, actual_magic);
740e639e0d2SMartin Matuska 		err = ECKSUM;
741e639e0d2SMartin Matuska 	}
742e639e0d2SMartin Matuska 	if (!ZIO_CHECKSUM_EQUAL(actual_cksum, expected_cksum)) {
743e639e0d2SMartin Matuska 		(void) fprintf(stderr, "error: label %d: "
744e639e0d2SMartin Matuska 		    "Expected the nvlist checksum to be ", l);
745e639e0d2SMartin Matuska 		(void) zhack_repair_print_cksum(stderr,
746e639e0d2SMartin Matuska 		    &expected_cksum);
747e639e0d2SMartin Matuska 		(void) fprintf(stderr, " not ");
748e639e0d2SMartin Matuska 		zhack_repair_print_cksum(stderr, &actual_cksum);
749e639e0d2SMartin Matuska 		(void) fprintf(stderr, "\n");
750e639e0d2SMartin Matuska 		err = ECKSUM;
751e639e0d2SMartin Matuska 	}
752e639e0d2SMartin Matuska 	return (err);
753e639e0d2SMartin Matuska }
754e639e0d2SMartin Matuska 
755e639e0d2SMartin Matuska static void
756e639e0d2SMartin Matuska zhack_repair_one_label(const zhack_repair_op_t op, const int fd,
757e639e0d2SMartin Matuska     vdev_label_t *vl, const uint64_t label_offset, const int l,
758e639e0d2SMartin Matuska     uint32_t *labels_repaired)
759e639e0d2SMartin Matuska {
760e639e0d2SMartin Matuska 	ssize_t err;
761e639e0d2SMartin Matuska 	uberblock_t *ub = (uberblock_t *)vl->vl_uberblock;
762e639e0d2SMartin Matuska 	void *vdev_data =
763e639e0d2SMartin Matuska 	    (char *)vl + offsetof(vdev_label_t, vl_vdev_phys);
764e639e0d2SMartin Matuska 	zio_eck_t *vdev_eck =
765e639e0d2SMartin Matuska 	    (zio_eck_t *)((char *)(vdev_data) + VDEV_PHYS_SIZE) - 1;
766e639e0d2SMartin Matuska 	const uint64_t vdev_phys_offset =
767e639e0d2SMartin Matuska 	    label_offset + offsetof(vdev_label_t, vl_vdev_phys);
768716fd348SMartin Matuska 	const char *cfg_keys[] = { ZPOOL_CONFIG_VERSION,
769716fd348SMartin Matuska 	    ZPOOL_CONFIG_POOL_STATE, ZPOOL_CONFIG_GUID };
770e639e0d2SMartin Matuska 	nvlist_t *cfg;
771e639e0d2SMartin Matuska 	nvlist_t *vdev_tree_cfg = NULL;
772e639e0d2SMartin Matuska 	uint64_t ashift;
773e639e0d2SMartin Matuska 	int byteswap;
774e639e0d2SMartin Matuska 
775e639e0d2SMartin Matuska 	err = zhack_repair_read_label(fd, vl, label_offset, l);
776e639e0d2SMartin Matuska 	if (err)
777e639e0d2SMartin Matuska 		return;
778e639e0d2SMartin Matuska 
779e639e0d2SMartin Matuska 	if (vdev_eck->zec_magic == 0) {
780e639e0d2SMartin Matuska 		(void) fprintf(stderr, "error: label %d: "
781e639e0d2SMartin Matuska 		    "Expected the nvlist checksum magic number to not be zero"
782e639e0d2SMartin Matuska 		    "\n",
783e639e0d2SMartin Matuska 		    l);
784e639e0d2SMartin Matuska 		(void) fprintf(stderr, "There should already be a checksum "
785e639e0d2SMartin Matuska 		    "for the label.\n");
786e639e0d2SMartin Matuska 		return;
787e639e0d2SMartin Matuska 	}
788e639e0d2SMartin Matuska 
789e639e0d2SMartin Matuska 	byteswap =
790e639e0d2SMartin Matuska 	    (vdev_eck->zec_magic == BSWAP_64((uint64_t)ZEC_MAGIC));
791e639e0d2SMartin Matuska 
792e639e0d2SMartin Matuska 	if (byteswap) {
793e639e0d2SMartin Matuska 		byteswap_uint64_array(&vdev_eck->zec_cksum,
794e639e0d2SMartin Matuska 		    sizeof (zio_cksum_t));
795e639e0d2SMartin Matuska 		vdev_eck->zec_magic = BSWAP_64(vdev_eck->zec_magic);
796e639e0d2SMartin Matuska 	}
797e639e0d2SMartin Matuska 
798e639e0d2SMartin Matuska 	if ((op & ZHACK_REPAIR_OP_CKSUM) == 0 &&
799e639e0d2SMartin Matuska 	    zhack_repair_test_cksum(byteswap, vdev_data, vdev_eck,
800e639e0d2SMartin Matuska 	    vdev_phys_offset, l) != 0) {
801e639e0d2SMartin Matuska 		(void) fprintf(stderr, "It would appear checksums are "
802e639e0d2SMartin Matuska 		    "corrupted. Try zhack repair label -c <device>\n");
803e639e0d2SMartin Matuska 		return;
804e639e0d2SMartin Matuska 	}
805e639e0d2SMartin Matuska 
806e639e0d2SMartin Matuska 	err = nvlist_unpack(vl->vl_vdev_phys.vp_nvlist,
807e639e0d2SMartin Matuska 	    VDEV_PHYS_SIZE - sizeof (zio_eck_t), &cfg, 0);
808e639e0d2SMartin Matuska 	if (err) {
809e639e0d2SMartin Matuska 		(void) fprintf(stderr,
810e639e0d2SMartin Matuska 		    "error: cannot unpack nvlist label %d\n", l);
811e639e0d2SMartin Matuska 		return;
812e639e0d2SMartin Matuska 	}
813e639e0d2SMartin Matuska 
814e639e0d2SMartin Matuska 	err = zhack_repair_check_label(ub,
815e639e0d2SMartin Matuska 	    l, cfg_keys, ARRAY_SIZE(cfg_keys), cfg, vdev_tree_cfg, &ashift);
816e639e0d2SMartin Matuska 	if (err)
817e639e0d2SMartin Matuska 		return;
818e639e0d2SMartin Matuska 
819e639e0d2SMartin Matuska 	if ((op & ZHACK_REPAIR_OP_UNDETACH) != 0) {
820e639e0d2SMartin Matuska 		char *buf;
821e639e0d2SMartin Matuska 		size_t buflen;
822e639e0d2SMartin Matuska 
823e639e0d2SMartin Matuska 		err = zhack_repair_undetach(ub, cfg, l);
824e639e0d2SMartin Matuska 		if (err)
825e639e0d2SMartin Matuska 			return;
826e639e0d2SMartin Matuska 
827e639e0d2SMartin Matuska 		buf = vl->vl_vdev_phys.vp_nvlist;
828e639e0d2SMartin Matuska 		buflen = VDEV_PHYS_SIZE - sizeof (zio_eck_t);
829e639e0d2SMartin Matuska 		if (nvlist_pack(cfg, &buf, &buflen, NV_ENCODE_XDR, 0) != 0) {
830e639e0d2SMartin Matuska 			(void) fprintf(stderr,
831e639e0d2SMartin Matuska 			    "error: label %d: Failed to pack nvlist\n", l);
832e639e0d2SMartin Matuska 			return;
833e639e0d2SMartin Matuska 		}
834e639e0d2SMartin Matuska 
835e639e0d2SMartin Matuska 		zhack_repair_write_uberblock(vl,
836e639e0d2SMartin Matuska 		    l, ashift, fd, byteswap, label_offset, labels_repaired);
837e639e0d2SMartin Matuska 	}
838e639e0d2SMartin Matuska 
839e639e0d2SMartin Matuska 	if (zhack_repair_write_label(l, fd, byteswap, vdev_data, vdev_eck,
840e639e0d2SMartin Matuska 	    vdev_phys_offset, VDEV_PHYS_SIZE))
841e639e0d2SMartin Matuska 			labels_repaired[l] |= REPAIR_LABEL_STATUS_CKSUM;
842e639e0d2SMartin Matuska 
843e639e0d2SMartin Matuska 	fsync(fd);
844e639e0d2SMartin Matuska }
845e639e0d2SMartin Matuska 
846e639e0d2SMartin Matuska static const char *
847e639e0d2SMartin Matuska zhack_repair_label_status(const uint32_t label_status,
848e639e0d2SMartin Matuska     const uint32_t to_check)
849e639e0d2SMartin Matuska {
850e639e0d2SMartin Matuska 	return ((label_status & to_check) != 0 ? "repaired" : "skipped");
851e639e0d2SMartin Matuska }
852e639e0d2SMartin Matuska 
853e639e0d2SMartin Matuska static int
854e639e0d2SMartin Matuska zhack_label_repair(const zhack_repair_op_t op, const int argc, char **argv)
855e639e0d2SMartin Matuska {
856e639e0d2SMartin Matuska 	uint32_t labels_repaired[VDEV_LABELS] = {0};
857716fd348SMartin Matuska 	vdev_label_t labels[VDEV_LABELS] = {{{0}}};
858e639e0d2SMartin Matuska 	struct stat64 st;
859716fd348SMartin Matuska 	int fd;
860e639e0d2SMartin Matuska 	off_t filesize;
861e639e0d2SMartin Matuska 	uint32_t repaired = 0;
862716fd348SMartin Matuska 
863716fd348SMartin Matuska 	abd_init();
864716fd348SMartin Matuska 
865716fd348SMartin Matuska 	if (argc < 1) {
866716fd348SMartin Matuska 		(void) fprintf(stderr, "error: missing device\n");
867716fd348SMartin Matuska 		usage();
868716fd348SMartin Matuska 	}
869716fd348SMartin Matuska 
870716fd348SMartin Matuska 	if ((fd = open(argv[0], O_RDWR)) == -1)
871716fd348SMartin Matuska 		fatal(NULL, FTAG, "cannot open '%s': %s", argv[0],
872716fd348SMartin Matuska 		    strerror(errno));
873716fd348SMartin Matuska 
874e639e0d2SMartin Matuska 	if (fstat64_blk(fd, &st) != 0)
875716fd348SMartin Matuska 		fatal(NULL, FTAG, "cannot stat '%s': %s", argv[0],
876716fd348SMartin Matuska 		    strerror(errno));
877716fd348SMartin Matuska 
878e639e0d2SMartin Matuska 	filesize = st.st_size;
879e639e0d2SMartin Matuska 	(void) fprintf(stderr, "Calculated filesize to be %jd\n",
880e639e0d2SMartin Matuska 	    (intmax_t)filesize);
881e639e0d2SMartin Matuska 
882e639e0d2SMartin Matuska 	if (filesize % sizeof (vdev_label_t) != 0)
883e639e0d2SMartin Matuska 		filesize =
884e639e0d2SMartin Matuska 		    (filesize / sizeof (vdev_label_t)) * sizeof (vdev_label_t);
885e639e0d2SMartin Matuska 
886716fd348SMartin Matuska 	for (int l = 0; l < VDEV_LABELS; l++) {
887e639e0d2SMartin Matuska 		zhack_repair_one_label(op, fd, &labels[l],
888e639e0d2SMartin Matuska 		    vdev_label_offset(filesize, l, 0), l, labels_repaired);
889716fd348SMartin Matuska 	}
890716fd348SMartin Matuska 
891716fd348SMartin Matuska 	close(fd);
892716fd348SMartin Matuska 
893716fd348SMartin Matuska 	abd_fini();
894716fd348SMartin Matuska 
895716fd348SMartin Matuska 	for (int l = 0; l < VDEV_LABELS; l++) {
896e639e0d2SMartin Matuska 		const uint32_t lr = labels_repaired[l];
897e639e0d2SMartin Matuska 		(void) printf("label %d: ", l);
898e639e0d2SMartin Matuska 		(void) printf("uberblock: %s ",
899e639e0d2SMartin Matuska 		    zhack_repair_label_status(lr, REPAIR_LABEL_STATUS_UB));
900e639e0d2SMartin Matuska 		(void) printf("checksum: %s\n",
901e639e0d2SMartin Matuska 		    zhack_repair_label_status(lr, REPAIR_LABEL_STATUS_CKSUM));
902e639e0d2SMartin Matuska 		repaired |= lr;
903716fd348SMartin Matuska 	}
904716fd348SMartin Matuska 
905e639e0d2SMartin Matuska 	if (repaired > 0)
906716fd348SMartin Matuska 		return (0);
907716fd348SMartin Matuska 
908716fd348SMartin Matuska 	return (1);
909716fd348SMartin Matuska }
910716fd348SMartin Matuska 
911716fd348SMartin Matuska static int
912e639e0d2SMartin Matuska zhack_do_label_repair(int argc, char **argv)
913e639e0d2SMartin Matuska {
914e639e0d2SMartin Matuska 	zhack_repair_op_t op = ZHACK_REPAIR_OP_UNKNOWN;
915e639e0d2SMartin Matuska 	int c;
916e639e0d2SMartin Matuska 
917e639e0d2SMartin Matuska 	optind = 1;
918e639e0d2SMartin Matuska 	while ((c = getopt(argc, argv, "+cu")) != -1) {
919e639e0d2SMartin Matuska 		switch (c) {
920e639e0d2SMartin Matuska 		case 'c':
921e639e0d2SMartin Matuska 			op |= ZHACK_REPAIR_OP_CKSUM;
922e639e0d2SMartin Matuska 			break;
923e639e0d2SMartin Matuska 		case 'u':
924e639e0d2SMartin Matuska 			op |= ZHACK_REPAIR_OP_UNDETACH;
925e639e0d2SMartin Matuska 			break;
926e639e0d2SMartin Matuska 		default:
927e639e0d2SMartin Matuska 			usage();
928e639e0d2SMartin Matuska 			break;
929e639e0d2SMartin Matuska 		}
930e639e0d2SMartin Matuska 	}
931e639e0d2SMartin Matuska 
932e639e0d2SMartin Matuska 	argc -= optind;
933e639e0d2SMartin Matuska 	argv += optind;
934e639e0d2SMartin Matuska 
935e639e0d2SMartin Matuska 	if (op == ZHACK_REPAIR_OP_UNKNOWN)
936e639e0d2SMartin Matuska 		op = ZHACK_REPAIR_OP_CKSUM;
937e639e0d2SMartin Matuska 
938e639e0d2SMartin Matuska 	return (zhack_label_repair(op, argc, argv));
939e639e0d2SMartin Matuska }
940e639e0d2SMartin Matuska 
941e639e0d2SMartin Matuska static int
942716fd348SMartin Matuska zhack_do_label(int argc, char **argv)
943716fd348SMartin Matuska {
944716fd348SMartin Matuska 	char *subcommand;
945716fd348SMartin Matuska 	int err;
946716fd348SMartin Matuska 
947716fd348SMartin Matuska 	argc--;
948716fd348SMartin Matuska 	argv++;
949716fd348SMartin Matuska 	if (argc == 0) {
950716fd348SMartin Matuska 		(void) fprintf(stderr,
951716fd348SMartin Matuska 		    "error: no label operation specified\n");
952716fd348SMartin Matuska 		usage();
953716fd348SMartin Matuska 	}
954716fd348SMartin Matuska 
955716fd348SMartin Matuska 	subcommand = argv[0];
956716fd348SMartin Matuska 	if (strcmp(subcommand, "repair") == 0) {
957e639e0d2SMartin Matuska 		err = zhack_do_label_repair(argc, argv);
958716fd348SMartin Matuska 	} else {
959716fd348SMartin Matuska 		(void) fprintf(stderr, "error: unknown subcommand: %s\n",
960716fd348SMartin Matuska 		    subcommand);
961716fd348SMartin Matuska 		usage();
962716fd348SMartin Matuska 	}
963716fd348SMartin Matuska 
964716fd348SMartin Matuska 	return (err);
965716fd348SMartin Matuska }
966716fd348SMartin Matuska 
967716fd348SMartin Matuska #define	MAX_NUM_PATHS 1024
968716fd348SMartin Matuska 
969716fd348SMartin Matuska int
970716fd348SMartin Matuska main(int argc, char **argv)
971716fd348SMartin Matuska {
972716fd348SMartin Matuska 	char *path[MAX_NUM_PATHS];
973716fd348SMartin Matuska 	const char *subcommand;
974716fd348SMartin Matuska 	int rv = 0;
975716fd348SMartin Matuska 	int c;
976716fd348SMartin Matuska 
977716fd348SMartin Matuska 	g_importargs.path = path;
978716fd348SMartin Matuska 
979716fd348SMartin Matuska 	dprintf_setup(&argc, argv);
980716fd348SMartin Matuska 	zfs_prop_init();
981716fd348SMartin Matuska 
982716fd348SMartin Matuska 	while ((c = getopt(argc, argv, "+c:d:")) != -1) {
983716fd348SMartin Matuska 		switch (c) {
984716fd348SMartin Matuska 		case 'c':
985716fd348SMartin Matuska 			g_importargs.cachefile = optarg;
986716fd348SMartin Matuska 			break;
987716fd348SMartin Matuska 		case 'd':
988716fd348SMartin Matuska 			assert(g_importargs.paths < MAX_NUM_PATHS);
989716fd348SMartin Matuska 			g_importargs.path[g_importargs.paths++] = optarg;
990716fd348SMartin Matuska 			break;
991716fd348SMartin Matuska 		default:
992716fd348SMartin Matuska 			usage();
993716fd348SMartin Matuska 			break;
994716fd348SMartin Matuska 		}
995716fd348SMartin Matuska 	}
996716fd348SMartin Matuska 
997716fd348SMartin Matuska 	argc -= optind;
998716fd348SMartin Matuska 	argv += optind;
999716fd348SMartin Matuska 	optind = 1;
1000716fd348SMartin Matuska 
1001716fd348SMartin Matuska 	if (argc == 0) {
1002716fd348SMartin Matuska 		(void) fprintf(stderr, "error: no command specified\n");
1003716fd348SMartin Matuska 		usage();
1004716fd348SMartin Matuska 	}
1005716fd348SMartin Matuska 
1006716fd348SMartin Matuska 	subcommand = argv[0];
1007716fd348SMartin Matuska 
1008716fd348SMartin Matuska 	if (strcmp(subcommand, "feature") == 0) {
1009716fd348SMartin Matuska 		rv = zhack_do_feature(argc, argv);
1010716fd348SMartin Matuska 	} else if (strcmp(subcommand, "label") == 0) {
1011716fd348SMartin Matuska 		return (zhack_do_label(argc, argv));
1012716fd348SMartin Matuska 	} else {
1013716fd348SMartin Matuska 		(void) fprintf(stderr, "error: unknown subcommand: %s\n",
1014716fd348SMartin Matuska 		    subcommand);
1015716fd348SMartin Matuska 		usage();
1016716fd348SMartin Matuska 	}
1017716fd348SMartin Matuska 
1018716fd348SMartin Matuska 	if (!g_readonly && spa_export(g_pool, NULL, B_TRUE, B_FALSE) != 0) {
1019716fd348SMartin Matuska 		fatal(NULL, FTAG, "pool export failed; "
1020716fd348SMartin Matuska 		    "changes may not be committed to disk\n");
1021716fd348SMartin Matuska 	}
1022716fd348SMartin Matuska 
1023716fd348SMartin Matuska 	kernel_fini();
1024716fd348SMartin Matuska 
1025716fd348SMartin Matuska 	return (rv);
1026716fd348SMartin Matuska }
1027