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 }