xref: /freebsd/sys/contrib/openzfs/cmd/zhack.c (revision 911f0260390e18cf85f3dbf2c719b593efdc1e3c)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or https://opensource.org/licenses/CDDL-1.0.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
24  * Copyright (c) 2013 Steven Hartland. All rights reserved.
25  */
26 
27 /*
28  * zhack is a debugging tool that can write changes to ZFS pool using libzpool
29  * for testing purposes. Altering pools with zhack is unsupported and may
30  * result in corrupted pools.
31  */
32 
33 #include <zfs_prop.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <ctype.h>
37 #include <sys/stat.h>
38 #include <sys/zfs_context.h>
39 #include <sys/spa.h>
40 #include <sys/spa_impl.h>
41 #include <sys/dmu.h>
42 #include <sys/zap.h>
43 #include <sys/zfs_znode.h>
44 #include <sys/dsl_synctask.h>
45 #include <sys/vdev.h>
46 #include <sys/vdev_impl.h>
47 #include <sys/fs/zfs.h>
48 #include <sys/dmu_objset.h>
49 #include <sys/dsl_pool.h>
50 #include <sys/zio_checksum.h>
51 #include <sys/zio_compress.h>
52 #include <sys/zfeature.h>
53 #include <sys/dmu_tx.h>
54 #include <zfeature_common.h>
55 #include <libzutil.h>
56 
57 static importargs_t g_importargs;
58 static char *g_pool;
59 static boolean_t g_readonly;
60 
61 typedef enum {
62 	ZHACK_REPAIR_OP_UNKNOWN  = 0,
63 	ZHACK_REPAIR_OP_CKSUM    = (1 << 0),
64 	ZHACK_REPAIR_OP_UNDETACH = (1 << 1)
65 } zhack_repair_op_t;
66 
67 static __attribute__((noreturn)) void
68 usage(void)
69 {
70 	(void) fprintf(stderr,
71 	    "Usage: zhack [-c cachefile] [-d dir] <subcommand> <args> ...\n"
72 	    "where <subcommand> <args> is one of the following:\n"
73 	    "\n");
74 
75 	(void) fprintf(stderr,
76 	    "    feature stat <pool>\n"
77 	    "        print information about enabled features\n"
78 	    "    feature enable [-r] [-d desc] <pool> <feature>\n"
79 	    "        add a new enabled feature to the pool\n"
80 	    "        -d <desc> sets the feature's description\n"
81 	    "        -r set read-only compatible flag for feature\n"
82 	    "    feature ref [-md] <pool> <feature>\n"
83 	    "        change the refcount on the given feature\n"
84 	    "        -d decrease instead of increase the refcount\n"
85 	    "        -m add the feature to the label if increasing refcount\n"
86 	    "\n"
87 	    "    <feature> : should be a feature guid\n"
88 	    "\n"
89 	    "    label repair <device>\n"
90 	    "        repair labels of a specified device according to options\n"
91 	    "        which may be combined to do their functions in one call\n"
92 	    "        -c repair corrupted label checksums\n"
93 	    "        -u restore the label on a detached device\n"
94 	    "\n"
95 	    "    <device> : path to vdev\n");
96 	exit(1);
97 }
98 
99 
100 static __attribute__((format(printf, 3, 4))) __attribute__((noreturn)) void
101 fatal(spa_t *spa, const void *tag, const char *fmt, ...)
102 {
103 	va_list ap;
104 
105 	if (spa != NULL) {
106 		spa_close(spa, tag);
107 		(void) spa_export(g_pool, NULL, B_TRUE, B_FALSE);
108 	}
109 
110 	va_start(ap, fmt);
111 	(void) fputs("zhack: ", stderr);
112 	(void) vfprintf(stderr, fmt, ap);
113 	va_end(ap);
114 	(void) fputc('\n', stderr);
115 
116 	exit(1);
117 }
118 
119 static int
120 space_delta_cb(dmu_object_type_t bonustype, const void *data,
121     zfs_file_info_t *zoi)
122 {
123 	(void) data, (void) zoi;
124 
125 	/*
126 	 * Is it a valid type of object to track?
127 	 */
128 	if (bonustype != DMU_OT_ZNODE && bonustype != DMU_OT_SA)
129 		return (ENOENT);
130 	(void) fprintf(stderr, "modifying object that needs user accounting");
131 	abort();
132 }
133 
134 /*
135  * Target is the dataset whose pool we want to open.
136  */
137 static void
138 zhack_import(char *target, boolean_t readonly)
139 {
140 	nvlist_t *config;
141 	nvlist_t *props;
142 	int error;
143 
144 	kernel_init(readonly ? SPA_MODE_READ :
145 	    (SPA_MODE_READ | SPA_MODE_WRITE));
146 
147 	dmu_objset_register_type(DMU_OST_ZFS, space_delta_cb);
148 
149 	g_readonly = readonly;
150 	g_importargs.can_be_active = readonly;
151 	g_pool = strdup(target);
152 
153 	libpc_handle_t lpch = {
154 		.lpc_lib_handle = NULL,
155 		.lpc_ops = &libzpool_config_ops,
156 		.lpc_printerr = B_TRUE
157 	};
158 	error = zpool_find_config(&lpch, target, &config, &g_importargs);
159 	if (error)
160 		fatal(NULL, FTAG, "cannot import '%s'", target);
161 
162 	props = NULL;
163 	if (readonly) {
164 		VERIFY(nvlist_alloc(&props, NV_UNIQUE_NAME, 0) == 0);
165 		VERIFY(nvlist_add_uint64(props,
166 		    zpool_prop_to_name(ZPOOL_PROP_READONLY), 1) == 0);
167 	}
168 
169 	zfeature_checks_disable = B_TRUE;
170 	error = spa_import(target, config, props,
171 	    (readonly ?  ZFS_IMPORT_SKIP_MMP : ZFS_IMPORT_NORMAL));
172 	fnvlist_free(config);
173 	zfeature_checks_disable = B_FALSE;
174 	if (error == EEXIST)
175 		error = 0;
176 
177 	if (error)
178 		fatal(NULL, FTAG, "can't import '%s': %s", target,
179 		    strerror(error));
180 }
181 
182 static void
183 zhack_spa_open(char *target, boolean_t readonly, const void *tag, spa_t **spa)
184 {
185 	int err;
186 
187 	zhack_import(target, readonly);
188 
189 	zfeature_checks_disable = B_TRUE;
190 	err = spa_open(target, spa, tag);
191 	zfeature_checks_disable = B_FALSE;
192 
193 	if (err != 0)
194 		fatal(*spa, FTAG, "cannot open '%s': %s", target,
195 		    strerror(err));
196 	if (spa_version(*spa) < SPA_VERSION_FEATURES) {
197 		fatal(*spa, FTAG, "'%s' has version %d, features not enabled",
198 		    target, (int)spa_version(*spa));
199 	}
200 }
201 
202 static void
203 dump_obj(objset_t *os, uint64_t obj, const char *name)
204 {
205 	zap_cursor_t zc;
206 	zap_attribute_t za;
207 
208 	(void) printf("%s_obj:\n", name);
209 
210 	for (zap_cursor_init(&zc, os, obj);
211 	    zap_cursor_retrieve(&zc, &za) == 0;
212 	    zap_cursor_advance(&zc)) {
213 		if (za.za_integer_length == 8) {
214 			ASSERT(za.za_num_integers == 1);
215 			(void) printf("\t%s = %llu\n",
216 			    za.za_name, (u_longlong_t)za.za_first_integer);
217 		} else {
218 			ASSERT(za.za_integer_length == 1);
219 			char val[1024];
220 			VERIFY(zap_lookup(os, obj, za.za_name,
221 			    1, sizeof (val), val) == 0);
222 			(void) printf("\t%s = %s\n", za.za_name, val);
223 		}
224 	}
225 	zap_cursor_fini(&zc);
226 }
227 
228 static void
229 dump_mos(spa_t *spa)
230 {
231 	nvlist_t *nv = spa->spa_label_features;
232 	nvpair_t *pair;
233 
234 	(void) printf("label config:\n");
235 	for (pair = nvlist_next_nvpair(nv, NULL);
236 	    pair != NULL;
237 	    pair = nvlist_next_nvpair(nv, pair)) {
238 		(void) printf("\t%s\n", nvpair_name(pair));
239 	}
240 }
241 
242 static void
243 zhack_do_feature_stat(int argc, char **argv)
244 {
245 	spa_t *spa;
246 	objset_t *os;
247 	char *target;
248 
249 	argc--;
250 	argv++;
251 
252 	if (argc < 1) {
253 		(void) fprintf(stderr, "error: missing pool name\n");
254 		usage();
255 	}
256 	target = argv[0];
257 
258 	zhack_spa_open(target, B_TRUE, FTAG, &spa);
259 	os = spa->spa_meta_objset;
260 
261 	dump_obj(os, spa->spa_feat_for_read_obj, "for_read");
262 	dump_obj(os, spa->spa_feat_for_write_obj, "for_write");
263 	dump_obj(os, spa->spa_feat_desc_obj, "descriptions");
264 	if (spa_feature_is_active(spa, SPA_FEATURE_ENABLED_TXG)) {
265 		dump_obj(os, spa->spa_feat_enabled_txg_obj, "enabled_txg");
266 	}
267 	dump_mos(spa);
268 
269 	spa_close(spa, FTAG);
270 }
271 
272 static void
273 zhack_feature_enable_sync(void *arg, dmu_tx_t *tx)
274 {
275 	spa_t *spa = dmu_tx_pool(tx)->dp_spa;
276 	zfeature_info_t *feature = arg;
277 
278 	feature_enable_sync(spa, feature, tx);
279 
280 	spa_history_log_internal(spa, "zhack enable feature", tx,
281 	    "name=%s flags=%u",
282 	    feature->fi_guid, feature->fi_flags);
283 }
284 
285 static void
286 zhack_do_feature_enable(int argc, char **argv)
287 {
288 	int c;
289 	char *desc, *target;
290 	spa_t *spa;
291 	objset_t *mos;
292 	zfeature_info_t feature;
293 	const spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
294 
295 	/*
296 	 * Features are not added to the pool's label until their refcounts
297 	 * are incremented, so fi_mos can just be left as false for now.
298 	 */
299 	desc = NULL;
300 	feature.fi_uname = "zhack";
301 	feature.fi_flags = 0;
302 	feature.fi_depends = nodeps;
303 	feature.fi_feature = SPA_FEATURE_NONE;
304 
305 	optind = 1;
306 	while ((c = getopt(argc, argv, "+rd:")) != -1) {
307 		switch (c) {
308 		case 'r':
309 			feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT;
310 			break;
311 		case 'd':
312 			if (desc != NULL)
313 				free(desc);
314 			desc = strdup(optarg);
315 			break;
316 		default:
317 			usage();
318 			break;
319 		}
320 	}
321 
322 	if (desc == NULL)
323 		desc = strdup("zhack injected");
324 	feature.fi_desc = desc;
325 
326 	argc -= optind;
327 	argv += optind;
328 
329 	if (argc < 2) {
330 		(void) fprintf(stderr, "error: missing feature or pool name\n");
331 		usage();
332 	}
333 	target = argv[0];
334 	feature.fi_guid = argv[1];
335 
336 	if (!zfeature_is_valid_guid(feature.fi_guid))
337 		fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid);
338 
339 	zhack_spa_open(target, B_FALSE, FTAG, &spa);
340 	mos = spa->spa_meta_objset;
341 
342 	if (zfeature_is_supported(feature.fi_guid))
343 		fatal(spa, FTAG, "'%s' is a real feature, will not enable",
344 		    feature.fi_guid);
345 	if (0 == zap_contains(mos, spa->spa_feat_desc_obj, feature.fi_guid))
346 		fatal(spa, FTAG, "feature already enabled: %s",
347 		    feature.fi_guid);
348 
349 	VERIFY0(dsl_sync_task(spa_name(spa), NULL,
350 	    zhack_feature_enable_sync, &feature, 5, ZFS_SPACE_CHECK_NORMAL));
351 
352 	spa_close(spa, FTAG);
353 
354 	free(desc);
355 }
356 
357 static void
358 feature_incr_sync(void *arg, dmu_tx_t *tx)
359 {
360 	spa_t *spa = dmu_tx_pool(tx)->dp_spa;
361 	zfeature_info_t *feature = arg;
362 	uint64_t refcount;
363 
364 	VERIFY0(feature_get_refcount_from_disk(spa, feature, &refcount));
365 	feature_sync(spa, feature, refcount + 1, tx);
366 	spa_history_log_internal(spa, "zhack feature incr", tx,
367 	    "name=%s", feature->fi_guid);
368 }
369 
370 static void
371 feature_decr_sync(void *arg, dmu_tx_t *tx)
372 {
373 	spa_t *spa = dmu_tx_pool(tx)->dp_spa;
374 	zfeature_info_t *feature = arg;
375 	uint64_t refcount;
376 
377 	VERIFY0(feature_get_refcount_from_disk(spa, feature, &refcount));
378 	feature_sync(spa, feature, refcount - 1, tx);
379 	spa_history_log_internal(spa, "zhack feature decr", tx,
380 	    "name=%s", feature->fi_guid);
381 }
382 
383 static void
384 zhack_do_feature_ref(int argc, char **argv)
385 {
386 	int c;
387 	char *target;
388 	boolean_t decr = B_FALSE;
389 	spa_t *spa;
390 	objset_t *mos;
391 	zfeature_info_t feature;
392 	const spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
393 
394 	/*
395 	 * fi_desc does not matter here because it was written to disk
396 	 * when the feature was enabled, but we need to properly set the
397 	 * feature for read or write based on the information we read off
398 	 * disk later.
399 	 */
400 	feature.fi_uname = "zhack";
401 	feature.fi_flags = 0;
402 	feature.fi_desc = NULL;
403 	feature.fi_depends = nodeps;
404 	feature.fi_feature = SPA_FEATURE_NONE;
405 
406 	optind = 1;
407 	while ((c = getopt(argc, argv, "+md")) != -1) {
408 		switch (c) {
409 		case 'm':
410 			feature.fi_flags |= ZFEATURE_FLAG_MOS;
411 			break;
412 		case 'd':
413 			decr = B_TRUE;
414 			break;
415 		default:
416 			usage();
417 			break;
418 		}
419 	}
420 	argc -= optind;
421 	argv += optind;
422 
423 	if (argc < 2) {
424 		(void) fprintf(stderr, "error: missing feature or pool name\n");
425 		usage();
426 	}
427 	target = argv[0];
428 	feature.fi_guid = argv[1];
429 
430 	if (!zfeature_is_valid_guid(feature.fi_guid))
431 		fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid);
432 
433 	zhack_spa_open(target, B_FALSE, FTAG, &spa);
434 	mos = spa->spa_meta_objset;
435 
436 	if (zfeature_is_supported(feature.fi_guid)) {
437 		fatal(spa, FTAG,
438 		    "'%s' is a real feature, will not change refcount",
439 		    feature.fi_guid);
440 	}
441 
442 	if (0 == zap_contains(mos, spa->spa_feat_for_read_obj,
443 	    feature.fi_guid)) {
444 		feature.fi_flags &= ~ZFEATURE_FLAG_READONLY_COMPAT;
445 	} else if (0 == zap_contains(mos, spa->spa_feat_for_write_obj,
446 	    feature.fi_guid)) {
447 		feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT;
448 	} else {
449 		fatal(spa, FTAG, "feature is not enabled: %s", feature.fi_guid);
450 	}
451 
452 	if (decr) {
453 		uint64_t count;
454 		if (feature_get_refcount_from_disk(spa, &feature,
455 		    &count) == 0 && count == 0) {
456 			fatal(spa, FTAG, "feature refcount already 0: %s",
457 			    feature.fi_guid);
458 		}
459 	}
460 
461 	VERIFY0(dsl_sync_task(spa_name(spa), NULL,
462 	    decr ? feature_decr_sync : feature_incr_sync, &feature,
463 	    5, ZFS_SPACE_CHECK_NORMAL));
464 
465 	spa_close(spa, FTAG);
466 }
467 
468 static int
469 zhack_do_feature(int argc, char **argv)
470 {
471 	char *subcommand;
472 
473 	argc--;
474 	argv++;
475 	if (argc == 0) {
476 		(void) fprintf(stderr,
477 		    "error: no feature operation specified\n");
478 		usage();
479 	}
480 
481 	subcommand = argv[0];
482 	if (strcmp(subcommand, "stat") == 0) {
483 		zhack_do_feature_stat(argc, argv);
484 	} else if (strcmp(subcommand, "enable") == 0) {
485 		zhack_do_feature_enable(argc, argv);
486 	} else if (strcmp(subcommand, "ref") == 0) {
487 		zhack_do_feature_ref(argc, argv);
488 	} else {
489 		(void) fprintf(stderr, "error: unknown subcommand: %s\n",
490 		    subcommand);
491 		usage();
492 	}
493 
494 	return (0);
495 }
496 
497 #define	ASHIFT_UBERBLOCK_SHIFT(ashift)	\
498 	MIN(MAX(ashift, UBERBLOCK_SHIFT), \
499 	MAX_UBERBLOCK_SHIFT)
500 #define	ASHIFT_UBERBLOCK_SIZE(ashift) \
501 	(1ULL << ASHIFT_UBERBLOCK_SHIFT(ashift))
502 
503 #define	REPAIR_LABEL_STATUS_CKSUM (1 << 0)
504 #define	REPAIR_LABEL_STATUS_UB    (1 << 1)
505 
506 static int
507 zhack_repair_read_label(const int fd, vdev_label_t *vl,
508     const uint64_t label_offset, const int l)
509 {
510 	const int err = pread64(fd, vl, sizeof (vdev_label_t), label_offset);
511 
512 	if (err == -1) {
513 		(void) fprintf(stderr,
514 		    "error: cannot read label %d: %s\n",
515 		    l, strerror(errno));
516 		return (err);
517 	} else if (err != sizeof (vdev_label_t)) {
518 		(void) fprintf(stderr,
519 		    "error: bad label %d read size\n", l);
520 		return (err);
521 	}
522 
523 	return (0);
524 }
525 
526 static void
527 zhack_repair_calc_cksum(const int byteswap, void *data, const uint64_t offset,
528     const uint64_t abdsize, zio_eck_t *eck, zio_cksum_t *cksum)
529 {
530 	zio_cksum_t verifier;
531 	zio_cksum_t current_cksum;
532 	zio_checksum_info_t *ci;
533 	abd_t *abd;
534 
535 	ZIO_SET_CHECKSUM(&verifier, offset, 0, 0, 0);
536 
537 	if (byteswap)
538 		byteswap_uint64_array(&verifier, sizeof (zio_cksum_t));
539 
540 	current_cksum = eck->zec_cksum;
541 	eck->zec_cksum = verifier;
542 
543 	ci = &zio_checksum_table[ZIO_CHECKSUM_LABEL];
544 	abd = abd_get_from_buf(data, abdsize);
545 	ci->ci_func[byteswap](abd, abdsize, NULL, cksum);
546 	abd_free(abd);
547 
548 	eck->zec_cksum = current_cksum;
549 }
550 
551 static int
552 zhack_repair_check_label(uberblock_t *ub, const int l, const char **cfg_keys,
553     const size_t cfg_keys_len, nvlist_t *cfg, nvlist_t *vdev_tree_cfg,
554     uint64_t *ashift)
555 {
556 	int err;
557 
558 	if (ub->ub_txg != 0) {
559 		(void) fprintf(stderr,
560 		    "error: label %d: UB TXG of 0 expected, but got %"
561 		    PRIu64 "\n",
562 		    l, ub->ub_txg);
563 		(void) fprintf(stderr, "It would appear the device was not "
564 		    "properly removed.\n");
565 		return (1);
566 	}
567 
568 	for (int i = 0; i < cfg_keys_len; i++) {
569 		uint64_t val;
570 		err = nvlist_lookup_uint64(cfg, cfg_keys[i], &val);
571 		if (err) {
572 			(void) fprintf(stderr,
573 			    "error: label %d, %d: "
574 			    "cannot find nvlist key %s\n",
575 			    l, i, cfg_keys[i]);
576 			return (err);
577 		}
578 	}
579 
580 	err = nvlist_lookup_nvlist(cfg,
581 	    ZPOOL_CONFIG_VDEV_TREE, &vdev_tree_cfg);
582 	if (err) {
583 		(void) fprintf(stderr,
584 		    "error: label %d: cannot find nvlist key %s\n",
585 		    l, ZPOOL_CONFIG_VDEV_TREE);
586 		return (err);
587 	}
588 
589 	err = nvlist_lookup_uint64(vdev_tree_cfg,
590 	    ZPOOL_CONFIG_ASHIFT, ashift);
591 	if (err) {
592 		(void) fprintf(stderr,
593 		    "error: label %d: cannot find nvlist key %s\n",
594 		    l, ZPOOL_CONFIG_ASHIFT);
595 		return (err);
596 	}
597 
598 	if (*ashift == 0) {
599 		(void) fprintf(stderr,
600 		    "error: label %d: nvlist key %s is zero\n",
601 		    l, ZPOOL_CONFIG_ASHIFT);
602 		return (err);
603 	}
604 
605 	return (0);
606 }
607 
608 static int
609 zhack_repair_undetach(uberblock_t *ub, nvlist_t *cfg, const int l)
610 {
611 	/*
612 	 * Uberblock root block pointer has valid birth TXG.
613 	 * Copying it to the label NVlist
614 	 */
615 	if (ub->ub_rootbp.blk_birth != 0) {
616 		const uint64_t txg = ub->ub_rootbp.blk_birth;
617 		ub->ub_txg = txg;
618 
619 		if (nvlist_remove_all(cfg, ZPOOL_CONFIG_CREATE_TXG) != 0) {
620 			(void) fprintf(stderr,
621 			    "error: label %d: "
622 			    "Failed to remove pool creation TXG\n",
623 			    l);
624 			return (1);
625 		}
626 
627 		if (nvlist_remove_all(cfg, ZPOOL_CONFIG_POOL_TXG) != 0) {
628 			(void) fprintf(stderr,
629 			    "error: label %d: Failed to remove pool TXG to "
630 			    "be replaced.\n",
631 			    l);
632 			return (1);
633 		}
634 
635 		if (nvlist_add_uint64(cfg, ZPOOL_CONFIG_POOL_TXG, txg) != 0) {
636 			(void) fprintf(stderr,
637 			    "error: label %d: "
638 			    "Failed to add pool TXG of %" PRIu64 "\n",
639 			    l, txg);
640 			return (1);
641 		}
642 	}
643 
644 	return (0);
645 }
646 
647 static boolean_t
648 zhack_repair_write_label(const int l, const int fd, const int byteswap,
649     void *data, zio_eck_t *eck, const uint64_t offset, const uint64_t abdsize)
650 {
651 	zio_cksum_t actual_cksum;
652 	zhack_repair_calc_cksum(byteswap, data, offset, abdsize, eck,
653 	    &actual_cksum);
654 	zio_cksum_t expected_cksum = eck->zec_cksum;
655 	ssize_t err;
656 
657 	if (ZIO_CHECKSUM_EQUAL(actual_cksum, expected_cksum))
658 		return (B_FALSE);
659 
660 	eck->zec_cksum = actual_cksum;
661 
662 	err = pwrite64(fd, data, abdsize, offset);
663 	if (err == -1) {
664 		(void) fprintf(stderr, "error: cannot write label %d: %s\n",
665 		    l, strerror(errno));
666 		return (B_FALSE);
667 	} else if (err != abdsize) {
668 		(void) fprintf(stderr, "error: bad write size label %d\n", l);
669 		return (B_FALSE);
670 	} else {
671 		(void) fprintf(stderr,
672 		    "label %d: wrote %" PRIu64 " bytes at offset %" PRIu64 "\n",
673 		    l, abdsize, offset);
674 	}
675 
676 	return (B_TRUE);
677 }
678 
679 static void
680 zhack_repair_write_uberblock(vdev_label_t *vl, const int l,
681     const uint64_t ashift, const int fd, const int byteswap,
682     const uint64_t label_offset, uint32_t *labels_repaired)
683 {
684 	void *ub_data =
685 	    (char *)vl + offsetof(vdev_label_t, vl_uberblock);
686 	zio_eck_t *ub_eck =
687 	    (zio_eck_t *)
688 	    ((char *)(ub_data) + (ASHIFT_UBERBLOCK_SIZE(ashift))) - 1;
689 
690 	if (ub_eck->zec_magic != 0) {
691 		(void) fprintf(stderr,
692 		    "error: label %d: "
693 		    "Expected Uberblock checksum magic number to "
694 		    "be 0, but got %" PRIu64 "\n",
695 		    l, ub_eck->zec_magic);
696 		(void) fprintf(stderr, "It would appear there's already "
697 		    "a checksum for the uberblock.\n");
698 		return;
699 	}
700 
701 
702 	ub_eck->zec_magic = byteswap ? BSWAP_64(ZEC_MAGIC) : ZEC_MAGIC;
703 
704 	if (zhack_repair_write_label(l, fd, byteswap,
705 	    ub_data, ub_eck,
706 	    label_offset + offsetof(vdev_label_t, vl_uberblock),
707 	    ASHIFT_UBERBLOCK_SIZE(ashift)))
708 			labels_repaired[l] |= REPAIR_LABEL_STATUS_UB;
709 }
710 
711 static void
712 zhack_repair_print_cksum(FILE *stream, const zio_cksum_t *cksum)
713 {
714 	(void) fprintf(stream,
715 	    "%016llx:%016llx:%016llx:%016llx",
716 	    (u_longlong_t)cksum->zc_word[0],
717 	    (u_longlong_t)cksum->zc_word[1],
718 	    (u_longlong_t)cksum->zc_word[2],
719 	    (u_longlong_t)cksum->zc_word[3]);
720 }
721 
722 static int
723 zhack_repair_test_cksum(const int byteswap, void *vdev_data,
724     zio_eck_t *vdev_eck, const uint64_t vdev_phys_offset, const int l)
725 {
726 	const zio_cksum_t expected_cksum = vdev_eck->zec_cksum;
727 	zio_cksum_t actual_cksum;
728 	zhack_repair_calc_cksum(byteswap, vdev_data, vdev_phys_offset,
729 	    VDEV_PHYS_SIZE, vdev_eck, &actual_cksum);
730 	const uint64_t expected_magic = byteswap ?
731 	    BSWAP_64(ZEC_MAGIC) : ZEC_MAGIC;
732 	const uint64_t actual_magic = vdev_eck->zec_magic;
733 	int err = 0;
734 	if (actual_magic != expected_magic) {
735 		(void) fprintf(stderr, "error: label %d: "
736 		    "Expected "
737 		    "the nvlist checksum magic number to not be %"
738 		    PRIu64 " not %" PRIu64 "\n",
739 		    l, expected_magic, actual_magic);
740 		err = ECKSUM;
741 	}
742 	if (!ZIO_CHECKSUM_EQUAL(actual_cksum, expected_cksum)) {
743 		(void) fprintf(stderr, "error: label %d: "
744 		    "Expected the nvlist checksum to be ", l);
745 		(void) zhack_repair_print_cksum(stderr,
746 		    &expected_cksum);
747 		(void) fprintf(stderr, " not ");
748 		zhack_repair_print_cksum(stderr, &actual_cksum);
749 		(void) fprintf(stderr, "\n");
750 		err = ECKSUM;
751 	}
752 	return (err);
753 }
754 
755 static void
756 zhack_repair_one_label(const zhack_repair_op_t op, const int fd,
757     vdev_label_t *vl, const uint64_t label_offset, const int l,
758     uint32_t *labels_repaired)
759 {
760 	ssize_t err;
761 	uberblock_t *ub = (uberblock_t *)vl->vl_uberblock;
762 	void *vdev_data =
763 	    (char *)vl + offsetof(vdev_label_t, vl_vdev_phys);
764 	zio_eck_t *vdev_eck =
765 	    (zio_eck_t *)((char *)(vdev_data) + VDEV_PHYS_SIZE) - 1;
766 	const uint64_t vdev_phys_offset =
767 	    label_offset + offsetof(vdev_label_t, vl_vdev_phys);
768 	const char *cfg_keys[] = { ZPOOL_CONFIG_VERSION,
769 	    ZPOOL_CONFIG_POOL_STATE, ZPOOL_CONFIG_GUID };
770 	nvlist_t *cfg;
771 	nvlist_t *vdev_tree_cfg = NULL;
772 	uint64_t ashift;
773 	int byteswap;
774 
775 	err = zhack_repair_read_label(fd, vl, label_offset, l);
776 	if (err)
777 		return;
778 
779 	if (vdev_eck->zec_magic == 0) {
780 		(void) fprintf(stderr, "error: label %d: "
781 		    "Expected the nvlist checksum magic number to not be zero"
782 		    "\n",
783 		    l);
784 		(void) fprintf(stderr, "There should already be a checksum "
785 		    "for the label.\n");
786 		return;
787 	}
788 
789 	byteswap =
790 	    (vdev_eck->zec_magic == BSWAP_64((uint64_t)ZEC_MAGIC));
791 
792 	if (byteswap) {
793 		byteswap_uint64_array(&vdev_eck->zec_cksum,
794 		    sizeof (zio_cksum_t));
795 		vdev_eck->zec_magic = BSWAP_64(vdev_eck->zec_magic);
796 	}
797 
798 	if ((op & ZHACK_REPAIR_OP_CKSUM) == 0 &&
799 	    zhack_repair_test_cksum(byteswap, vdev_data, vdev_eck,
800 	    vdev_phys_offset, l) != 0) {
801 		(void) fprintf(stderr, "It would appear checksums are "
802 		    "corrupted. Try zhack repair label -c <device>\n");
803 		return;
804 	}
805 
806 	err = nvlist_unpack(vl->vl_vdev_phys.vp_nvlist,
807 	    VDEV_PHYS_SIZE - sizeof (zio_eck_t), &cfg, 0);
808 	if (err) {
809 		(void) fprintf(stderr,
810 		    "error: cannot unpack nvlist label %d\n", l);
811 		return;
812 	}
813 
814 	err = zhack_repair_check_label(ub,
815 	    l, cfg_keys, ARRAY_SIZE(cfg_keys), cfg, vdev_tree_cfg, &ashift);
816 	if (err)
817 		return;
818 
819 	if ((op & ZHACK_REPAIR_OP_UNDETACH) != 0) {
820 		char *buf;
821 		size_t buflen;
822 
823 		err = zhack_repair_undetach(ub, cfg, l);
824 		if (err)
825 			return;
826 
827 		buf = vl->vl_vdev_phys.vp_nvlist;
828 		buflen = VDEV_PHYS_SIZE - sizeof (zio_eck_t);
829 		if (nvlist_pack(cfg, &buf, &buflen, NV_ENCODE_XDR, 0) != 0) {
830 			(void) fprintf(stderr,
831 			    "error: label %d: Failed to pack nvlist\n", l);
832 			return;
833 		}
834 
835 		zhack_repair_write_uberblock(vl,
836 		    l, ashift, fd, byteswap, label_offset, labels_repaired);
837 	}
838 
839 	if (zhack_repair_write_label(l, fd, byteswap, vdev_data, vdev_eck,
840 	    vdev_phys_offset, VDEV_PHYS_SIZE))
841 			labels_repaired[l] |= REPAIR_LABEL_STATUS_CKSUM;
842 
843 	fsync(fd);
844 }
845 
846 static const char *
847 zhack_repair_label_status(const uint32_t label_status,
848     const uint32_t to_check)
849 {
850 	return ((label_status & to_check) != 0 ? "repaired" : "skipped");
851 }
852 
853 static int
854 zhack_label_repair(const zhack_repair_op_t op, const int argc, char **argv)
855 {
856 	uint32_t labels_repaired[VDEV_LABELS] = {0};
857 	vdev_label_t labels[VDEV_LABELS] = {{{0}}};
858 	struct stat64 st;
859 	int fd;
860 	off_t filesize;
861 	uint32_t repaired = 0;
862 
863 	abd_init();
864 
865 	if (argc < 1) {
866 		(void) fprintf(stderr, "error: missing device\n");
867 		usage();
868 	}
869 
870 	if ((fd = open(argv[0], O_RDWR)) == -1)
871 		fatal(NULL, FTAG, "cannot open '%s': %s", argv[0],
872 		    strerror(errno));
873 
874 	if (fstat64_blk(fd, &st) != 0)
875 		fatal(NULL, FTAG, "cannot stat '%s': %s", argv[0],
876 		    strerror(errno));
877 
878 	filesize = st.st_size;
879 	(void) fprintf(stderr, "Calculated filesize to be %jd\n",
880 	    (intmax_t)filesize);
881 
882 	if (filesize % sizeof (vdev_label_t) != 0)
883 		filesize =
884 		    (filesize / sizeof (vdev_label_t)) * sizeof (vdev_label_t);
885 
886 	for (int l = 0; l < VDEV_LABELS; l++) {
887 		zhack_repair_one_label(op, fd, &labels[l],
888 		    vdev_label_offset(filesize, l, 0), l, labels_repaired);
889 	}
890 
891 	close(fd);
892 
893 	abd_fini();
894 
895 	for (int l = 0; l < VDEV_LABELS; l++) {
896 		const uint32_t lr = labels_repaired[l];
897 		(void) printf("label %d: ", l);
898 		(void) printf("uberblock: %s ",
899 		    zhack_repair_label_status(lr, REPAIR_LABEL_STATUS_UB));
900 		(void) printf("checksum: %s\n",
901 		    zhack_repair_label_status(lr, REPAIR_LABEL_STATUS_CKSUM));
902 		repaired |= lr;
903 	}
904 
905 	if (repaired > 0)
906 		return (0);
907 
908 	return (1);
909 }
910 
911 static int
912 zhack_do_label_repair(int argc, char **argv)
913 {
914 	zhack_repair_op_t op = ZHACK_REPAIR_OP_UNKNOWN;
915 	int c;
916 
917 	optind = 1;
918 	while ((c = getopt(argc, argv, "+cu")) != -1) {
919 		switch (c) {
920 		case 'c':
921 			op |= ZHACK_REPAIR_OP_CKSUM;
922 			break;
923 		case 'u':
924 			op |= ZHACK_REPAIR_OP_UNDETACH;
925 			break;
926 		default:
927 			usage();
928 			break;
929 		}
930 	}
931 
932 	argc -= optind;
933 	argv += optind;
934 
935 	if (op == ZHACK_REPAIR_OP_UNKNOWN)
936 		op = ZHACK_REPAIR_OP_CKSUM;
937 
938 	return (zhack_label_repair(op, argc, argv));
939 }
940 
941 static int
942 zhack_do_label(int argc, char **argv)
943 {
944 	char *subcommand;
945 	int err;
946 
947 	argc--;
948 	argv++;
949 	if (argc == 0) {
950 		(void) fprintf(stderr,
951 		    "error: no label operation specified\n");
952 		usage();
953 	}
954 
955 	subcommand = argv[0];
956 	if (strcmp(subcommand, "repair") == 0) {
957 		err = zhack_do_label_repair(argc, argv);
958 	} else {
959 		(void) fprintf(stderr, "error: unknown subcommand: %s\n",
960 		    subcommand);
961 		usage();
962 	}
963 
964 	return (err);
965 }
966 
967 #define	MAX_NUM_PATHS 1024
968 
969 int
970 main(int argc, char **argv)
971 {
972 	char *path[MAX_NUM_PATHS];
973 	const char *subcommand;
974 	int rv = 0;
975 	int c;
976 
977 	g_importargs.path = path;
978 
979 	dprintf_setup(&argc, argv);
980 	zfs_prop_init();
981 
982 	while ((c = getopt(argc, argv, "+c:d:")) != -1) {
983 		switch (c) {
984 		case 'c':
985 			g_importargs.cachefile = optarg;
986 			break;
987 		case 'd':
988 			assert(g_importargs.paths < MAX_NUM_PATHS);
989 			g_importargs.path[g_importargs.paths++] = optarg;
990 			break;
991 		default:
992 			usage();
993 			break;
994 		}
995 	}
996 
997 	argc -= optind;
998 	argv += optind;
999 	optind = 1;
1000 
1001 	if (argc == 0) {
1002 		(void) fprintf(stderr, "error: no command specified\n");
1003 		usage();
1004 	}
1005 
1006 	subcommand = argv[0];
1007 
1008 	if (strcmp(subcommand, "feature") == 0) {
1009 		rv = zhack_do_feature(argc, argv);
1010 	} else if (strcmp(subcommand, "label") == 0) {
1011 		return (zhack_do_label(argc, argv));
1012 	} else {
1013 		(void) fprintf(stderr, "error: unknown subcommand: %s\n",
1014 		    subcommand);
1015 		usage();
1016 	}
1017 
1018 	if (!g_readonly && spa_export(g_pool, NULL, B_TRUE, B_FALSE) != 0) {
1019 		fatal(NULL, FTAG, "pool export failed; "
1020 		    "changes may not be committed to disk\n");
1021 	}
1022 
1023 	kernel_fini();
1024 
1025 	return (rv);
1026 }
1027