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