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