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