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