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