*/
/*
- * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
* Copyright (c) 2013 Steven Hartland. All rights reserved.
*/
#include <sys/zio_compress.h>
#include <sys/zfeature.h>
#include <sys/dmu_tx.h>
-#undef ZFS_MAXNAMELEN
#include <libzfs.h>
extern boolean_t zfeature_checks_disable;
(void) fprintf(stderr,
" feature stat <pool>\n"
" print information about enabled features\n"
- " feature enable [-d desc] <pool> <feature>\n"
+ " feature enable [-r] [-d desc] <pool> <feature>\n"
" add a new enabled feature to the pool\n"
" -d <desc> sets the feature's description\n"
+ " -r set read-only compatible flag for feature\n"
" feature ref [-md] <pool> <feature>\n"
" change the refcount on the given feature\n"
" -d decrease instead of increase the refcount\n"
static void
-fatal(const char *fmt, ...)
+fatal(spa_t *spa, void *tag, const char *fmt, ...)
{
va_list ap;
+ if (spa != NULL) {
+ spa_close(spa, tag);
+ (void) spa_export(g_pool, NULL, B_TRUE, B_FALSE);
+ }
+
va_start(ap, fmt);
(void) fprintf(stderr, "%s: ", cmdname);
(void) vfprintf(stderr, fmt, ap);
* Target is the dataset whose pool we want to open.
*/
static void
-import_pool(const char *target, boolean_t readonly)
+zhack_import(char *target, boolean_t readonly)
{
nvlist_t *config;
- nvlist_t *pools;
- int error;
- char *sepp;
- spa_t *spa;
- nvpair_t *elem;
nvlist_t *props;
- char *name;
+ int error;
kernel_init(readonly ? FREAD : (FREAD | FWRITE));
g_zfs = libzfs_init();
dmu_objset_register_type(DMU_OST_ZFS, space_delta_cb);
g_readonly = readonly;
-
- /*
- * If we only want readonly access, it's OK if we find
- * a potentially-active (ie, imported into the kernel) pool from the
- * default cachefile.
- */
- if (readonly && spa_open(target, &spa, FTAG) == 0) {
- spa_close(spa, FTAG);
- return;
- }
-
g_importargs.unique = B_TRUE;
g_importargs.can_be_active = readonly;
g_pool = strdup(target);
- if ((sepp = strpbrk(g_pool, "/@")) != NULL)
- *sepp = '\0';
- g_importargs.poolname = g_pool;
- pools = zpool_search_import(g_zfs, &g_importargs);
-
- if (nvlist_empty(pools)) {
- if (!g_importargs.can_be_active) {
- g_importargs.can_be_active = B_TRUE;
- if (zpool_search_import(g_zfs, &g_importargs) != NULL ||
- spa_open(target, &spa, FTAG) == 0) {
- fatal("cannot import '%s': pool is active; run "
- "\"zpool export %s\" first\n",
- g_pool, g_pool);
- }
- }
-
- fatal("cannot import '%s': no such pool available\n", g_pool);
- }
- elem = nvlist_next_nvpair(pools, NULL);
- name = nvpair_name(elem);
- VERIFY(nvpair_value_nvlist(elem, &config) == 0);
+ error = zpool_tryimport(g_zfs, target, &config, &g_importargs);
+ if (error)
+ fatal(NULL, FTAG, "cannot import '%s': %s", target,
+ libzfs_error_description(g_zfs));
props = NULL;
if (readonly) {
}
zfeature_checks_disable = B_TRUE;
- error = spa_import(name, config, props, ZFS_IMPORT_NORMAL);
+ error = spa_import(target, config, props,
+ (readonly ? ZFS_IMPORT_SKIP_MMP : ZFS_IMPORT_NORMAL));
zfeature_checks_disable = B_FALSE;
if (error == EEXIST)
error = 0;
if (error)
- fatal("can't import '%s': %s", name, strerror(error));
+ fatal(NULL, FTAG, "can't import '%s': %s", target,
+ strerror(error));
}
static void
-zhack_spa_open(const char *target, boolean_t readonly, void *tag, spa_t **spa)
+zhack_spa_open(char *target, boolean_t readonly, void *tag, spa_t **spa)
{
int err;
- import_pool(target, readonly);
+ zhack_import(target, readonly);
zfeature_checks_disable = B_TRUE;
err = spa_open(target, spa, tag);
zfeature_checks_disable = B_FALSE;
if (err != 0)
- fatal("cannot open '%s': %s", target, strerror(err));
+ fatal(*spa, FTAG, "cannot open '%s': %s", target,
+ strerror(err));
if (spa_version(*spa) < SPA_VERSION_FEATURES) {
- fatal("'%s' has version %d, features not enabled", target,
- (int)spa_version(*spa));
+ fatal(*spa, FTAG, "'%s' has version %d, features not enabled",
+ target, (int)spa_version(*spa));
}
}
dump_obj(os, spa->spa_feat_for_read_obj, "for_read");
dump_obj(os, spa->spa_feat_for_write_obj, "for_write");
dump_obj(os, spa->spa_feat_desc_obj, "descriptions");
+ if (spa_feature_is_active(spa, SPA_FEATURE_ENABLED_TXG)) {
+ dump_obj(os, spa->spa_feat_enabled_txg_obj, "enabled_txg");
+ }
dump_mos(spa);
spa_close(spa, FTAG);
}
static void
-feature_enable_sync(void *arg, dmu_tx_t *tx)
+zhack_feature_enable_sync(void *arg, dmu_tx_t *tx)
{
spa_t *spa = dmu_tx_pool(tx)->dp_spa;
zfeature_info_t *feature = arg;
- spa_feature_enable(spa, feature, tx);
+ feature_enable_sync(spa, feature, tx);
+
spa_history_log_internal(spa, "zhack enable feature", tx,
- "name=%s can_readonly=%u",
- feature->fi_guid, feature->fi_can_readonly);
+ "name=%s flags=%u",
+ feature->fi_guid, feature->fi_flags);
}
static void
zhack_do_feature_enable(int argc, char **argv)
{
- char c;
+ int c;
char *desc, *target;
spa_t *spa;
objset_t *mos;
zfeature_info_t feature;
- zfeature_info_t *nodeps[] = { NULL };
+ spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
/*
* Features are not added to the pool's label until their refcounts
*/
desc = NULL;
feature.fi_uname = "zhack";
- feature.fi_mos = B_FALSE;
- feature.fi_can_readonly = B_FALSE;
+ feature.fi_flags = 0;
feature.fi_depends = nodeps;
+ feature.fi_feature = SPA_FEATURE_NONE;
optind = 1;
- while ((c = getopt(argc, argv, "rmd:")) != -1) {
+ while ((c = getopt(argc, argv, "+rd:")) != -1) {
switch (c) {
case 'r':
- feature.fi_can_readonly = B_TRUE;
+ feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT;
break;
case 'd':
desc = strdup(optarg);
feature.fi_guid = argv[1];
if (!zfeature_is_valid_guid(feature.fi_guid))
- fatal("invalid feature guid: %s", feature.fi_guid);
+ fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid);
zhack_spa_open(target, B_FALSE, FTAG, &spa);
mos = spa->spa_meta_objset;
- if (0 == zfeature_lookup_guid(feature.fi_guid, NULL))
- fatal("'%s' is a real feature, will not enable");
+ if (zfeature_is_supported(feature.fi_guid))
+ fatal(spa, FTAG, "'%s' is a real feature, will not enable");
if (0 == zap_contains(mos, spa->spa_feat_desc_obj, feature.fi_guid))
- fatal("feature already enabled: %s", feature.fi_guid);
+ fatal(spa, FTAG, "feature already enabled: %s",
+ feature.fi_guid);
VERIFY0(dsl_sync_task(spa_name(spa), NULL,
- feature_enable_sync, &feature, 5));
+ zhack_feature_enable_sync, &feature, 5, ZFS_SPACE_CHECK_NORMAL));
spa_close(spa, FTAG);
{
spa_t *spa = dmu_tx_pool(tx)->dp_spa;
zfeature_info_t *feature = arg;
+ uint64_t refcount;
- spa_feature_incr(spa, feature, tx);
+ VERIFY0(feature_get_refcount_from_disk(spa, feature, &refcount));
+ feature_sync(spa, feature, refcount + 1, tx);
spa_history_log_internal(spa, "zhack feature incr", tx,
"name=%s", feature->fi_guid);
}
{
spa_t *spa = dmu_tx_pool(tx)->dp_spa;
zfeature_info_t *feature = arg;
+ uint64_t refcount;
- spa_feature_decr(spa, feature, tx);
+ VERIFY0(feature_get_refcount_from_disk(spa, feature, &refcount));
+ feature_sync(spa, feature, refcount - 1, tx);
spa_history_log_internal(spa, "zhack feature decr", tx,
"name=%s", feature->fi_guid);
}
static void
zhack_do_feature_ref(int argc, char **argv)
{
- char c;
+ int c;
char *target;
boolean_t decr = B_FALSE;
spa_t *spa;
objset_t *mos;
zfeature_info_t feature;
- zfeature_info_t *nodeps[] = { NULL };
+ spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
/*
* fi_desc does not matter here because it was written to disk
* disk later.
*/
feature.fi_uname = "zhack";
- feature.fi_mos = B_FALSE;
+ feature.fi_flags = 0;
feature.fi_desc = NULL;
feature.fi_depends = nodeps;
+ feature.fi_feature = SPA_FEATURE_NONE;
optind = 1;
- while ((c = getopt(argc, argv, "md")) != -1) {
+ while ((c = getopt(argc, argv, "+md")) != -1) {
switch (c) {
case 'm':
- feature.fi_mos = B_TRUE;
+ feature.fi_flags |= ZFEATURE_FLAG_MOS;
break;
case 'd':
decr = B_TRUE;
feature.fi_guid = argv[1];
if (!zfeature_is_valid_guid(feature.fi_guid))
- fatal("invalid feature guid: %s", feature.fi_guid);
+ fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid);
zhack_spa_open(target, B_FALSE, FTAG, &spa);
mos = spa->spa_meta_objset;
- if (0 == zfeature_lookup_guid(feature.fi_guid, NULL))
- fatal("'%s' is a real feature, will not change refcount");
+ if (zfeature_is_supported(feature.fi_guid)) {
+ fatal(spa, FTAG,
+ "'%s' is a real feature, will not change refcount");
+ }
if (0 == zap_contains(mos, spa->spa_feat_for_read_obj,
feature.fi_guid)) {
- feature.fi_can_readonly = B_FALSE;
+ feature.fi_flags &= ~ZFEATURE_FLAG_READONLY_COMPAT;
} else if (0 == zap_contains(mos, spa->spa_feat_for_write_obj,
feature.fi_guid)) {
- feature.fi_can_readonly = B_TRUE;
+ feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT;
} else {
- fatal("feature is not enabled: %s", feature.fi_guid);
+ fatal(spa, FTAG, "feature is not enabled: %s", feature.fi_guid);
}
- if (decr && !spa_feature_is_active(spa, &feature))
- fatal("feature refcount already 0: %s", feature.fi_guid);
+ if (decr) {
+ uint64_t count;
+ if (feature_get_refcount_from_disk(spa, &feature,
+ &count) == 0 && count == 0) {
+ fatal(spa, FTAG, "feature refcount already 0: %s",
+ feature.fi_guid);
+ }
+ }
VERIFY0(dsl_sync_task(spa_name(spa), NULL,
- decr ? feature_decr_sync : feature_incr_sync, &feature, 5));
+ decr ? feature_decr_sync : feature_incr_sync, &feature,
+ 5, ZFS_SPACE_CHECK_NORMAL));
spa_close(spa, FTAG);
}
char *path[MAX_NUM_PATHS];
const char *subcommand;
int rv = 0;
- char c;
+ int c;
g_importargs.path = path;
dprintf_setup(&argc, argv);
zfs_prop_init();
- while ((c = getopt(argc, argv, "c:d:")) != -1) {
+ while ((c = getopt(argc, argv, "+c:d:")) != -1) {
switch (c) {
case 'c':
g_importargs.cachefile = optarg;
usage();
}
- if (!g_readonly && spa_export(g_pool, NULL, B_TRUE, B_TRUE) != 0) {
- fatal("pool export failed; "
+ if (!g_readonly && spa_export(g_pool, NULL, B_TRUE, B_FALSE) != 0) {
+ fatal(NULL, FTAG, "pool export failed; "
"changes may not be committed to disk\n");
}