Path: blob/main/sys/contrib/openzfs/module/zfs/dsl_prop.c
48383 views
// SPDX-License-Identifier: CDDL-1.01/*2* CDDL HEADER START3*4* The contents of this file are subject to the terms of the5* 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.LICENSE9* or https://opensource.org/licenses/CDDL-1.0.10* See the License for the specific language governing permissions11* and limitations under the License.12*13* When distributing Covered Code, include this CDDL HEADER in each14* file and include the License file at usr/src/OPENSOLARIS.LICENSE.15* If applicable, add the following below this CDDL HEADER, with the16* fields enclosed by brackets "[]" replaced with your own identifying17* information: Portions Copyright [yyyy] [name of copyright owner]18*19* CDDL HEADER END20*/21/*22* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.23* Copyright (c) 2012, 2015 by Delphix. All rights reserved.24* Copyright (c) 2013 Martin Matuska. All rights reserved.25* Copyright 2019 Joyent, Inc.26* Copyright (c) 2022 Hewlett Packard Enterprise Development LP.27*/2829#include <sys/zfs_context.h>30#include <sys/dmu.h>31#include <sys/dmu_objset.h>32#include <sys/dmu_tx.h>33#include <sys/dsl_dataset.h>34#include <sys/dsl_dir.h>35#include <sys/dsl_prop.h>36#include <sys/dsl_synctask.h>37#include <sys/spa.h>38#include <sys/zap.h>39#include <sys/fs/zfs.h>4041#include "zfs_prop.h"4243#define ZPROP_INHERIT_SUFFIX "$inherit"44#define ZPROP_RECVD_SUFFIX "$recvd"45#define ZPROP_IUV_SUFFIX "$iuv"4647static int48dodefault(zfs_prop_t prop, int intsz, int numints, void *buf)49{50/*51* The setonce properties are read-only, BUT they still52* have a default value that can be used as the initial53* value.54*/55if (prop == ZPROP_INVAL ||56(zfs_prop_readonly(prop) && !zfs_prop_setonce(prop)))57return (SET_ERROR(ENOENT));5859if (zfs_prop_get_type(prop) == PROP_TYPE_STRING) {60if (intsz != 1)61return (SET_ERROR(EOVERFLOW));62(void) strlcpy(buf, zfs_prop_default_string(prop),63numints);64} else {65if (intsz != 8 || numints < 1)66return (SET_ERROR(EOVERFLOW));6768*(uint64_t *)buf = zfs_prop_default_numeric(prop);69}7071return (0);72}7374static int75dsl_prop_known_index(zfs_prop_t prop, uint64_t value)76{77const char *str = NULL;78if (prop != ZPROP_CONT && prop != ZPROP_INVAL &&79zfs_prop_get_type(prop) == PROP_TYPE_INDEX)80return (!zfs_prop_index_to_string(prop, value, &str));8182return (-1);83}8485int86dsl_prop_get_dd(dsl_dir_t *dd, const char *propname,87int intsz, int numints, void *buf, char *setpoint, boolean_t snapshot)88{89int err;90dsl_dir_t *target = dd;91objset_t *mos = dd->dd_pool->dp_meta_objset;92zfs_prop_t prop;93boolean_t inheritable;94boolean_t inheriting = B_FALSE;95char *inheritstr;96char *recvdstr;97char *iuvstr;9899ASSERT(dsl_pool_config_held(dd->dd_pool));100101if (setpoint)102setpoint[0] = '\0';103104prop = zfs_name_to_prop(propname);105inheritable = (prop == ZPROP_USERPROP || zfs_prop_inheritable(prop));106inheritstr = kmem_asprintf("%s%s", propname, ZPROP_INHERIT_SUFFIX);107recvdstr = kmem_asprintf("%s%s", propname, ZPROP_RECVD_SUFFIX);108iuvstr = kmem_asprintf("%s%s", propname, ZPROP_IUV_SUFFIX);109110/*111* Note: dd may become NULL, therefore we shouldn't dereference it112* after this loop.113*/114for (; dd != NULL; dd = dd->dd_parent) {115if (dd != target || snapshot) {116if (!inheritable) {117err = SET_ERROR(ENOENT);118break;119}120inheriting = B_TRUE;121}122123/* Check for a iuv value. */124err = zap_lookup(mos, dsl_dir_phys(dd)->dd_props_zapobj,125iuvstr, intsz, numints, buf);126if (err == 0 && dsl_prop_known_index(prop,127*(uint64_t *)buf) != 1)128err = ENOENT;129if (err != ENOENT) {130if (setpoint != NULL && err == 0)131dsl_dir_name(dd, setpoint);132break;133}134135/* Check for a local value. */136err = zap_lookup(mos, dsl_dir_phys(dd)->dd_props_zapobj,137propname, intsz, numints, buf);138if (err != ENOENT) {139if (setpoint != NULL && err == 0)140dsl_dir_name(dd, setpoint);141break;142}143144/*145* Skip the check for a received value if there is an explicit146* inheritance entry.147*/148err = zap_contains(mos, dsl_dir_phys(dd)->dd_props_zapobj,149inheritstr);150if (err != 0 && err != ENOENT)151break;152153if (err == ENOENT) {154/* Check for a received value. */155err = zap_lookup(mos, dsl_dir_phys(dd)->dd_props_zapobj,156recvdstr, intsz, numints, buf);157if (err != ENOENT) {158if (setpoint != NULL && err == 0) {159if (inheriting) {160dsl_dir_name(dd, setpoint);161} else {162(void) strlcpy(setpoint,163ZPROP_SOURCE_VAL_RECVD,164MAXNAMELEN);165}166}167break;168}169}170171/*172* If we found an explicit inheritance entry, err is zero even173* though we haven't yet found the value, so reinitializing err174* at the end of the loop (instead of at the beginning) ensures175* that err has a valid post-loop value.176*/177err = SET_ERROR(ENOENT);178}179180if (err == ENOENT)181err = dodefault(prop, intsz, numints, buf);182183kmem_strfree(inheritstr);184kmem_strfree(recvdstr);185kmem_strfree(iuvstr);186187return (err);188}189190int191dsl_prop_get_ds(dsl_dataset_t *ds, const char *propname,192int intsz, int numints, void *buf, char *setpoint)193{194zfs_prop_t prop = zfs_name_to_prop(propname);195boolean_t inheritable;196uint64_t zapobj;197198ASSERT(dsl_pool_config_held(ds->ds_dir->dd_pool));199inheritable = (prop == ZPROP_USERPROP || zfs_prop_inheritable(prop));200zapobj = dsl_dataset_phys(ds)->ds_props_obj;201202if (zapobj != 0) {203objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;204int err;205206ASSERT(ds->ds_is_snapshot);207208/* Check for a local value. */209err = zap_lookup(mos, zapobj, propname, intsz, numints, buf);210if (err != ENOENT) {211if (setpoint != NULL && err == 0)212dsl_dataset_name(ds, setpoint);213return (err);214}215216/*217* Skip the check for a received value if there is an explicit218* inheritance entry.219*/220if (inheritable) {221char *inheritstr = kmem_asprintf("%s%s", propname,222ZPROP_INHERIT_SUFFIX);223err = zap_contains(mos, zapobj, inheritstr);224kmem_strfree(inheritstr);225if (err != 0 && err != ENOENT)226return (err);227}228229if (err == ENOENT) {230/* Check for a received value. */231char *recvdstr = kmem_asprintf("%s%s", propname,232ZPROP_RECVD_SUFFIX);233err = zap_lookup(mos, zapobj, recvdstr,234intsz, numints, buf);235kmem_strfree(recvdstr);236if (err != ENOENT) {237if (setpoint != NULL && err == 0)238(void) strlcpy(setpoint,239ZPROP_SOURCE_VAL_RECVD,240MAXNAMELEN);241return (err);242}243}244}245246return (dsl_prop_get_dd(ds->ds_dir, propname,247intsz, numints, buf, setpoint, ds->ds_is_snapshot));248}249250static dsl_prop_record_t *251dsl_prop_record_find(dsl_dir_t *dd, const char *propname)252{253dsl_prop_record_t *pr = NULL;254255ASSERT(MUTEX_HELD(&dd->dd_lock));256257for (pr = list_head(&dd->dd_props);258pr != NULL; pr = list_next(&dd->dd_props, pr)) {259if (strcmp(pr->pr_propname, propname) == 0)260break;261}262263return (pr);264}265266static dsl_prop_record_t *267dsl_prop_record_create(dsl_dir_t *dd, const char *propname)268{269dsl_prop_record_t *pr;270271ASSERT(MUTEX_HELD(&dd->dd_lock));272273pr = kmem_alloc(sizeof (dsl_prop_record_t), KM_SLEEP);274pr->pr_propname = spa_strdup(propname);275list_create(&pr->pr_cbs, sizeof (dsl_prop_cb_record_t),276offsetof(dsl_prop_cb_record_t, cbr_pr_node));277list_insert_head(&dd->dd_props, pr);278279return (pr);280}281282void283dsl_prop_init(dsl_dir_t *dd)284{285list_create(&dd->dd_props, sizeof (dsl_prop_record_t),286offsetof(dsl_prop_record_t, pr_node));287}288289void290dsl_prop_fini(dsl_dir_t *dd)291{292dsl_prop_record_t *pr;293294while ((pr = list_remove_head(&dd->dd_props)) != NULL) {295list_destroy(&pr->pr_cbs);296spa_strfree((char *)pr->pr_propname);297kmem_free(pr, sizeof (dsl_prop_record_t));298}299list_destroy(&dd->dd_props);300}301302/*303* Register interest in the named property. We'll call the callback304* once to notify it of the current property value, and again each time305* the property changes, until this callback is unregistered.306*307* Return 0 on success, errno if the prop is not an integer value.308*/309int310dsl_prop_register(dsl_dataset_t *ds, const char *propname,311dsl_prop_changed_cb_t *callback, void *cbarg)312{313dsl_dir_t *dd = ds->ds_dir;314uint64_t value;315dsl_prop_record_t *pr;316dsl_prop_cb_record_t *cbr;317int err;318dsl_pool_t *dp __maybe_unused = dd->dd_pool;319320ASSERT(dsl_pool_config_held(dp));321322err = dsl_prop_get_int_ds(ds, propname, &value);323if (err != 0)324return (err);325326cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP);327cbr->cbr_ds = ds;328cbr->cbr_func = callback;329cbr->cbr_arg = cbarg;330331mutex_enter(&dd->dd_lock);332pr = dsl_prop_record_find(dd, propname);333if (pr == NULL)334pr = dsl_prop_record_create(dd, propname);335cbr->cbr_pr = pr;336list_insert_head(&pr->pr_cbs, cbr);337list_insert_head(&ds->ds_prop_cbs, cbr);338mutex_exit(&dd->dd_lock);339340cbr->cbr_func(cbr->cbr_arg, value);341return (0);342}343344int345dsl_prop_get(const char *dsname, const char *propname,346int intsz, int numints, void *buf, char *setpoint)347{348objset_t *os;349int error;350351error = dmu_objset_hold(dsname, FTAG, &os);352if (error != 0)353return (error);354355error = dsl_prop_get_ds(dmu_objset_ds(os), propname,356intsz, numints, buf, setpoint);357358dmu_objset_rele(os, FTAG);359return (error);360}361362/*363* Get the current property value. It may have changed by the time this364* function returns, so it is NOT safe to follow up with365* dsl_prop_register() and assume that the value has not changed in366* between.367*368* Return 0 on success, ENOENT if ddname is invalid.369*/370int371dsl_prop_get_integer(const char *ddname, const char *propname,372uint64_t *valuep, char *setpoint)373{374return (dsl_prop_get(ddname, propname, 8, 1, valuep, setpoint));375}376377int378dsl_prop_get_int_ds(dsl_dataset_t *ds, const char *propname,379uint64_t *valuep)380{381return (dsl_prop_get_ds(ds, propname, 8, 1, valuep, NULL));382}383384/*385* Predict the effective value of the given special property if it were set with386* the given value and source. This is not a general purpose function. It exists387* only to handle the special requirements of the quota and reservation388* properties. The fact that these properties are non-inheritable greatly389* simplifies the prediction logic.390*391* Returns 0 on success, a positive error code on failure, or -1 if called with392* a property not handled by this function.393*/394int395dsl_prop_predict(dsl_dir_t *dd, const char *propname,396zprop_source_t source, uint64_t value, uint64_t *newvalp)397{398zfs_prop_t prop = zfs_name_to_prop(propname);399objset_t *mos;400uint64_t zapobj;401uint64_t version;402char *recvdstr;403int err = 0;404405switch (prop) {406case ZFS_PROP_QUOTA:407case ZFS_PROP_RESERVATION:408case ZFS_PROP_REFQUOTA:409case ZFS_PROP_REFRESERVATION:410break;411default:412return (-1);413}414415mos = dd->dd_pool->dp_meta_objset;416zapobj = dsl_dir_phys(dd)->dd_props_zapobj;417recvdstr = kmem_asprintf("%s%s", propname, ZPROP_RECVD_SUFFIX);418419version = spa_version(dd->dd_pool->dp_spa);420if (version < SPA_VERSION_RECVD_PROPS) {421if (source & ZPROP_SRC_NONE)422source = ZPROP_SRC_NONE;423else if (source & ZPROP_SRC_RECEIVED)424source = ZPROP_SRC_LOCAL;425}426427switch ((int)source) {428case ZPROP_SRC_NONE:429/* Revert to the received value, if any. */430err = zap_lookup(mos, zapobj, recvdstr, 8, 1, newvalp);431if (err == ENOENT)432*newvalp = 0;433break;434case ZPROP_SRC_LOCAL:435*newvalp = value;436break;437case ZPROP_SRC_RECEIVED:438/*439* If there's no local setting, then the new received value will440* be the effective value.441*/442err = zap_lookup(mos, zapobj, propname, 8, 1, newvalp);443if (err == ENOENT)444*newvalp = value;445break;446case (ZPROP_SRC_NONE | ZPROP_SRC_RECEIVED):447/*448* We're clearing the received value, so the local setting (if449* it exists) remains the effective value.450*/451err = zap_lookup(mos, zapobj, propname, 8, 1, newvalp);452if (err == ENOENT)453*newvalp = 0;454break;455default:456panic("unexpected property source: %d", source);457}458459kmem_strfree(recvdstr);460461if (err == ENOENT)462return (0);463464return (err);465}466467/*468* Unregister this callback. Return 0 on success, ENOENT if ddname is469* invalid, or ENOMSG if no matching callback registered.470*471* NOTE: This function is no longer used internally but has been preserved472* to prevent breaking external consumers (Lustre, etc).473*/474int475dsl_prop_unregister(dsl_dataset_t *ds, const char *propname,476dsl_prop_changed_cb_t *callback, void *cbarg)477{478dsl_dir_t *dd = ds->ds_dir;479dsl_prop_cb_record_t *cbr;480481mutex_enter(&dd->dd_lock);482for (cbr = list_head(&ds->ds_prop_cbs);483cbr; cbr = list_next(&ds->ds_prop_cbs, cbr)) {484if (cbr->cbr_ds == ds &&485cbr->cbr_func == callback &&486cbr->cbr_arg == cbarg &&487strcmp(cbr->cbr_pr->pr_propname, propname) == 0)488break;489}490491if (cbr == NULL) {492mutex_exit(&dd->dd_lock);493return (SET_ERROR(ENOMSG));494}495496list_remove(&ds->ds_prop_cbs, cbr);497list_remove(&cbr->cbr_pr->pr_cbs, cbr);498mutex_exit(&dd->dd_lock);499kmem_free(cbr, sizeof (dsl_prop_cb_record_t));500501return (0);502}503504/*505* Unregister all callbacks that are registered with the506* given callback argument.507*/508void509dsl_prop_unregister_all(dsl_dataset_t *ds, void *cbarg)510{511dsl_prop_cb_record_t *cbr, *next_cbr;512513dsl_dir_t *dd = ds->ds_dir;514515mutex_enter(&dd->dd_lock);516next_cbr = list_head(&ds->ds_prop_cbs);517while (next_cbr != NULL) {518cbr = next_cbr;519next_cbr = list_next(&ds->ds_prop_cbs, cbr);520if (cbr->cbr_arg == cbarg) {521list_remove(&ds->ds_prop_cbs, cbr);522list_remove(&cbr->cbr_pr->pr_cbs, cbr);523kmem_free(cbr, sizeof (dsl_prop_cb_record_t));524}525}526mutex_exit(&dd->dd_lock);527}528529boolean_t530dsl_prop_hascb(dsl_dataset_t *ds)531{532return (!list_is_empty(&ds->ds_prop_cbs));533}534535static int536dsl_prop_notify_all_cb(dsl_pool_t *dp, dsl_dataset_t *ds, void *arg)537{538(void) arg;539dsl_dir_t *dd = ds->ds_dir;540dsl_prop_record_t *pr;541dsl_prop_cb_record_t *cbr;542543mutex_enter(&dd->dd_lock);544for (pr = list_head(&dd->dd_props);545pr; pr = list_next(&dd->dd_props, pr)) {546for (cbr = list_head(&pr->pr_cbs); cbr;547cbr = list_next(&pr->pr_cbs, cbr)) {548uint64_t value;549550/*551* Callback entries do not have holds on their552* datasets so that datasets with registered553* callbacks are still eligible for eviction.554* Unlike operations to update properties on a555* single dataset, we are performing a recursive556* descent of related head datasets. The caller557* of this function only has a dataset hold on558* the passed in head dataset, not the snapshots559* associated with this dataset. Without a hold,560* the dataset pointer within callback records561* for snapshots can be invalidated by eviction562* at any time.563*564* Use dsl_dataset_try_add_ref() to verify565* that the dataset for a snapshot has not566* begun eviction processing and to prevent567* eviction from occurring for the duration of568* the callback. If the hold attempt fails,569* this object is already being evicted and the570* callback can be safely ignored.571*/572if (ds != cbr->cbr_ds &&573!dsl_dataset_try_add_ref(dp, cbr->cbr_ds, FTAG))574continue;575576if (dsl_prop_get_ds(cbr->cbr_ds,577cbr->cbr_pr->pr_propname, sizeof (value), 1,578&value, NULL) == 0)579cbr->cbr_func(cbr->cbr_arg, value);580581if (ds != cbr->cbr_ds)582dsl_dataset_rele(cbr->cbr_ds, FTAG);583}584}585mutex_exit(&dd->dd_lock);586587return (0);588}589590/*591* Update all property values for ddobj & its descendants. This is used592* when renaming the dir.593*/594void595dsl_prop_notify_all(dsl_dir_t *dd)596{597dsl_pool_t *dp = dd->dd_pool;598ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock));599(void) dmu_objset_find_dp(dp, dd->dd_object, dsl_prop_notify_all_cb,600NULL, DS_FIND_CHILDREN);601}602603static void604dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj,605const char *propname, uint64_t value, int first)606{607dsl_dir_t *dd;608dsl_prop_record_t *pr;609dsl_prop_cb_record_t *cbr;610objset_t *mos = dp->dp_meta_objset;611zap_cursor_t zc;612zap_attribute_t *za;613int err;614615ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock));616err = dsl_dir_hold_obj(dp, ddobj, NULL, FTAG, &dd);617if (err)618return;619620if (!first) {621/*622* If the prop is set here, then this change is not623* being inherited here or below; stop the recursion.624*/625err = zap_contains(mos, dsl_dir_phys(dd)->dd_props_zapobj,626propname);627if (err == 0) {628dsl_dir_rele(dd, FTAG);629return;630}631ASSERT3U(err, ==, ENOENT);632}633634mutex_enter(&dd->dd_lock);635pr = dsl_prop_record_find(dd, propname);636if (pr != NULL) {637for (cbr = list_head(&pr->pr_cbs); cbr;638cbr = list_next(&pr->pr_cbs, cbr)) {639uint64_t propobj;640641/*642* cbr->cbr_ds may be invalidated due to eviction,643* requiring the use of dsl_dataset_try_add_ref().644* See comment block in dsl_prop_notify_all_cb()645* for details.646*/647if (!dsl_dataset_try_add_ref(dp, cbr->cbr_ds, FTAG))648continue;649650propobj = dsl_dataset_phys(cbr->cbr_ds)->ds_props_obj;651652/*653* If the property is not set on this ds, then it is654* inherited here; call the callback.655*/656if (propobj == 0 ||657zap_contains(mos, propobj, propname) != 0)658cbr->cbr_func(cbr->cbr_arg, value);659660dsl_dataset_rele(cbr->cbr_ds, FTAG);661}662}663mutex_exit(&dd->dd_lock);664665za = zap_attribute_alloc();666for (zap_cursor_init(&zc, mos,667dsl_dir_phys(dd)->dd_child_dir_zapobj);668zap_cursor_retrieve(&zc, za) == 0;669zap_cursor_advance(&zc)) {670dsl_prop_changed_notify(dp, za->za_first_integer,671propname, value, FALSE);672}673zap_attribute_free(za);674zap_cursor_fini(&zc);675dsl_dir_rele(dd, FTAG);676}677678679/*680* For newer values in zfs index type properties, we add a new key681* propname$iuv (iuv = Ignore Unknown Values) to the properties zap object682* to store the new property value and store the default value in the683* existing prop key. So that the propname$iuv key is ignored by the older zfs684* versions and the default property value from the existing prop key is685* used.686*/687static void688dsl_prop_set_iuv(objset_t *mos, uint64_t zapobj, const char *propname,689int intsz, int numints, const void *value, dmu_tx_t *tx)690{691char *iuvstr = kmem_asprintf("%s%s", propname, ZPROP_IUV_SUFFIX);692boolean_t iuv = B_FALSE;693zfs_prop_t prop = zfs_name_to_prop(propname);694695switch (prop) {696case ZFS_PROP_REDUNDANT_METADATA:697if (*(uint64_t *)value == ZFS_REDUNDANT_METADATA_SOME ||698*(uint64_t *)value == ZFS_REDUNDANT_METADATA_NONE)699iuv = B_TRUE;700break;701case ZFS_PROP_SNAPDIR:702if (*(uint64_t *)value == ZFS_SNAPDIR_DISABLED)703iuv = B_TRUE;704break;705default:706break;707}708709if (iuv) {710VERIFY0(zap_update(mos, zapobj, iuvstr, intsz, numints,711value, tx));712uint64_t val = zfs_prop_default_numeric(prop);713VERIFY0(zap_update(mos, zapobj, propname, intsz, numints,714&val, tx));715} else {716zap_remove(mos, zapobj, iuvstr, tx);717}718kmem_strfree(iuvstr);719}720721void722dsl_prop_set_sync_impl(dsl_dataset_t *ds, const char *propname,723zprop_source_t source, int intsz, int numints, const void *value,724dmu_tx_t *tx)725{726objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;727uint64_t zapobj, intval, dummy, count;728int isint;729char valbuf[32];730const char *valstr = NULL;731char *inheritstr;732char *recvdstr;733char *iuvstr;734char *tbuf = NULL;735int err;736uint64_t version = spa_version(ds->ds_dir->dd_pool->dp_spa);737738isint = (dodefault(zfs_name_to_prop(propname), 8, 1, &intval) == 0);739740if (ds->ds_is_snapshot) {741ASSERT(version >= SPA_VERSION_SNAP_PROPS);742if (dsl_dataset_phys(ds)->ds_props_obj == 0 &&743(source & ZPROP_SRC_NONE) == 0) {744dmu_buf_will_dirty(ds->ds_dbuf, tx);745dsl_dataset_phys(ds)->ds_props_obj =746zap_create(mos,747DMU_OT_DSL_PROPS, DMU_OT_NONE, 0, tx);748}749zapobj = dsl_dataset_phys(ds)->ds_props_obj;750} else {751zapobj = dsl_dir_phys(ds->ds_dir)->dd_props_zapobj;752}753754/* If we are removing objects from a non-existent ZAP just return */755if (zapobj == 0)756return;757758if (version < SPA_VERSION_RECVD_PROPS) {759if (source & ZPROP_SRC_NONE)760source = ZPROP_SRC_NONE;761else if (source & ZPROP_SRC_RECEIVED)762source = ZPROP_SRC_LOCAL;763}764765inheritstr = kmem_asprintf("%s%s", propname, ZPROP_INHERIT_SUFFIX);766recvdstr = kmem_asprintf("%s%s", propname, ZPROP_RECVD_SUFFIX);767iuvstr = kmem_asprintf("%s%s", propname, ZPROP_IUV_SUFFIX);768769switch ((int)source) {770case ZPROP_SRC_NONE:771/*772* revert to received value, if any (inherit -S)773* - remove propname774* - remove propname$inherit775*/776err = zap_remove(mos, zapobj, propname, tx);777ASSERT(err == 0 || err == ENOENT);778err = zap_remove(mos, zapobj, inheritstr, tx);779ASSERT(err == 0 || err == ENOENT);780break;781case ZPROP_SRC_LOCAL:782/*783* remove propname$inherit784* set propname -> value785* set propname$iuv -> new property value786*/787err = zap_remove(mos, zapobj, inheritstr, tx);788ASSERT(err == 0 || err == ENOENT);789VERIFY0(zap_update(mos, zapobj, propname,790intsz, numints, value, tx));791(void) dsl_prop_set_iuv(mos, zapobj, propname, intsz,792numints, value, tx);793break;794case ZPROP_SRC_INHERITED:795/*796* explicitly inherit797* - remove propname798* - set propname$inherit799*/800err = zap_remove(mos, zapobj, propname, tx);801ASSERT(err == 0 || err == ENOENT);802err = zap_remove(mos, zapobj, iuvstr, tx);803ASSERT(err == 0 || err == ENOENT);804if (version >= SPA_VERSION_RECVD_PROPS &&805dsl_prop_get_int_ds(ds, ZPROP_HAS_RECVD, &dummy) == 0) {806dummy = 0;807VERIFY0(zap_update(mos, zapobj, inheritstr,8088, 1, &dummy, tx));809}810break;811case ZPROP_SRC_RECEIVED:812/*813* set propname$recvd -> value814*/815err = zap_update(mos, zapobj, recvdstr,816intsz, numints, value, tx);817ASSERT0(err);818break;819case (ZPROP_SRC_NONE | ZPROP_SRC_LOCAL | ZPROP_SRC_RECEIVED):820/*821* clear local and received settings822* - remove propname823* - remove propname$inherit824* - remove propname$recvd825*/826err = zap_remove(mos, zapobj, propname, tx);827ASSERT(err == 0 || err == ENOENT);828err = zap_remove(mos, zapobj, inheritstr, tx);829ASSERT(err == 0 || err == ENOENT);830zfs_fallthrough;831case (ZPROP_SRC_NONE | ZPROP_SRC_RECEIVED):832/*833* remove propname$recvd834*/835err = zap_remove(mos, zapobj, recvdstr, tx);836ASSERT(err == 0 || err == ENOENT);837break;838default:839cmn_err(CE_PANIC, "unexpected property source: %d", source);840}841842kmem_strfree(inheritstr);843kmem_strfree(recvdstr);844kmem_strfree(iuvstr);845846/*847* If we are left with an empty snap zap we can destroy it.848* This will prevent unnecessary calls to zap_lookup() in849* the "zfs list" and "zfs get" code paths.850*/851if (ds->ds_is_snapshot &&852zap_count(mos, zapobj, &count) == 0 && count == 0) {853dmu_buf_will_dirty(ds->ds_dbuf, tx);854dsl_dataset_phys(ds)->ds_props_obj = 0;855zap_destroy(mos, zapobj, tx);856}857858if (isint) {859VERIFY0(dsl_prop_get_int_ds(ds, propname, &intval));860861if (ds->ds_is_snapshot) {862dsl_prop_cb_record_t *cbr;863/*864* It's a snapshot; nothing can inherit this865* property, so just look for callbacks on this866* ds here.867*/868mutex_enter(&ds->ds_dir->dd_lock);869for (cbr = list_head(&ds->ds_prop_cbs); cbr;870cbr = list_next(&ds->ds_prop_cbs, cbr)) {871if (strcmp(cbr->cbr_pr->pr_propname,872propname) == 0)873cbr->cbr_func(cbr->cbr_arg, intval);874}875mutex_exit(&ds->ds_dir->dd_lock);876} else {877dsl_prop_changed_notify(ds->ds_dir->dd_pool,878ds->ds_dir->dd_object, propname, intval, TRUE);879}880881(void) snprintf(valbuf, sizeof (valbuf),882"%lld", (longlong_t)intval);883valstr = valbuf;884} else {885if (source == ZPROP_SRC_LOCAL) {886valstr = value;887} else {888tbuf = kmem_alloc(ZAP_MAXVALUELEN, KM_SLEEP);889if (dsl_prop_get_ds(ds, propname, 1,890ZAP_MAXVALUELEN, tbuf, NULL) == 0)891valstr = tbuf;892}893}894895spa_history_log_internal_ds(ds, (source == ZPROP_SRC_NONE ||896source == ZPROP_SRC_INHERITED) ? "inherit" : "set", tx,897"%s=%s", propname, (valstr == NULL ? "" : valstr));898899if (tbuf != NULL)900kmem_free(tbuf, ZAP_MAXVALUELEN);901}902903int904dsl_prop_set_int(const char *dsname, const char *propname,905zprop_source_t source, uint64_t value)906{907nvlist_t *nvl = fnvlist_alloc();908int error;909910fnvlist_add_uint64(nvl, propname, value);911error = dsl_props_set(dsname, source, nvl);912fnvlist_free(nvl);913return (error);914}915916int917dsl_prop_set_string(const char *dsname, const char *propname,918zprop_source_t source, const char *value)919{920nvlist_t *nvl = fnvlist_alloc();921int error;922923fnvlist_add_string(nvl, propname, value);924error = dsl_props_set(dsname, source, nvl);925fnvlist_free(nvl);926return (error);927}928929int930dsl_prop_inherit(const char *dsname, const char *propname,931zprop_source_t source)932{933nvlist_t *nvl = fnvlist_alloc();934int error;935936fnvlist_add_boolean(nvl, propname);937error = dsl_props_set(dsname, source, nvl);938fnvlist_free(nvl);939return (error);940}941942int943dsl_props_set_check(void *arg, dmu_tx_t *tx)944{945dsl_props_set_arg_t *dpsa = arg;946dsl_pool_t *dp = dmu_tx_pool(tx);947dsl_dataset_t *ds;948uint64_t version;949nvpair_t *elem = NULL;950int err;951952err = dsl_dataset_hold(dp, dpsa->dpsa_dsname, FTAG, &ds);953if (err != 0)954return (err);955956version = spa_version(ds->ds_dir->dd_pool->dp_spa);957while ((elem = nvlist_next_nvpair(dpsa->dpsa_props, elem)) != NULL) {958if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) {959dsl_dataset_rele(ds, FTAG);960return (SET_ERROR(ENAMETOOLONG));961}962if (nvpair_type(elem) == DATA_TYPE_STRING) {963const char *valstr = fnvpair_value_string(elem);964if (strlen(valstr) >= (version <965SPA_VERSION_STMF_PROP ?966ZAP_OLDMAXVALUELEN : ZAP_MAXVALUELEN)) {967dsl_dataset_rele(ds, FTAG);968return (SET_ERROR(E2BIG));969}970}971}972973if (ds->ds_is_snapshot && version < SPA_VERSION_SNAP_PROPS) {974dsl_dataset_rele(ds, FTAG);975return (SET_ERROR(ENOTSUP));976}977dsl_dataset_rele(ds, FTAG);978return (0);979}980981void982dsl_props_set_sync_impl(dsl_dataset_t *ds, zprop_source_t source,983nvlist_t *props, dmu_tx_t *tx)984{985nvpair_t *elem = NULL;986987while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {988nvpair_t *pair = elem;989const char *name = nvpair_name(pair);990991if (nvpair_type(pair) == DATA_TYPE_NVLIST) {992/*993* This usually happens when we reuse the nvlist_t data994* returned by the counterpart dsl_prop_get_all_impl().995* For instance we do this to restore the original996* received properties when an error occurs in the997* zfs_ioc_recv() codepath.998*/999nvlist_t *attrs = fnvpair_value_nvlist(pair);1000pair = fnvlist_lookup_nvpair(attrs, ZPROP_VALUE);1001}10021003if (nvpair_type(pair) == DATA_TYPE_STRING) {1004const char *value = fnvpair_value_string(pair);1005dsl_prop_set_sync_impl(ds, name,1006source, 1, strlen(value) + 1, value, tx);1007} else if (nvpair_type(pair) == DATA_TYPE_UINT64) {1008uint64_t intval = fnvpair_value_uint64(pair);1009dsl_prop_set_sync_impl(ds, name,1010source, sizeof (intval), 1, &intval, tx);1011} else if (nvpair_type(pair) == DATA_TYPE_BOOLEAN) {1012dsl_prop_set_sync_impl(ds, name,1013source, 0, 0, NULL, tx);1014} else {1015panic("invalid nvpair type");1016}1017}1018}10191020void1021dsl_props_set_sync(void *arg, dmu_tx_t *tx)1022{1023dsl_props_set_arg_t *dpsa = arg;1024dsl_pool_t *dp = dmu_tx_pool(tx);1025dsl_dataset_t *ds;10261027VERIFY0(dsl_dataset_hold(dp, dpsa->dpsa_dsname, FTAG, &ds));1028dsl_props_set_sync_impl(ds, dpsa->dpsa_source, dpsa->dpsa_props, tx);1029dsl_dataset_rele(ds, FTAG);1030}10311032/*1033* All-or-nothing; if any prop can't be set, nothing will be modified.1034*/1035int1036dsl_props_set(const char *dsname, zprop_source_t source, nvlist_t *props)1037{1038dsl_props_set_arg_t dpsa;1039int nblks = 0;10401041dpsa.dpsa_dsname = dsname;1042dpsa.dpsa_source = source;1043dpsa.dpsa_props = props;10441045/*1046* If the source includes NONE, then we will only be removing entries1047* from the ZAP object. In that case don't check for ENOSPC.1048*/1049if ((source & ZPROP_SRC_NONE) == 0)1050nblks = 2 * fnvlist_num_pairs(props);10511052return (dsl_sync_task(dsname, dsl_props_set_check, dsl_props_set_sync,1053&dpsa, nblks, ZFS_SPACE_CHECK_RESERVED));1054}10551056typedef enum dsl_prop_getflags {1057DSL_PROP_GET_INHERITING = 0x1, /* searching parent of target ds */1058DSL_PROP_GET_SNAPSHOT = 0x2, /* snapshot dataset */1059DSL_PROP_GET_LOCAL = 0x4, /* local properties */1060DSL_PROP_GET_RECEIVED = 0x8, /* received properties */1061} dsl_prop_getflags_t;10621063static int1064dsl_prop_get_all_impl(objset_t *mos, uint64_t propobj,1065const char *setpoint, dsl_prop_getflags_t flags, nvlist_t *nv)1066{1067zap_cursor_t zc;1068zap_attribute_t *za = zap_attribute_alloc();1069int err = 0;10701071for (zap_cursor_init(&zc, mos, propobj);1072(err = zap_cursor_retrieve(&zc, za)) == 0;1073zap_cursor_advance(&zc)) {1074nvlist_t *propval;1075zfs_prop_t prop;1076char buf[ZAP_MAXNAMELEN];1077char *valstr;1078const char *suffix;1079const char *propname;1080const char *source;10811082suffix = strchr(za->za_name, '$');10831084if (suffix == NULL) {1085/*1086* Skip local properties if we only want received1087* properties.1088*/1089if (flags & DSL_PROP_GET_RECEIVED)1090continue;10911092propname = za->za_name;1093source = setpoint;10941095/* Skip if iuv entries are preset. */1096valstr = kmem_asprintf("%s%s", propname,1097ZPROP_IUV_SUFFIX);1098err = zap_contains(mos, propobj, valstr);1099kmem_strfree(valstr);1100if (err == 0)1101continue;1102} else if (strcmp(suffix, ZPROP_INHERIT_SUFFIX) == 0) {1103/* Skip explicitly inherited entries. */1104continue;1105} else if (strcmp(suffix, ZPROP_RECVD_SUFFIX) == 0) {1106if (flags & DSL_PROP_GET_LOCAL)1107continue;11081109(void) strlcpy(buf, za->za_name,1110MIN(sizeof (buf), suffix - za->za_name + 1));1111propname = buf;11121113if (!(flags & DSL_PROP_GET_RECEIVED)) {1114/* Skip if locally overridden. */1115err = zap_contains(mos, propobj, propname);1116if (err == 0)1117continue;1118if (err != ENOENT)1119break;11201121/* Skip if explicitly inherited. */1122valstr = kmem_asprintf("%s%s", propname,1123ZPROP_INHERIT_SUFFIX);1124err = zap_contains(mos, propobj, valstr);1125kmem_strfree(valstr);1126if (err == 0)1127continue;1128if (err != ENOENT)1129break;1130}11311132source = ((flags & DSL_PROP_GET_INHERITING) ?1133setpoint : ZPROP_SOURCE_VAL_RECVD);1134} else if (strcmp(suffix, ZPROP_IUV_SUFFIX) == 0) {1135(void) strlcpy(buf, za->za_name,1136MIN(sizeof (buf), suffix - za->za_name + 1));1137propname = buf;1138source = setpoint;1139prop = zfs_name_to_prop(propname);11401141if (dsl_prop_known_index(prop,1142za->za_first_integer) != 1)1143continue;1144} else {1145/*1146* For backward compatibility, skip suffixes we don't1147* recognize.1148*/1149continue;1150}11511152prop = zfs_name_to_prop(propname);11531154/* Skip non-inheritable properties. */1155if ((flags & DSL_PROP_GET_INHERITING) &&1156prop != ZPROP_USERPROP && !zfs_prop_inheritable(prop))1157continue;11581159/* Skip properties not valid for this type. */1160if ((flags & DSL_PROP_GET_SNAPSHOT) && prop != ZPROP_USERPROP &&1161!zfs_prop_valid_for_type(prop, ZFS_TYPE_SNAPSHOT, B_FALSE))1162continue;11631164/* Skip properties already defined. */1165if (nvlist_exists(nv, propname))1166continue;11671168VERIFY0(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP));1169if (za->za_integer_length == 1) {1170/*1171* String property1172*/1173char *tmp = kmem_alloc(za->za_num_integers,1174KM_SLEEP);1175err = zap_lookup(mos, propobj,1176za->za_name, 1, za->za_num_integers, tmp);1177if (err != 0) {1178kmem_free(tmp, za->za_num_integers);1179break;1180}1181VERIFY0(nvlist_add_string(propval, ZPROP_VALUE, tmp));1182kmem_free(tmp, za->za_num_integers);1183} else {1184/*1185* Integer property1186*/1187ASSERT(za->za_integer_length == 8);1188(void) nvlist_add_uint64(propval, ZPROP_VALUE,1189za->za_first_integer);1190}11911192VERIFY0(nvlist_add_string(propval, ZPROP_SOURCE, source));1193VERIFY0(nvlist_add_nvlist(nv, propname, propval));1194nvlist_free(propval);1195}1196zap_cursor_fini(&zc);1197zap_attribute_free(za);1198if (err == ENOENT)1199err = 0;1200return (err);1201}12021203/*1204* Iterate over all properties for this dataset and return them in an nvlist.1205*/1206static int1207dsl_prop_get_all_ds(dsl_dataset_t *ds, nvlist_t **nvp,1208dsl_prop_getflags_t flags)1209{1210dsl_dir_t *dd = ds->ds_dir;1211dsl_pool_t *dp = dd->dd_pool;1212objset_t *mos = dp->dp_meta_objset;1213int err = 0;1214char setpoint[ZFS_MAX_DATASET_NAME_LEN];12151216VERIFY0(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP));12171218if (ds->ds_is_snapshot)1219flags |= DSL_PROP_GET_SNAPSHOT;12201221ASSERT(dsl_pool_config_held(dp));12221223if (dsl_dataset_phys(ds)->ds_props_obj != 0) {1224ASSERT(flags & DSL_PROP_GET_SNAPSHOT);1225dsl_dataset_name(ds, setpoint);1226err = dsl_prop_get_all_impl(mos,1227dsl_dataset_phys(ds)->ds_props_obj, setpoint, flags, *nvp);1228if (err)1229goto out;1230}12311232for (; dd != NULL; dd = dd->dd_parent) {1233if (dd != ds->ds_dir || (flags & DSL_PROP_GET_SNAPSHOT)) {1234if (flags & (DSL_PROP_GET_LOCAL |1235DSL_PROP_GET_RECEIVED))1236break;1237flags |= DSL_PROP_GET_INHERITING;1238}1239dsl_dir_name(dd, setpoint);1240err = dsl_prop_get_all_impl(mos,1241dsl_dir_phys(dd)->dd_props_zapobj, setpoint, flags, *nvp);1242if (err)1243break;1244}12451246out:1247if (err) {1248nvlist_free(*nvp);1249*nvp = NULL;1250}1251return (err);1252}12531254boolean_t1255dsl_prop_get_hasrecvd(const char *dsname)1256{1257uint64_t dummy;12581259return (0 ==1260dsl_prop_get_integer(dsname, ZPROP_HAS_RECVD, &dummy, NULL));1261}12621263static int1264dsl_prop_set_hasrecvd_impl(const char *dsname, zprop_source_t source)1265{1266uint64_t version;1267spa_t *spa;1268int error = 0;12691270VERIFY0(spa_open(dsname, &spa, FTAG));1271version = spa_version(spa);1272spa_close(spa, FTAG);12731274if (version >= SPA_VERSION_RECVD_PROPS)1275error = dsl_prop_set_int(dsname, ZPROP_HAS_RECVD, source, 0);1276return (error);1277}12781279/*1280* Call after successfully receiving properties to ensure that only the first1281* receive on or after SPA_VERSION_RECVD_PROPS blows away local properties.1282*/1283int1284dsl_prop_set_hasrecvd(const char *dsname)1285{1286int error = 0;1287if (!dsl_prop_get_hasrecvd(dsname))1288error = dsl_prop_set_hasrecvd_impl(dsname, ZPROP_SRC_LOCAL);1289return (error);1290}12911292void1293dsl_prop_unset_hasrecvd(const char *dsname)1294{1295VERIFY0(dsl_prop_set_hasrecvd_impl(dsname, ZPROP_SRC_NONE));1296}12971298int1299dsl_prop_get_all(objset_t *os, nvlist_t **nvp)1300{1301return (dsl_prop_get_all_ds(os->os_dsl_dataset, nvp, 0));1302}13031304int1305dsl_prop_get_received(const char *dsname, nvlist_t **nvp)1306{1307objset_t *os;1308int error;13091310/*1311* Received properties are not distinguishable from local properties1312* until the dataset has received properties on or after1313* SPA_VERSION_RECVD_PROPS.1314*/1315dsl_prop_getflags_t flags = (dsl_prop_get_hasrecvd(dsname) ?1316DSL_PROP_GET_RECEIVED : DSL_PROP_GET_LOCAL);13171318error = dmu_objset_hold(dsname, FTAG, &os);1319if (error != 0)1320return (error);1321error = dsl_prop_get_all_ds(os->os_dsl_dataset, nvp, flags);1322dmu_objset_rele(os, FTAG);1323return (error);1324}13251326void1327dsl_prop_nvlist_add_uint64(nvlist_t *nv, zfs_prop_t prop, uint64_t value)1328{1329nvlist_t *propval;1330const char *propname = zfs_prop_to_name(prop);1331uint64_t default_value;13321333if (nvlist_lookup_nvlist(nv, propname, &propval) == 0) {1334VERIFY0(nvlist_add_uint64(propval, ZPROP_VALUE, value));1335return;1336}13371338VERIFY0(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP));1339VERIFY0(nvlist_add_uint64(propval, ZPROP_VALUE, value));1340/* Indicate the default source if we can. */1341if (dodefault(prop, 8, 1, &default_value) == 0 &&1342value == default_value) {1343VERIFY0(nvlist_add_string(propval, ZPROP_SOURCE, ""));1344}1345VERIFY0(nvlist_add_nvlist(nv, propname, propval));1346nvlist_free(propval);1347}13481349void1350dsl_prop_nvlist_add_string(nvlist_t *nv, zfs_prop_t prop, const char *value)1351{1352nvlist_t *propval;1353const char *propname = zfs_prop_to_name(prop);13541355if (nvlist_lookup_nvlist(nv, propname, &propval) == 0) {1356VERIFY0(nvlist_add_string(propval, ZPROP_VALUE, value));1357return;1358}13591360VERIFY0(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP));1361VERIFY0(nvlist_add_string(propval, ZPROP_VALUE, value));1362VERIFY0(nvlist_add_nvlist(nv, propname, propval));1363nvlist_free(propval);1364}13651366#if defined(_KERNEL)1367EXPORT_SYMBOL(dsl_prop_register);1368EXPORT_SYMBOL(dsl_prop_unregister);1369EXPORT_SYMBOL(dsl_prop_unregister_all);1370EXPORT_SYMBOL(dsl_prop_get);1371EXPORT_SYMBOL(dsl_prop_get_integer);1372EXPORT_SYMBOL(dsl_prop_get_all);1373EXPORT_SYMBOL(dsl_prop_get_received);1374EXPORT_SYMBOL(dsl_prop_get_ds);1375EXPORT_SYMBOL(dsl_prop_get_int_ds);1376EXPORT_SYMBOL(dsl_prop_get_dd);1377EXPORT_SYMBOL(dsl_props_set);1378EXPORT_SYMBOL(dsl_prop_set_int);1379EXPORT_SYMBOL(dsl_prop_set_string);1380EXPORT_SYMBOL(dsl_prop_inherit);1381EXPORT_SYMBOL(dsl_prop_predict);1382EXPORT_SYMBOL(dsl_prop_nvlist_add_uint64);1383EXPORT_SYMBOL(dsl_prop_nvlist_add_string);1384#endif138513861387