Merge ACPICA 20200326.
[freebsd.git] / cddl / contrib / opensolaris / cmd / zhack / 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/zfs_context.h>
37 #include <sys/spa.h>
38 #include <sys/spa_impl.h>
39 #include <sys/dmu.h>
40 #include <sys/zap.h>
41 #include <sys/zfs_znode.h>
42 #include <sys/dsl_synctask.h>
43 #include <sys/vdev.h>
44 #include <sys/fs/zfs.h>
45 #include <sys/dmu_objset.h>
46 #include <sys/dsl_pool.h>
47 #include <sys/zio_checksum.h>
48 #include <sys/zio_compress.h>
49 #include <sys/zfeature.h>
50 #include <sys/dmu_tx.h>
51 #undef verify
52 #include <libzfs.h>
53
54 extern boolean_t zfeature_checks_disable;
55
56 const char cmdname[] = "zhack";
57 libzfs_handle_t *g_zfs;
58 static importargs_t g_importargs;
59 static char *g_pool;
60 static boolean_t g_readonly;
61
62 static void
63 usage(void)
64 {
65         (void) fprintf(stderr,
66             "Usage: %s [-c cachefile] [-d dir] <subcommand> <args> ...\n"
67             "where <subcommand> <args> is one of the following:\n"
68             "\n", cmdname);
69
70         (void) fprintf(stderr,
71             "    feature stat <pool>\n"
72             "        print information about enabled features\n"
73             "    feature enable [-d desc] <pool> <feature>\n"
74             "        add a new enabled feature to the pool\n"
75             "        -d <desc> sets the feature's description\n"
76             "    feature ref [-md] <pool> <feature>\n"
77             "        change the refcount on the given feature\n"
78             "        -d decrease instead of increase the refcount\n"
79             "        -m add the feature to the label if increasing refcount\n"
80             "\n"
81             "    <feature> : should be a feature guid\n");
82         exit(1);
83 }
84
85
86 static void
87 fatal(spa_t *spa, void *tag, const char *fmt, ...)
88 {
89         va_list ap;
90
91         if (spa != NULL) {
92                 spa_close(spa, tag);
93                 (void) spa_export(g_pool, NULL, B_TRUE, B_FALSE);
94         }
95
96         va_start(ap, fmt);
97         (void) fprintf(stderr, "%s: ", cmdname);
98         (void) vfprintf(stderr, fmt, ap);
99         va_end(ap);
100         (void) fprintf(stderr, "\n");
101
102         exit(1);
103 }
104
105 /* ARGSUSED */
106 static int
107 space_delta_cb(dmu_object_type_t bonustype, void *data,
108     uint64_t *userp, uint64_t *groupp)
109 {
110         /*
111          * Is it a valid type of object to track?
112          */
113         if (bonustype != DMU_OT_ZNODE && bonustype != DMU_OT_SA)
114                 return (ENOENT);
115         (void) fprintf(stderr, "modifying object that needs user accounting");
116         abort();
117         /* NOTREACHED */
118 }
119
120 /*
121  * Target is the dataset whose pool we want to open.
122  */
123 static void
124 zhack_import(char *target, boolean_t readonly)
125 {
126         nvlist_t *config;
127         nvlist_t *props;
128         int error;
129
130         kernel_init(readonly ? FREAD : (FREAD | FWRITE));
131         g_zfs = libzfs_init();
132         ASSERT(g_zfs != NULL);
133
134         dmu_objset_register_type(DMU_OST_ZFS, space_delta_cb);
135
136         g_readonly = readonly;
137         g_importargs.unique = B_TRUE;
138         g_importargs.can_be_active = readonly;
139         g_pool = strdup(target);
140
141         error = zpool_tryimport(g_zfs, target, &config, &g_importargs);
142         if (error)
143                 fatal(NULL, FTAG, "cannot import '%s': %s", target,
144                     libzfs_error_description(g_zfs));
145
146         props = NULL;
147         if (readonly) {
148                 VERIFY(nvlist_alloc(&props, NV_UNIQUE_NAME, 0) == 0);
149                 VERIFY(nvlist_add_uint64(props,
150                     zpool_prop_to_name(ZPOOL_PROP_READONLY), 1) == 0);
151         }
152
153         zfeature_checks_disable = B_TRUE;
154         error = spa_import(target, config, props,
155             (readonly ?  ZFS_IMPORT_SKIP_MMP : ZFS_IMPORT_NORMAL));
156         zfeature_checks_disable = B_FALSE;
157         if (error == EEXIST)
158                 error = 0;
159
160         if (error)
161                 fatal(NULL, FTAG, "can't import '%s': %s", target,
162                     strerror(error));
163 }
164
165 static void
166 zhack_spa_open(char *target, boolean_t readonly, void *tag, spa_t **spa)
167 {
168         int err;
169
170         zhack_import(target, readonly);
171
172         zfeature_checks_disable = B_TRUE;
173         err = spa_open(target, spa, tag);
174         zfeature_checks_disable = B_FALSE;
175
176         if (err != 0)
177                 fatal(*spa, FTAG, "cannot open '%s': %s", target,
178                     strerror(err));
179         if (spa_version(*spa) < SPA_VERSION_FEATURES) {
180                 fatal(*spa, FTAG, "'%s' has version %d, features not enabled",
181                     target, (int)spa_version(*spa));
182         }
183 }
184
185 static void
186 dump_obj(objset_t *os, uint64_t obj, const char *name)
187 {
188         zap_cursor_t zc;
189         zap_attribute_t za;
190
191         (void) printf("%s_obj:\n", name);
192
193         for (zap_cursor_init(&zc, os, obj);
194             zap_cursor_retrieve(&zc, &za) == 0;
195             zap_cursor_advance(&zc)) {
196                 if (za.za_integer_length == 8) {
197                         ASSERT(za.za_num_integers == 1);
198                         (void) printf("\t%s = %llu\n",
199                             za.za_name, (u_longlong_t)za.za_first_integer);
200                 } else {
201                         ASSERT(za.za_integer_length == 1);
202                         char val[1024];
203                         VERIFY(zap_lookup(os, obj, za.za_name,
204                             1, sizeof (val), val) == 0);
205                         (void) printf("\t%s = %s\n", za.za_name, val);
206                 }
207         }
208         zap_cursor_fini(&zc);
209 }
210
211 static void
212 dump_mos(spa_t *spa)
213 {
214         nvlist_t *nv = spa->spa_label_features;
215
216         (void) printf("label config:\n");
217         for (nvpair_t *pair = nvlist_next_nvpair(nv, NULL);
218             pair != NULL;
219             pair = nvlist_next_nvpair(nv, pair)) {
220                 (void) printf("\t%s\n", nvpair_name(pair));
221         }
222 }
223
224 static void
225 zhack_do_feature_stat(int argc, char **argv)
226 {
227         spa_t *spa;
228         objset_t *os;
229         char *target;
230
231         argc--;
232         argv++;
233
234         if (argc < 1) {
235                 (void) fprintf(stderr, "error: missing pool name\n");
236                 usage();
237         }
238         target = argv[0];
239
240         zhack_spa_open(target, B_TRUE, FTAG, &spa);
241         os = spa->spa_meta_objset;
242
243         dump_obj(os, spa->spa_feat_for_read_obj, "for_read");
244         dump_obj(os, spa->spa_feat_for_write_obj, "for_write");
245         dump_obj(os, spa->spa_feat_desc_obj, "descriptions");
246         if (spa_feature_is_active(spa, SPA_FEATURE_ENABLED_TXG)) {
247                 dump_obj(os, spa->spa_feat_enabled_txg_obj, "enabled_txg");
248         }
249         dump_mos(spa);
250
251         spa_close(spa, FTAG);
252 }
253
254 static void
255 zhack_feature_enable_sync(void *arg, dmu_tx_t *tx)
256 {
257         spa_t *spa = dmu_tx_pool(tx)->dp_spa;
258         zfeature_info_t *feature = arg;
259
260         feature_enable_sync(spa, feature, tx);
261
262         spa_history_log_internal(spa, "zhack enable feature", tx,
263             "guid=%s flags=%x",
264             feature->fi_guid, feature->fi_flags);
265 }
266
267 static void
268 zhack_do_feature_enable(int argc, char **argv)
269 {
270         char c;
271         char *desc, *target;
272         spa_t *spa;
273         objset_t *mos;
274         zfeature_info_t feature;
275         spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
276
277         /*
278          * Features are not added to the pool's label until their refcounts
279          * are incremented, so fi_mos can just be left as false for now.
280          */
281         desc = NULL;
282         feature.fi_uname = "zhack";
283         feature.fi_flags = 0;
284         feature.fi_depends = nodeps;
285         feature.fi_feature = SPA_FEATURE_NONE;
286
287         optind = 1;
288         while ((c = getopt(argc, argv, "rmd:")) != -1) {
289                 switch (c) {
290                 case 'r':
291                         feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT;
292                         break;
293                 case 'd':
294                         desc = strdup(optarg);
295                         break;
296                 default:
297                         usage();
298                         break;
299                 }
300         }
301
302         if (desc == NULL)
303                 desc = strdup("zhack injected");
304         feature.fi_desc = desc;
305
306         argc -= optind;
307         argv += optind;
308
309         if (argc < 2) {
310                 (void) fprintf(stderr, "error: missing feature or pool name\n");
311                 usage();
312         }
313         target = argv[0];
314         feature.fi_guid = argv[1];
315
316         if (!zfeature_is_valid_guid(feature.fi_guid))
317                 fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid);
318
319         zhack_spa_open(target, B_FALSE, FTAG, &spa);
320         mos = spa->spa_meta_objset;
321
322         if (zfeature_is_supported(feature.fi_guid))
323                 fatal(spa, FTAG, "'%s' is a real feature, will not enable");
324         if (0 == zap_contains(mos, spa->spa_feat_desc_obj, feature.fi_guid))
325                 fatal(spa, FTAG, "feature already enabled: %s",
326                     feature.fi_guid);
327
328         VERIFY0(dsl_sync_task(spa_name(spa), NULL,
329             zhack_feature_enable_sync, &feature, 5, ZFS_SPACE_CHECK_NORMAL));
330
331         spa_close(spa, FTAG);
332
333         free(desc);
334 }
335
336 static void
337 feature_incr_sync(void *arg, dmu_tx_t *tx)
338 {
339         spa_t *spa = dmu_tx_pool(tx)->dp_spa;
340         zfeature_info_t *feature = arg;
341         uint64_t refcount;
342
343         VERIFY0(feature_get_refcount_from_disk(spa, feature, &refcount));
344         feature_sync(spa, feature, refcount + 1, tx);
345         spa_history_log_internal(spa, "zhack feature incr", tx,
346             "name=%s", feature->fi_guid);
347 }
348
349 static void
350 feature_decr_sync(void *arg, dmu_tx_t *tx)
351 {
352         spa_t *spa = dmu_tx_pool(tx)->dp_spa;
353         zfeature_info_t *feature = arg;
354         uint64_t refcount;
355
356         VERIFY0(feature_get_refcount_from_disk(spa, feature, &refcount));
357         feature_sync(spa, feature, refcount - 1, tx);
358         spa_history_log_internal(spa, "zhack feature decr", tx,
359             "name=%s", feature->fi_guid);
360 }
361
362 static void
363 zhack_do_feature_ref(int argc, char **argv)
364 {
365         char c;
366         char *target;
367         boolean_t decr = B_FALSE;
368         spa_t *spa;
369         objset_t *mos;
370         zfeature_info_t feature;
371         spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
372
373         /*
374          * fi_desc does not matter here because it was written to disk
375          * when the feature was enabled, but we need to properly set the
376          * feature for read or write based on the information we read off
377          * disk later.
378          */
379         feature.fi_uname = "zhack";
380         feature.fi_flags = 0;
381         feature.fi_desc = NULL;
382         feature.fi_depends = nodeps;
383         feature.fi_feature = SPA_FEATURE_NONE;
384
385         optind = 1;
386         while ((c = getopt(argc, argv, "md")) != -1) {
387                 switch (c) {
388                 case 'm':
389                         feature.fi_flags |= ZFEATURE_FLAG_MOS;
390                         break;
391                 case 'd':
392                         decr = B_TRUE;
393                         break;
394                 default:
395                         usage();
396                         break;
397                 }
398         }
399         argc -= optind;
400         argv += optind;
401
402         if (argc < 2) {
403                 (void) fprintf(stderr, "error: missing feature or pool name\n");
404                 usage();
405         }
406         target = argv[0];
407         feature.fi_guid = argv[1];
408
409         if (!zfeature_is_valid_guid(feature.fi_guid))
410                 fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid);
411
412         zhack_spa_open(target, B_FALSE, FTAG, &spa);
413         mos = spa->spa_meta_objset;
414
415         if (zfeature_is_supported(feature.fi_guid)) {
416                 fatal(spa, FTAG,
417                     "'%s' is a real feature, will not change refcount");
418         }
419
420         if (0 == zap_contains(mos, spa->spa_feat_for_read_obj,
421             feature.fi_guid)) {
422                 feature.fi_flags &= ~ZFEATURE_FLAG_READONLY_COMPAT;
423         } else if (0 == zap_contains(mos, spa->spa_feat_for_write_obj,
424             feature.fi_guid)) {
425                 feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT;
426         } else {
427                 fatal(spa, FTAG, "feature is not enabled: %s", feature.fi_guid);
428         }
429
430         if (decr) {
431                 uint64_t count;
432                 if (feature_get_refcount_from_disk(spa, &feature,
433                     &count) == 0 && count != 0) {
434                         fatal(spa, FTAG, "feature refcount already 0: %s",
435                             feature.fi_guid);
436                 }
437         }
438
439         VERIFY0(dsl_sync_task(spa_name(spa), NULL,
440             decr ? feature_decr_sync : feature_incr_sync, &feature,
441             5, ZFS_SPACE_CHECK_NORMAL));
442
443         spa_close(spa, FTAG);
444 }
445
446 static int
447 zhack_do_feature(int argc, char **argv)
448 {
449         char *subcommand;
450
451         argc--;
452         argv++;
453         if (argc == 0) {
454                 (void) fprintf(stderr,
455                     "error: no feature operation specified\n");
456                 usage();
457         }
458
459         subcommand = argv[0];
460         if (strcmp(subcommand, "stat") == 0) {
461                 zhack_do_feature_stat(argc, argv);
462         } else if (strcmp(subcommand, "enable") == 0) {
463                 zhack_do_feature_enable(argc, argv);
464         } else if (strcmp(subcommand, "ref") == 0) {
465                 zhack_do_feature_ref(argc, argv);
466         } else {
467                 (void) fprintf(stderr, "error: unknown subcommand: %s\n",
468                     subcommand);
469                 usage();
470         }
471
472         return (0);
473 }
474
475 #define MAX_NUM_PATHS 1024
476
477 int
478 main(int argc, char **argv)
479 {
480         extern void zfs_prop_init(void);
481
482         char *path[MAX_NUM_PATHS];
483         const char *subcommand;
484         int rv = 0;
485         char c;
486
487         g_importargs.path = path;
488
489         dprintf_setup(&argc, argv);
490         zfs_prop_init();
491
492         while ((c = getopt(argc, argv, "c:d:")) != -1) {
493                 switch (c) {
494                 case 'c':
495                         g_importargs.cachefile = optarg;
496                         break;
497                 case 'd':
498                         assert(g_importargs.paths < MAX_NUM_PATHS);
499                         g_importargs.path[g_importargs.paths++] = optarg;
500                         break;
501                 default:
502                         usage();
503                         break;
504                 }
505         }
506
507         argc -= optind;
508         argv += optind;
509         optind = 1;
510
511         if (argc == 0) {
512                 (void) fprintf(stderr, "error: no command specified\n");
513                 usage();
514         }
515
516         subcommand = argv[0];
517
518         if (strcmp(subcommand, "feature") == 0) {
519                 rv = zhack_do_feature(argc, argv);
520         } else {
521                 (void) fprintf(stderr, "error: unknown subcommand: %s\n",
522                     subcommand);
523                 usage();
524         }
525
526         if (!g_readonly && spa_export(g_pool, NULL, B_TRUE, B_FALSE) != 0) {
527                 fatal(NULL, FTAG, "pool export failed; "
528                     "changes may not be committed to disk\n");
529         }
530
531         libzfs_fini(g_zfs);
532         kernel_fini();
533
534         return (rv);
535 }