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 }