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