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