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