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