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