static void zfeature_register(spa_feature_t fid, const char *guid, const char *name, const char *desc, boolean_t readonly, boolean_t mos, boolean_t activate_on_enable, const spa_feature_t *deps) { zfeature_info_t *feature = &spa_feature_table[fid]; static spa_feature_t nodeps[] = { SPA_FEATURE_NONE }; ASSERT(name != NULL); ASSERT(desc != NULL); ASSERT(!readonly || !mos); ASSERT3U(fid, <, SPA_FEATURES); ASSERT(zfeature_is_valid_guid(guid)); if (deps == NULL) deps = nodeps; feature->fi_feature = fid; feature->fi_guid = guid; feature->fi_uname = name; feature->fi_desc = desc; feature->fi_can_readonly = readonly; feature->fi_mos = mos; feature->fi_activate_on_enable = activate_on_enable; feature->fi_depends = deps; }
static void zfeature_register(spa_feature_t fid, const char *guid, const char *name, const char *desc, zfeature_flags_t flags, const spa_feature_t *deps) { zfeature_info_t *feature = &spa_feature_table[fid]; static spa_feature_t nodeps[] = { SPA_FEATURE_NONE }; ASSERT(name != NULL); ASSERT(desc != NULL); ASSERT((flags & ZFEATURE_FLAG_READONLY_COMPAT) == 0 || (flags & ZFEATURE_FLAG_MOS) == 0); ASSERT3U(fid, <, SPA_FEATURES); ASSERT(zfeature_is_valid_guid(guid)); if (deps == NULL) deps = nodeps; VERIFY(((flags & ZFEATURE_FLAG_PER_DATASET) == 0) || (deps_contains_feature(deps, SPA_FEATURE_EXTENSIBLE_DATASET))); feature->fi_feature = fid; feature->fi_guid = guid; feature->fi_uname = name; feature->fi_desc = desc; feature->fi_flags = flags; feature->fi_depends = deps; }
static void zhack_do_feature_ref(int argc, char **argv) { char c; char *target; boolean_t decr = B_FALSE; spa_t *spa; objset_t *mos; zfeature_info_t feature; spa_feature_t nodeps[] = { SPA_FEATURE_NONE }; /* * fi_desc does not matter here because it was written to disk * when the feature was enabled, but we need to properly set the * feature for read or write based on the information we read off * disk later. */ feature.fi_uname = "zhack"; 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) { switch (c) { case 'm': feature.fi_flags |= ZFEATURE_FLAG_MOS; break; case 'd': decr = B_TRUE; break; default: usage(); break; } } argc -= optind; argv += optind; if (argc < 2) { (void) fprintf(stderr, "error: missing feature or pool name\n"); usage(); } target = argv[0]; feature.fi_guid = argv[1]; if (!zfeature_is_valid_guid(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 (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_flags &= ~ZFEATURE_FLAG_READONLY_COMPAT; } else if (0 == zap_contains(mos, spa->spa_feat_for_write_obj, feature.fi_guid)) { feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT; } else { fatal(spa, FTAG, "feature is not enabled: %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, ZFS_SPACE_CHECK_NORMAL)); spa_close(spa, FTAG); }
static void zhack_do_feature_enable(int argc, char **argv) { char c; char *desc, *target; spa_t *spa; objset_t *mos; zfeature_info_t feature; spa_feature_t nodeps[] = { SPA_FEATURE_NONE }; /* * Features are not added to the pool's label until their refcounts * are incremented, so fi_mos can just be left as false for now. */ desc = NULL; feature.fi_uname = "zhack"; feature.fi_flags = 0; feature.fi_depends = nodeps; feature.fi_feature = SPA_FEATURE_NONE; optind = 1; while ((c = getopt(argc, argv, "rmd:")) != -1) { switch (c) { case 'r': feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT; break; case 'd': desc = strdup(optarg); break; default: usage(); break; } } if (desc == NULL) desc = strdup("zhack injected"); feature.fi_desc = desc; argc -= optind; argv += optind; if (argc < 2) { (void) fprintf(stderr, "error: missing feature or pool name\n"); usage(); } target = argv[0]; feature.fi_guid = argv[1]; if (!zfeature_is_valid_guid(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 (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(spa, FTAG, "feature already enabled: %s", feature.fi_guid); VERIFY0(dsl_sync_task(spa_name(spa), NULL, zhack_feature_enable_sync, &feature, 5, ZFS_SPACE_CHECK_NORMAL)); spa_close(spa, FTAG); free(desc); }
static int feature_do_action(objset_t *os, uint64_t read_obj, uint64_t write_obj, uint64_t desc_obj, zfeature_info_t *feature, feature_action_t action, dmu_tx_t *tx) { int error; uint64_t refcount; uint64_t zapobj = feature->fi_can_readonly ? write_obj : read_obj; ASSERT(0 != zapobj); ASSERT(zfeature_is_valid_guid(feature->fi_guid)); error = zap_lookup(os, zapobj, feature->fi_guid, sizeof (uint64_t), 1, &refcount); /* * If we can't ascertain the status of the specified feature, an I/O * error occurred. */ if (error != 0 && error != ENOENT) return (error); switch (action) { case FEATURE_ACTION_ENABLE: /* * If the feature is already enabled, ignore the request. */ if (error == 0) return (0); refcount = 0; break; case FEATURE_ACTION_INCR: if (error == ENOENT) return (ENOTSUP); if (refcount == UINT64_MAX) return (EOVERFLOW); refcount++; break; case FEATURE_ACTION_DECR: if (error == ENOENT) return (ENOTSUP); if (refcount == 0) return (EOVERFLOW); refcount--; break; default: ASSERT(0); break; } if (action == FEATURE_ACTION_ENABLE) { int i; for (i = 0; feature->fi_depends[i] != NULL; i++) { zfeature_info_t *dep = feature->fi_depends[i]; error = feature_do_action(os, read_obj, write_obj, desc_obj, dep, FEATURE_ACTION_ENABLE, tx); if (error != 0) return (error); } } error = zap_update(os, zapobj, feature->fi_guid, sizeof (uint64_t), 1, &refcount, tx); if (error != 0) return (error); if (action == FEATURE_ACTION_ENABLE) { error = zap_update(os, desc_obj, feature->fi_guid, 1, strlen(feature->fi_desc) + 1, feature->fi_desc, tx); if (error != 0) return (error); } if (action == FEATURE_ACTION_INCR && refcount == 1 && feature->fi_mos) { spa_activate_mos_feature(dmu_objset_spa(os), feature->fi_guid); } if (action == FEATURE_ACTION_DECR && refcount == 0) { spa_deactivate_mos_feature(dmu_objset_spa(os), feature->fi_guid); } return (0); }
static void zhack_do_feature_ref(int argc, char **argv) { char c; char *target; boolean_t decr = B_FALSE; spa_t *spa; objset_t *mos; zfeature_info_t feature; zfeature_info_t *nodeps[] = { NULL }; /* * fi_desc does not matter here because it was written to disk * when the feature was enabled, but we need to properly set the * feature for read or write based on the information we read off * disk later. */ feature.fi_uname = "zhack"; feature.fi_mos = B_FALSE; feature.fi_desc = NULL; feature.fi_depends = nodeps; optind = 1; while ((c = getopt(argc, argv, "md")) != -1) { switch (c) { case 'm': feature.fi_mos = B_TRUE; break; case 'd': decr = B_TRUE; break; default: usage(); break; } } argc -= optind; argv += optind; if (argc < 2) { (void) fprintf(stderr, "error: missing feature or pool name\n"); usage(); } target = argv[0]; feature.fi_guid = argv[1]; if (!zfeature_is_valid_guid(feature.fi_guid)) fatal("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 (0 == zap_contains(mos, spa->spa_feat_for_read_obj, feature.fi_guid)) { feature.fi_can_readonly = B_FALSE; } else if (0 == zap_contains(mos, spa->spa_feat_for_write_obj, feature.fi_guid)) { feature.fi_can_readonly = B_TRUE; } else { fatal("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); VERIFY0(dsl_sync_task(spa_name(spa), NULL, decr ? feature_decr_sync : feature_incr_sync, &feature, 5)); spa_close(spa, FTAG); }