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 import_pool(const char *target, boolean_t readonly) 125 { 126 nvlist_t *config; 127 nvlist_t *pools; 128 int error; 129 char *sepp; 130 spa_t *spa; 131 nvpair_t *elem; 132 nvlist_t *props; 133 const char *name; 134 135 kernel_init(readonly ? FREAD : (FREAD | FWRITE)); 136 g_zfs = libzfs_init(); 137 ASSERT(g_zfs != NULL); 138 139 dmu_objset_register_type(DMU_OST_ZFS, space_delta_cb); 140 141 g_readonly = readonly; 142 143 /* 144 * If we only want readonly access, it's OK if we find 145 * a potentially-active (ie, imported into the kernel) pool from the 146 * default cachefile. 147 */ 148 if (readonly && spa_open(target, &spa, FTAG) == 0) { 149 spa_close(spa, FTAG); 150 return; 151 } 152 153 g_importargs.unique = B_TRUE; 154 g_importargs.can_be_active = readonly; 155 g_pool = strdup(target); 156 if ((sepp = strpbrk(g_pool, "/@")) != NULL) 157 *sepp = '\0'; 158 g_importargs.poolname = g_pool; 159 pools = zpool_search_import(g_zfs, &g_importargs); 160 161 if (nvlist_empty(pools)) { 162 if (!g_importargs.can_be_active) { 163 g_importargs.can_be_active = B_TRUE; 164 if (zpool_search_import(g_zfs, &g_importargs) != NULL || 165 spa_open(target, &spa, FTAG) == 0) { 166 fatal(spa, FTAG, "cannot import '%s': pool is " 167 "active; run " "\"zpool export %s\" " 168 "first\n", g_pool, g_pool); 169 } 170 } 171 172 fatal(NULL, FTAG, "cannot import '%s': no such pool " 173 "available\n", g_pool); 174 } 175 176 elem = nvlist_next_nvpair(pools, NULL); 177 name = nvpair_name(elem); 178 verify(nvpair_value_nvlist(elem, &config) == 0); 179 180 props = NULL; 181 if (readonly) { 182 verify(nvlist_alloc(&props, NV_UNIQUE_NAME, 0) == 0); 183 verify(nvlist_add_uint64(props, 184 zpool_prop_to_name(ZPOOL_PROP_READONLY), 1) == 0); 185 } 186 187 zfeature_checks_disable = B_TRUE; 188 error = spa_import(name, config, props, ZFS_IMPORT_NORMAL); 189 zfeature_checks_disable = B_FALSE; 190 if (error == EEXIST) 191 error = 0; 192 193 if (error) 194 fatal(NULL, FTAG, "can't import '%s': %s", name, 195 strerror(error)); 196 } 197 198 static void 199 zhack_spa_open(const char *target, boolean_t readonly, void *tag, spa_t **spa) 200 { 201 int err; 202 203 import_pool(target, readonly); 204 205 zfeature_checks_disable = B_TRUE; 206 err = spa_open(target, spa, tag); 207 zfeature_checks_disable = B_FALSE; 208 209 if (err != 0) 210 fatal(*spa, FTAG, "cannot open '%s': %s", target, 211 strerror(err)); 212 if (spa_version(*spa) < SPA_VERSION_FEATURES) { 213 fatal(*spa, FTAG, "'%s' has version %d, features not enabled", 214 target, (int)spa_version(*spa)); 215 } 216 } 217 218 static void 219 dump_obj(objset_t *os, uint64_t obj, const char *name) 220 { 221 zap_cursor_t zc; 222 zap_attribute_t za; 223 224 (void) printf("%s_obj:\n", name); 225 226 for (zap_cursor_init(&zc, os, obj); 227 zap_cursor_retrieve(&zc, &za) == 0; 228 zap_cursor_advance(&zc)) { 229 if (za.za_integer_length == 8) { 230 ASSERT(za.za_num_integers == 1); 231 (void) printf("\t%s = %llu\n", 232 za.za_name, (u_longlong_t)za.za_first_integer); 233 } else { 234 ASSERT(za.za_integer_length == 1); 235 char val[1024]; 236 VERIFY(zap_lookup(os, obj, za.za_name, 237 1, sizeof (val), val) == 0); 238 (void) printf("\t%s = %s\n", za.za_name, val); 239 } 240 } 241 zap_cursor_fini(&zc); 242 } 243 244 static void 245 dump_mos(spa_t *spa) 246 { 247 nvlist_t *nv = spa->spa_label_features; 248 249 (void) printf("label config:\n"); 250 for (nvpair_t *pair = nvlist_next_nvpair(nv, NULL); 251 pair != NULL; 252 pair = nvlist_next_nvpair(nv, pair)) { 253 (void) printf("\t%s\n", nvpair_name(pair)); 254 } 255 } 256 257 static void 258 zhack_do_feature_stat(int argc, char **argv) 259 { 260 spa_t *spa; 261 objset_t *os; 262 char *target; 263 264 argc--; 265 argv++; 266 267 if (argc < 1) { 268 (void) fprintf(stderr, "error: missing pool name\n"); 269 usage(); 270 } 271 target = argv[0]; 272 273 zhack_spa_open(target, B_TRUE, FTAG, &spa); 274 os = spa->spa_meta_objset; 275 276 dump_obj(os, spa->spa_feat_for_read_obj, "for_read"); 277 dump_obj(os, spa->spa_feat_for_write_obj, "for_write"); 278 dump_obj(os, spa->spa_feat_desc_obj, "descriptions"); 279 if (spa_feature_is_active(spa, SPA_FEATURE_ENABLED_TXG)) { 280 dump_obj(os, spa->spa_feat_enabled_txg_obj, "enabled_txg"); 281 } 282 dump_mos(spa); 283 284 spa_close(spa, FTAG); 285 } 286 287 static void 288 zhack_feature_enable_sync(void *arg, dmu_tx_t *tx) 289 { 290 spa_t *spa = dmu_tx_pool(tx)->dp_spa; 291 zfeature_info_t *feature = arg; 292 293 feature_enable_sync(spa, feature, tx); 294 295 spa_history_log_internal(spa, "zhack enable feature", tx, 296 "guid=%s flags=%x", 297 feature->fi_guid, feature->fi_flags); 298 } 299 300 static void 301 zhack_do_feature_enable(int argc, char **argv) 302 { 303 char c; 304 char *desc, *target; 305 spa_t *spa; 306 objset_t *mos; 307 zfeature_info_t feature; 308 spa_feature_t nodeps[] = { SPA_FEATURE_NONE }; 309 310 /* 311 * Features are not added to the pool's label until their refcounts 312 * are incremented, so fi_mos can just be left as false for now. 313 */ 314 desc = NULL; 315 feature.fi_uname = "zhack"; 316 feature.fi_flags = 0; 317 feature.fi_depends = nodeps; 318 feature.fi_feature = SPA_FEATURE_NONE; 319 320 optind = 1; 321 while ((c = getopt(argc, argv, "rmd:")) != -1) { 322 switch (c) { 323 case 'r': 324 feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT; 325 break; 326 case 'd': 327 desc = strdup(optarg); 328 break; 329 default: 330 usage(); 331 break; 332 } 333 } 334 335 if (desc == NULL) 336 desc = strdup("zhack injected"); 337 feature.fi_desc = desc; 338 339 argc -= optind; 340 argv += optind; 341 342 if (argc < 2) { 343 (void) fprintf(stderr, "error: missing feature or pool name\n"); 344 usage(); 345 } 346 target = argv[0]; 347 feature.fi_guid = argv[1]; 348 349 if (!zfeature_is_valid_guid(feature.fi_guid)) 350 fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid); 351 352 zhack_spa_open(target, B_FALSE, FTAG, &spa); 353 mos = spa->spa_meta_objset; 354 355 if (zfeature_is_supported(feature.fi_guid)) 356 fatal(spa, FTAG, "'%s' is a real feature, will not enable"); 357 if (0 == zap_contains(mos, spa->spa_feat_desc_obj, feature.fi_guid)) 358 fatal(spa, FTAG, "feature already enabled: %s", 359 feature.fi_guid); 360 361 VERIFY0(dsl_sync_task(spa_name(spa), NULL, 362 zhack_feature_enable_sync, &feature, 5, ZFS_SPACE_CHECK_NORMAL)); 363 364 spa_close(spa, FTAG); 365 366 free(desc); 367 } 368 369 static void 370 feature_incr_sync(void *arg, dmu_tx_t *tx) 371 { 372 spa_t *spa = dmu_tx_pool(tx)->dp_spa; 373 zfeature_info_t *feature = arg; 374 uint64_t refcount; 375 376 VERIFY0(feature_get_refcount_from_disk(spa, feature, &refcount)); 377 feature_sync(spa, feature, refcount + 1, tx); 378 spa_history_log_internal(spa, "zhack feature incr", tx, 379 "guid=%s", feature->fi_guid); 380 } 381 382 static void 383 feature_decr_sync(void *arg, dmu_tx_t *tx) 384 { 385 spa_t *spa = dmu_tx_pool(tx)->dp_spa; 386 zfeature_info_t *feature = arg; 387 uint64_t refcount; 388 389 VERIFY0(feature_get_refcount_from_disk(spa, feature, &refcount)); 390 feature_sync(spa, feature, refcount - 1, tx); 391 spa_history_log_internal(spa, "zhack feature decr", tx, 392 "guid=%s", feature->fi_guid); 393 } 394 395 static void 396 zhack_do_feature_ref(int argc, char **argv) 397 { 398 char c; 399 char *target; 400 boolean_t decr = B_FALSE; 401 spa_t *spa; 402 objset_t *mos; 403 zfeature_info_t feature; 404 spa_feature_t nodeps[] = { SPA_FEATURE_NONE }; 405 406 /* 407 * fi_desc does not matter here because it was written to disk 408 * when the feature was enabled, but we need to properly set the 409 * feature for read or write based on the information we read off 410 * disk later. 411 */ 412 feature.fi_uname = "zhack"; 413 feature.fi_flags = 0; 414 feature.fi_desc = NULL; 415 feature.fi_depends = nodeps; 416 feature.fi_feature = SPA_FEATURE_NONE; 417 418 optind = 1; 419 while ((c = getopt(argc, argv, "md")) != -1) { 420 switch (c) { 421 case 'm': 422 feature.fi_flags |= ZFEATURE_FLAG_MOS; 423 break; 424 case 'd': 425 decr = B_TRUE; 426 break; 427 default: 428 usage(); 429 break; 430 } 431 } 432 argc -= optind; 433 argv += optind; 434 435 if (argc < 2) { 436 (void) fprintf(stderr, "error: missing feature or pool name\n"); 437 usage(); 438 } 439 target = argv[0]; 440 feature.fi_guid = argv[1]; 441 442 if (!zfeature_is_valid_guid(feature.fi_guid)) 443 fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid); 444 445 zhack_spa_open(target, B_FALSE, FTAG, &spa); 446 mos = spa->spa_meta_objset; 447 448 if (zfeature_is_supported(feature.fi_guid)) { 449 fatal(spa, FTAG, 450 "'%s' is a real feature, will not change refcount"); 451 } 452 453 if (0 == zap_contains(mos, spa->spa_feat_for_read_obj, 454 feature.fi_guid)) { 455 feature.fi_flags &= ~ZFEATURE_FLAG_READONLY_COMPAT; 456 } else if (0 == zap_contains(mos, spa->spa_feat_for_write_obj, 457 feature.fi_guid)) { 458 feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT; 459 } else { 460 fatal(spa, FTAG, "feature is not enabled: %s", feature.fi_guid); 461 } 462 463 if (decr) { 464 uint64_t count; 465 if (feature_get_refcount_from_disk(spa, &feature, 466 &count) == 0 && count != 0) { 467 fatal(spa, FTAG, "feature refcount already 0: %s", 468 feature.fi_guid); 469 } 470 } 471 472 VERIFY0(dsl_sync_task(spa_name(spa), NULL, 473 decr ? feature_decr_sync : feature_incr_sync, &feature, 474 5, ZFS_SPACE_CHECK_NORMAL)); 475 476 spa_close(spa, FTAG); 477 } 478 479 static int 480 zhack_do_feature(int argc, char **argv) 481 { 482 char *subcommand; 483 484 argc--; 485 argv++; 486 if (argc == 0) { 487 (void) fprintf(stderr, 488 "error: no feature operation specified\n"); 489 usage(); 490 } 491 492 subcommand = argv[0]; 493 if (strcmp(subcommand, "stat") == 0) { 494 zhack_do_feature_stat(argc, argv); 495 } else if (strcmp(subcommand, "enable") == 0) { 496 zhack_do_feature_enable(argc, argv); 497 } else if (strcmp(subcommand, "ref") == 0) { 498 zhack_do_feature_ref(argc, argv); 499 } else { 500 (void) fprintf(stderr, "error: unknown subcommand: %s\n", 501 subcommand); 502 usage(); 503 } 504 505 return (0); 506 } 507 508 #define MAX_NUM_PATHS 1024 509 510 int 511 main(int argc, char **argv) 512 { 513 extern void zfs_prop_init(void); 514 515 char *path[MAX_NUM_PATHS]; 516 const char *subcommand; 517 int rv = 0; 518 char c; 519 520 g_importargs.path = path; 521 522 dprintf_setup(&argc, argv); 523 zfs_prop_init(); 524 525 while ((c = getopt(argc, argv, "c:d:")) != -1) { 526 switch (c) { 527 case 'c': 528 g_importargs.cachefile = optarg; 529 break; 530 case 'd': 531 assert(g_importargs.paths < MAX_NUM_PATHS); 532 g_importargs.path[g_importargs.paths++] = optarg; 533 break; 534 default: 535 usage(); 536 break; 537 } 538 } 539 540 argc -= optind; 541 argv += optind; 542 optind = 1; 543 544 if (argc == 0) { 545 (void) fprintf(stderr, "error: no command specified\n"); 546 usage(); 547 } 548 549 subcommand = argv[0]; 550 551 if (strcmp(subcommand, "feature") == 0) { 552 rv = zhack_do_feature(argc, argv); 553 } else { 554 (void) fprintf(stderr, "error: unknown subcommand: %s\n", 555 subcommand); 556 usage(); 557 } 558 559 if (!g_readonly && spa_export(g_pool, NULL, B_TRUE, B_FALSE) != 0) { 560 fatal(NULL, FTAG, "pool export failed; " 561 "changes may not be committed to disk\n"); 562 } 563 564 libzfs_fini(g_zfs); 565 kernel_fini(); 566 567 return (rv); 568 } 569