]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - cmd/zhack.c
Increase limit of redaction list by using spill block
[FreeBSD/FreeBSD.git] / cmd / zhack.c
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 }