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 spa_history_log_internal(spa, "zhack enable feature", tx, 283 "guid=%s can_readonly=%u", 284 feature->fi_guid, feature->fi_can_readonly); 285 } 286 287 static void 288 zhack_do_feature_enable(int argc, char **argv) 289 { 290 char c; 291 char *desc, *target; 292 spa_t *spa; 293 objset_t *mos; 294 zfeature_info_t feature; 295 zfeature_info_t *nodeps[] = { NULL }; 296 297 /* 298 * Features are not added to the pool's label until their refcounts 299 * are incremented, so fi_mos can just be left as false for now. 300 */ 301 desc = NULL; 302 feature.fi_uname = "zhack"; 303 feature.fi_mos = B_FALSE; 304 feature.fi_can_readonly = B_FALSE; 305 feature.fi_depends = nodeps; 306 307 optind = 1; 308 while ((c = getopt(argc, argv, "rmd:")) != -1) { 309 switch (c) { 310 case 'r': 311 feature.fi_can_readonly = B_TRUE; 312 break; 313 case 'd': 314 desc = strdup(optarg); 315 break; 316 default: 317 usage(); 318 break; 319 } 320 } 321 322 if (desc == NULL) 323 desc = strdup("zhack injected"); 324 feature.fi_desc = desc; 325 326 argc -= optind; 327 argv += optind; 328 329 if (argc < 2) { 330 (void) fprintf(stderr, "error: missing feature or pool name\n"); 331 usage(); 332 } 333 target = argv[0]; 334 feature.fi_guid = argv[1]; 335 336 if (!zfeature_is_valid_guid(feature.fi_guid)) 337 fatal("invalid feature guid: %s", feature.fi_guid); 338 339 zhack_spa_open(target, B_FALSE, FTAG, &spa); 340 mos = spa->spa_meta_objset; 341 342 if (0 == zfeature_lookup_guid(feature.fi_guid, NULL)) 343 fatal("'%s' is a real feature, will not enable"); 344 if (0 == zap_contains(mos, spa->spa_feat_desc_obj, feature.fi_guid)) 345 fatal("feature already enabled: %s", feature.fi_guid); 346 347 VERIFY3U(0, ==, dsl_sync_task_do(spa->spa_dsl_pool, NULL, 348 feature_enable_sync, spa, &feature, 5)); 349 350 spa_close(spa, FTAG); 351 352 free(desc); 353 } 354 355 static void 356 feature_incr_sync(void *arg1, void *arg2, dmu_tx_t *tx) 357 { 358 spa_t *spa = arg1; 359 zfeature_info_t *feature = arg2; 360 361 spa_feature_incr(spa, feature, tx); 362 spa_history_log_internal(spa, "zhack feature incr", tx, 363 "guid=%s", feature->fi_guid); 364 } 365 366 static void 367 feature_decr_sync(void *arg1, void *arg2, dmu_tx_t *tx) 368 { 369 spa_t *spa = arg1; 370 zfeature_info_t *feature = arg2; 371 372 spa_feature_decr(spa, feature, tx); 373 spa_history_log_internal(spa, "zhack feature decr", tx, 374 "guid=%s", feature->fi_guid); 375 } 376 377 static void 378 zhack_do_feature_ref(int argc, char **argv) 379 { 380 char c; 381 char *target; 382 boolean_t decr = B_FALSE; 383 spa_t *spa; 384 objset_t *mos; 385 zfeature_info_t feature; 386 zfeature_info_t *nodeps[] = { NULL }; 387 388 /* 389 * fi_desc does not matter here because it was written to disk 390 * when the feature was enabled, but we need to properly set the 391 * feature for read or write based on the information we read off 392 * disk later. 393 */ 394 feature.fi_uname = "zhack"; 395 feature.fi_mos = B_FALSE; 396 feature.fi_desc = NULL; 397 feature.fi_depends = nodeps; 398 399 optind = 1; 400 while ((c = getopt(argc, argv, "md")) != -1) { 401 switch (c) { 402 case 'm': 403 feature.fi_mos = B_TRUE; 404 break; 405 case 'd': 406 decr = B_TRUE; 407 break; 408 default: 409 usage(); 410 break; 411 } 412 } 413 argc -= optind; 414 argv += optind; 415 416 if (argc < 2) { 417 (void) fprintf(stderr, "error: missing feature or pool name\n"); 418 usage(); 419 } 420 target = argv[0]; 421 feature.fi_guid = argv[1]; 422 423 if (!zfeature_is_valid_guid(feature.fi_guid)) 424 fatal("invalid feature guid: %s", feature.fi_guid); 425 426 zhack_spa_open(target, B_FALSE, FTAG, &spa); 427 mos = spa->spa_meta_objset; 428 429 if (0 == zfeature_lookup_guid(feature.fi_guid, NULL)) 430 fatal("'%s' is a real feature, will not change refcount"); 431 432 if (0 == zap_contains(mos, spa->spa_feat_for_read_obj, 433 feature.fi_guid)) { 434 feature.fi_can_readonly = B_FALSE; 435 } else if (0 == zap_contains(mos, spa->spa_feat_for_write_obj, 436 feature.fi_guid)) { 437 feature.fi_can_readonly = B_TRUE; 438 } else { 439 fatal("feature is not enabled: %s", feature.fi_guid); 440 } 441 442 if (decr && !spa_feature_is_active(spa, &feature)) 443 fatal("feature refcount already 0: %s", feature.fi_guid); 444 445 VERIFY3U(0, ==, dsl_sync_task_do(spa->spa_dsl_pool, NULL, 446 decr ? feature_decr_sync : feature_incr_sync, spa, &feature, 5)); 447 448 spa_close(spa, FTAG); 449 } 450 451 static int 452 zhack_do_feature(int argc, char **argv) 453 { 454 char *subcommand; 455 456 argc--; 457 argv++; 458 if (argc == 0) { 459 (void) fprintf(stderr, 460 "error: no feature operation specified\n"); 461 usage(); 462 } 463 464 subcommand = argv[0]; 465 if (strcmp(subcommand, "stat") == 0) { 466 zhack_do_feature_stat(argc, argv); 467 } else if (strcmp(subcommand, "enable") == 0) { 468 zhack_do_feature_enable(argc, argv); 469 } else if (strcmp(subcommand, "ref") == 0) { 470 zhack_do_feature_ref(argc, argv); 471 } else { 472 (void) fprintf(stderr, "error: unknown subcommand: %s\n", 473 subcommand); 474 usage(); 475 } 476 477 return (0); 478 } 479 480 #define MAX_NUM_PATHS 1024 481 482 int 483 main(int argc, char **argv) 484 { 485 extern void zfs_prop_init(void); 486 487 char *path[MAX_NUM_PATHS]; 488 const char *subcommand; 489 int rv = 0; 490 char c; 491 492 g_importargs.path = path; 493 494 dprintf_setup(&argc, argv); 495 zfs_prop_init(); 496 497 while ((c = getopt(argc, argv, "c:d:")) != -1) { 498 switch (c) { 499 case 'c': 500 g_importargs.cachefile = optarg; 501 break; 502 case 'd': 503 assert(g_importargs.paths < MAX_NUM_PATHS); 504 g_importargs.path[g_importargs.paths++] = optarg; 505 break; 506 default: 507 usage(); 508 break; 509 } 510 } 511 512 argc -= optind; 513 argv += optind; 514 optind = 1; 515 516 if (argc == 0) { 517 (void) fprintf(stderr, "error: no command specified\n"); 518 usage(); 519 } 520 521 subcommand = argv[0]; 522 523 if (strcmp(subcommand, "feature") == 0) { 524 rv = zhack_do_feature(argc, argv); 525 } else { 526 (void) fprintf(stderr, "error: unknown subcommand: %s\n", 527 subcommand); 528 usage(); 529 } 530 531 if (!g_readonly && spa_export(g_pool, NULL, B_TRUE, B_TRUE) != 0) { 532 fatal("pool export failed; " 533 "changes may not be committed to disk\n"); 534 } 535 536 libzfs_fini(g_zfs); 537 kernel_fini(); 538 539 return (rv); 540 } 541