xref: /onnv-gate/usr/src/uts/common/fs/zfs/dsl_prop.c (revision 12296:7cf402a7f374)
1789Sahrens /*
2789Sahrens  * CDDL HEADER START
3789Sahrens  *
4789Sahrens  * The contents of this file are subject to the terms of the
51544Seschrock  * Common Development and Distribution License (the "License").
61544Seschrock  * You may not use this file except in compliance with the License.
7789Sahrens  *
8789Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9789Sahrens  * or http://www.opensolaris.org/os/licensing.
10789Sahrens  * See the License for the specific language governing permissions
11789Sahrens  * and limitations under the License.
12789Sahrens  *
13789Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14789Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15789Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16789Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17789Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18789Sahrens  *
19789Sahrens  * CDDL HEADER END
20789Sahrens  */
21789Sahrens /*
22*12296SLin.Ling@Sun.COM  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23789Sahrens  */
24789Sahrens 
2511022STom.Erickson@Sun.COM #include <sys/zfs_context.h>
26789Sahrens #include <sys/dmu.h>
271356Seschrock #include <sys/dmu_objset.h>
28789Sahrens #include <sys/dmu_tx.h>
29789Sahrens #include <sys/dsl_dataset.h>
30789Sahrens #include <sys/dsl_dir.h>
31789Sahrens #include <sys/dsl_prop.h>
322199Sahrens #include <sys/dsl_synctask.h>
33789Sahrens #include <sys/spa.h>
34789Sahrens #include <sys/zap.h>
35789Sahrens #include <sys/fs/zfs.h>
36789Sahrens 
37789Sahrens #include "zfs_prop.h"
38789Sahrens 
3911022STom.Erickson@Sun.COM #define	ZPROP_INHERIT_SUFFIX "$inherit"
4011022STom.Erickson@Sun.COM #define	ZPROP_RECVD_SUFFIX "$recvd"
4111022STom.Erickson@Sun.COM 
42789Sahrens static int
4311022STom.Erickson@Sun.COM dodefault(const char *propname, int intsz, int numints, void *buf)
44789Sahrens {
45789Sahrens 	zfs_prop_t prop;
46789Sahrens 
475331Samw 	/*
485331Samw 	 * The setonce properties are read-only, BUT they still
495331Samw 	 * have a default value that can be used as the initial
505331Samw 	 * value.
515331Samw 	 */
525094Slling 	if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL ||
535331Samw 	    (zfs_prop_readonly(prop) && !zfs_prop_setonce(prop)))
54789Sahrens 		return (ENOENT);
55789Sahrens 
564787Sahrens 	if (zfs_prop_get_type(prop) == PROP_TYPE_STRING) {
57789Sahrens 		if (intsz != 1)
58789Sahrens 			return (EOVERFLOW);
595094Slling 		(void) strncpy(buf, zfs_prop_default_string(prop),
6011022STom.Erickson@Sun.COM 		    numints);
61789Sahrens 	} else {
6211022STom.Erickson@Sun.COM 		if (intsz != 8 || numints < 1)
63789Sahrens 			return (EOVERFLOW);
64789Sahrens 
65789Sahrens 		*(uint64_t *)buf = zfs_prop_default_numeric(prop);
66789Sahrens 	}
67789Sahrens 
68789Sahrens 	return (0);
69789Sahrens }
70789Sahrens 
717265Sahrens int
727265Sahrens dsl_prop_get_dd(dsl_dir_t *dd, const char *propname,
7311022STom.Erickson@Sun.COM     int intsz, int numints, void *buf, char *setpoint, boolean_t snapshot)
74789Sahrens {
752082Seschrock 	int err = ENOENT;
7611022STom.Erickson@Sun.COM 	dsl_dir_t *target = dd;
777265Sahrens 	objset_t *mos = dd->dd_pool->dp_meta_objset;
782676Seschrock 	zfs_prop_t prop;
7911022STom.Erickson@Sun.COM 	boolean_t inheritable;
8011022STom.Erickson@Sun.COM 	boolean_t inheriting = B_FALSE;
8111022STom.Erickson@Sun.COM 	char *inheritstr;
8211022STom.Erickson@Sun.COM 	char *recvdstr;
83789Sahrens 
847265Sahrens 	ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock));
857265Sahrens 
86789Sahrens 	if (setpoint)
87789Sahrens 		setpoint[0] = '\0';
88789Sahrens 
892676Seschrock 	prop = zfs_name_to_prop(propname);
9011022STom.Erickson@Sun.COM 	inheritable = (prop == ZPROP_INVAL || zfs_prop_inheritable(prop));
9111022STom.Erickson@Sun.COM 	inheritstr = kmem_asprintf("%s%s", propname, ZPROP_INHERIT_SUFFIX);
9211022STom.Erickson@Sun.COM 	recvdstr = kmem_asprintf("%s%s", propname, ZPROP_RECVD_SUFFIX);
932676Seschrock 
942082Seschrock 	/*
9511022STom.Erickson@Sun.COM 	 * Note: dd may become NULL, therefore we shouldn't dereference it
9611022STom.Erickson@Sun.COM 	 * after this loop.
972082Seschrock 	 */
982082Seschrock 	for (; dd != NULL; dd = dd->dd_parent) {
992082Seschrock 		ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock));
10011022STom.Erickson@Sun.COM 
10111022STom.Erickson@Sun.COM 		if (dd != target || snapshot) {
10211022STom.Erickson@Sun.COM 			if (!inheritable)
10311022STom.Erickson@Sun.COM 				break;
10411022STom.Erickson@Sun.COM 			inheriting = B_TRUE;
10511022STom.Erickson@Sun.COM 		}
10611022STom.Erickson@Sun.COM 
10711022STom.Erickson@Sun.COM 		/* Check for a local value. */
10811022STom.Erickson@Sun.COM 		err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, propname,
10911022STom.Erickson@Sun.COM 		    intsz, numints, buf);
110789Sahrens 		if (err != ENOENT) {
11111022STom.Erickson@Sun.COM 			if (setpoint != NULL && err == 0)
112789Sahrens 				dsl_dir_name(dd, setpoint);
113789Sahrens 			break;
114789Sahrens 		}
1152676Seschrock 
1162676Seschrock 		/*
11711022STom.Erickson@Sun.COM 		 * Skip the check for a received value if there is an explicit
11811022STom.Erickson@Sun.COM 		 * inheritance entry.
1192676Seschrock 		 */
12011022STom.Erickson@Sun.COM 		err = zap_contains(mos, dd->dd_phys->dd_props_zapobj,
12111022STom.Erickson@Sun.COM 		    inheritstr);
12211022STom.Erickson@Sun.COM 		if (err != 0 && err != ENOENT)
1232676Seschrock 			break;
12411022STom.Erickson@Sun.COM 
12511022STom.Erickson@Sun.COM 		if (err == ENOENT) {
12611022STom.Erickson@Sun.COM 			/* Check for a received value. */
12711022STom.Erickson@Sun.COM 			err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj,
12811022STom.Erickson@Sun.COM 			    recvdstr, intsz, numints, buf);
12911022STom.Erickson@Sun.COM 			if (err != ENOENT) {
13011022STom.Erickson@Sun.COM 				if (setpoint != NULL && err == 0) {
13111022STom.Erickson@Sun.COM 					if (inheriting) {
13211022STom.Erickson@Sun.COM 						dsl_dir_name(dd, setpoint);
13311022STom.Erickson@Sun.COM 					} else {
13411022STom.Erickson@Sun.COM 						(void) strcpy(setpoint,
13511022STom.Erickson@Sun.COM 						    ZPROP_SOURCE_VAL_RECVD);
13611022STom.Erickson@Sun.COM 					}
13711022STom.Erickson@Sun.COM 				}
13811022STom.Erickson@Sun.COM 				break;
13911022STom.Erickson@Sun.COM 			}
14011022STom.Erickson@Sun.COM 		}
14111022STom.Erickson@Sun.COM 
14211022STom.Erickson@Sun.COM 		/*
14311022STom.Erickson@Sun.COM 		 * If we found an explicit inheritance entry, err is zero even
14411022STom.Erickson@Sun.COM 		 * though we haven't yet found the value, so reinitializing err
14511022STom.Erickson@Sun.COM 		 * at the end of the loop (instead of at the beginning) ensures
14611022STom.Erickson@Sun.COM 		 * that err has a valid post-loop value.
14711022STom.Erickson@Sun.COM 		 */
14811022STom.Erickson@Sun.COM 		err = ENOENT;
149789Sahrens 	}
15011022STom.Erickson@Sun.COM 
151789Sahrens 	if (err == ENOENT)
15211022STom.Erickson@Sun.COM 		err = dodefault(propname, intsz, numints, buf);
15311022STom.Erickson@Sun.COM 
15411022STom.Erickson@Sun.COM 	strfree(inheritstr);
15511022STom.Erickson@Sun.COM 	strfree(recvdstr);
156789Sahrens 
157789Sahrens 	return (err);
158789Sahrens }
159789Sahrens 
1607265Sahrens int
1617265Sahrens dsl_prop_get_ds(dsl_dataset_t *ds, const char *propname,
16211022STom.Erickson@Sun.COM     int intsz, int numints, void *buf, char *setpoint)
1637265Sahrens {
16411022STom.Erickson@Sun.COM 	zfs_prop_t prop = zfs_name_to_prop(propname);
16511022STom.Erickson@Sun.COM 	boolean_t inheritable;
16611022STom.Erickson@Sun.COM 	boolean_t snapshot;
16711022STom.Erickson@Sun.COM 	uint64_t zapobj;
1687265Sahrens 
16911022STom.Erickson@Sun.COM 	ASSERT(RW_LOCK_HELD(&ds->ds_dir->dd_pool->dp_config_rwlock));
17011022STom.Erickson@Sun.COM 	inheritable = (prop == ZPROP_INVAL || zfs_prop_inheritable(prop));
17111022STom.Erickson@Sun.COM 	snapshot = (ds->ds_phys != NULL && dsl_dataset_is_snapshot(ds));
17211022STom.Erickson@Sun.COM 	zapobj = (ds->ds_phys == NULL ? 0 : ds->ds_phys->ds_props_obj);
17311022STom.Erickson@Sun.COM 
17411022STom.Erickson@Sun.COM 	if (zapobj != 0) {
17511022STom.Erickson@Sun.COM 		objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
17611022STom.Erickson@Sun.COM 		int err;
17711022STom.Erickson@Sun.COM 
17811022STom.Erickson@Sun.COM 		ASSERT(snapshot);
17911022STom.Erickson@Sun.COM 
18011022STom.Erickson@Sun.COM 		/* Check for a local value. */
18111022STom.Erickson@Sun.COM 		err = zap_lookup(mos, zapobj, propname, intsz, numints, buf);
1827265Sahrens 		if (err != ENOENT) {
18311022STom.Erickson@Sun.COM 			if (setpoint != NULL && err == 0)
1847265Sahrens 				dsl_dataset_name(ds, setpoint);
1857265Sahrens 			return (err);
1867265Sahrens 		}
18711022STom.Erickson@Sun.COM 
18811022STom.Erickson@Sun.COM 		/*
18911022STom.Erickson@Sun.COM 		 * Skip the check for a received value if there is an explicit
19011022STom.Erickson@Sun.COM 		 * inheritance entry.
19111022STom.Erickson@Sun.COM 		 */
19211022STom.Erickson@Sun.COM 		if (inheritable) {
19311022STom.Erickson@Sun.COM 			char *inheritstr = kmem_asprintf("%s%s", propname,
19411022STom.Erickson@Sun.COM 			    ZPROP_INHERIT_SUFFIX);
19511022STom.Erickson@Sun.COM 			err = zap_contains(mos, zapobj, inheritstr);
19611022STom.Erickson@Sun.COM 			strfree(inheritstr);
19711022STom.Erickson@Sun.COM 			if (err != 0 && err != ENOENT)
19811022STom.Erickson@Sun.COM 				return (err);
19911022STom.Erickson@Sun.COM 		}
20011022STom.Erickson@Sun.COM 
20111022STom.Erickson@Sun.COM 		if (err == ENOENT) {
20211022STom.Erickson@Sun.COM 			/* Check for a received value. */
20311022STom.Erickson@Sun.COM 			char *recvdstr = kmem_asprintf("%s%s", propname,
20411022STom.Erickson@Sun.COM 			    ZPROP_RECVD_SUFFIX);
20511022STom.Erickson@Sun.COM 			err = zap_lookup(mos, zapobj, recvdstr,
20611022STom.Erickson@Sun.COM 			    intsz, numints, buf);
20711022STom.Erickson@Sun.COM 			strfree(recvdstr);
20811022STom.Erickson@Sun.COM 			if (err != ENOENT) {
20911022STom.Erickson@Sun.COM 				if (setpoint != NULL && err == 0)
21011022STom.Erickson@Sun.COM 					(void) strcpy(setpoint,
21111022STom.Erickson@Sun.COM 					    ZPROP_SOURCE_VAL_RECVD);
21211022STom.Erickson@Sun.COM 				return (err);
21311022STom.Erickson@Sun.COM 			}
21411022STom.Erickson@Sun.COM 		}
2157265Sahrens 	}
2167265Sahrens 
2177265Sahrens 	return (dsl_prop_get_dd(ds->ds_dir, propname,
21811022STom.Erickson@Sun.COM 	    intsz, numints, buf, setpoint, snapshot));
2197265Sahrens }
2207265Sahrens 
221789Sahrens /*
222789Sahrens  * Register interest in the named property.  We'll call the callback
223789Sahrens  * once to notify it of the current property value, and again each time
224789Sahrens  * the property changes, until this callback is unregistered.
225789Sahrens  *
226789Sahrens  * Return 0 on success, errno if the prop is not an integer value.
227789Sahrens  */
228789Sahrens int
229789Sahrens dsl_prop_register(dsl_dataset_t *ds, const char *propname,
230789Sahrens     dsl_prop_changed_cb_t *callback, void *cbarg)
231789Sahrens {
2322082Seschrock 	dsl_dir_t *dd = ds->ds_dir;
2337265Sahrens 	dsl_pool_t *dp = dd->dd_pool;
234789Sahrens 	uint64_t value;
235789Sahrens 	dsl_prop_cb_record_t *cbr;
236789Sahrens 	int err;
2372199Sahrens 	int need_rwlock;
238789Sahrens 
2397265Sahrens 	need_rwlock = !RW_WRITE_HELD(&dp->dp_config_rwlock);
2402199Sahrens 	if (need_rwlock)
2417265Sahrens 		rw_enter(&dp->dp_config_rwlock, RW_READER);
242789Sahrens 
2437265Sahrens 	err = dsl_prop_get_ds(ds, propname, 8, 1, &value, NULL);
244789Sahrens 	if (err != 0) {
2455569Sck153898 		if (need_rwlock)
2467265Sahrens 			rw_exit(&dp->dp_config_rwlock);
247789Sahrens 		return (err);
248789Sahrens 	}
249789Sahrens 
250789Sahrens 	cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP);
2512082Seschrock 	cbr->cbr_ds = ds;
252789Sahrens 	cbr->cbr_propname = kmem_alloc(strlen(propname)+1, KM_SLEEP);
253789Sahrens 	(void) strcpy((char *)cbr->cbr_propname, propname);
254789Sahrens 	cbr->cbr_func = callback;
255789Sahrens 	cbr->cbr_arg = cbarg;
256789Sahrens 	mutex_enter(&dd->dd_lock);
257789Sahrens 	list_insert_head(&dd->dd_prop_cbs, cbr);
258789Sahrens 	mutex_exit(&dd->dd_lock);
259789Sahrens 
260789Sahrens 	cbr->cbr_func(cbr->cbr_arg, value);
261789Sahrens 
2622199Sahrens 	if (need_rwlock)
2637265Sahrens 		rw_exit(&dp->dp_config_rwlock);
264789Sahrens 	return (0);
265789Sahrens }
266789Sahrens 
267789Sahrens int
2687265Sahrens dsl_prop_get(const char *dsname, const char *propname,
2694543Smarks     int intsz, int numints, void *buf, char *setpoint)
2704543Smarks {
2717265Sahrens 	dsl_dataset_t *ds;
272789Sahrens 	int err;
273789Sahrens 
2747265Sahrens 	err = dsl_dataset_hold(dsname, FTAG, &ds);
2751544Seschrock 	if (err)
2761544Seschrock 		return (err);
277789Sahrens 
2787265Sahrens 	rw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER);
2797265Sahrens 	err = dsl_prop_get_ds(ds, propname, intsz, numints, buf, setpoint);
2807265Sahrens 	rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock);
281789Sahrens 
2827265Sahrens 	dsl_dataset_rele(ds, FTAG);
283789Sahrens 	return (err);
284789Sahrens }
285789Sahrens 
286789Sahrens /*
287789Sahrens  * Get the current property value.  It may have changed by the time this
288789Sahrens  * function returns, so it is NOT safe to follow up with
289789Sahrens  * dsl_prop_register() and assume that the value has not changed in
290789Sahrens  * between.
291789Sahrens  *
292789Sahrens  * Return 0 on success, ENOENT if ddname is invalid.
293789Sahrens  */
294789Sahrens int
295789Sahrens dsl_prop_get_integer(const char *ddname, const char *propname,
296789Sahrens     uint64_t *valuep, char *setpoint)
297789Sahrens {
298789Sahrens 	return (dsl_prop_get(ddname, propname, 8, 1, valuep, setpoint));
299789Sahrens }
300789Sahrens 
30111022STom.Erickson@Sun.COM void
30211022STom.Erickson@Sun.COM dsl_prop_setarg_init_uint64(dsl_prop_setarg_t *psa, const char *propname,
30311022STom.Erickson@Sun.COM     zprop_source_t source, uint64_t *value)
30411022STom.Erickson@Sun.COM {
30511022STom.Erickson@Sun.COM 	psa->psa_name = propname;
30611022STom.Erickson@Sun.COM 	psa->psa_source = source;
30711022STom.Erickson@Sun.COM 	psa->psa_intsz = 8;
30811022STom.Erickson@Sun.COM 	psa->psa_numints = 1;
30911022STom.Erickson@Sun.COM 	psa->psa_value = value;
31011022STom.Erickson@Sun.COM 
31111022STom.Erickson@Sun.COM 	psa->psa_effective_value = -1ULL;
31211022STom.Erickson@Sun.COM }
31311022STom.Erickson@Sun.COM 
31411022STom.Erickson@Sun.COM /*
31511022STom.Erickson@Sun.COM  * Predict the effective value of the given special property if it were set with
31611022STom.Erickson@Sun.COM  * the given value and source. This is not a general purpose function. It exists
31711022STom.Erickson@Sun.COM  * only to handle the special requirements of the quota and reservation
31811022STom.Erickson@Sun.COM  * properties. The fact that these properties are non-inheritable greatly
31911022STom.Erickson@Sun.COM  * simplifies the prediction logic.
32011022STom.Erickson@Sun.COM  *
32111022STom.Erickson@Sun.COM  * Returns 0 on success, a positive error code on failure, or -1 if called with
32211022STom.Erickson@Sun.COM  * a property not handled by this function.
32311022STom.Erickson@Sun.COM  */
32411022STom.Erickson@Sun.COM int
32511022STom.Erickson@Sun.COM dsl_prop_predict_sync(dsl_dir_t *dd, dsl_prop_setarg_t *psa)
32611022STom.Erickson@Sun.COM {
32711022STom.Erickson@Sun.COM 	const char *propname = psa->psa_name;
32811022STom.Erickson@Sun.COM 	zfs_prop_t prop = zfs_name_to_prop(propname);
32911022STom.Erickson@Sun.COM 	zprop_source_t source = psa->psa_source;
33011022STom.Erickson@Sun.COM 	objset_t *mos;
33111022STom.Erickson@Sun.COM 	uint64_t zapobj;
33211022STom.Erickson@Sun.COM 	uint64_t version;
33311022STom.Erickson@Sun.COM 	char *recvdstr;
33411022STom.Erickson@Sun.COM 	int err = 0;
33511022STom.Erickson@Sun.COM 
33611022STom.Erickson@Sun.COM 	switch (prop) {
33711022STom.Erickson@Sun.COM 	case ZFS_PROP_QUOTA:
33811022STom.Erickson@Sun.COM 	case ZFS_PROP_RESERVATION:
33911022STom.Erickson@Sun.COM 	case ZFS_PROP_REFQUOTA:
34011022STom.Erickson@Sun.COM 	case ZFS_PROP_REFRESERVATION:
34111022STom.Erickson@Sun.COM 		break;
34211022STom.Erickson@Sun.COM 	default:
34311022STom.Erickson@Sun.COM 		return (-1);
34411022STom.Erickson@Sun.COM 	}
34511022STom.Erickson@Sun.COM 
34611022STom.Erickson@Sun.COM 	mos = dd->dd_pool->dp_meta_objset;
34711022STom.Erickson@Sun.COM 	zapobj = dd->dd_phys->dd_props_zapobj;
34811022STom.Erickson@Sun.COM 	recvdstr = kmem_asprintf("%s%s", propname, ZPROP_RECVD_SUFFIX);
34911022STom.Erickson@Sun.COM 
35011022STom.Erickson@Sun.COM 	version = spa_version(dd->dd_pool->dp_spa);
35111022STom.Erickson@Sun.COM 	if (version < SPA_VERSION_RECVD_PROPS) {
35211022STom.Erickson@Sun.COM 		if (source & ZPROP_SRC_NONE)
35311022STom.Erickson@Sun.COM 			source = ZPROP_SRC_NONE;
35411022STom.Erickson@Sun.COM 		else if (source & ZPROP_SRC_RECEIVED)
35511022STom.Erickson@Sun.COM 			source = ZPROP_SRC_LOCAL;
35611022STom.Erickson@Sun.COM 	}
35711022STom.Erickson@Sun.COM 
35811022STom.Erickson@Sun.COM 	switch (source) {
35911022STom.Erickson@Sun.COM 	case ZPROP_SRC_NONE:
36011022STom.Erickson@Sun.COM 		/* Revert to the received value, if any. */
36111022STom.Erickson@Sun.COM 		err = zap_lookup(mos, zapobj, recvdstr, 8, 1,
36211022STom.Erickson@Sun.COM 		    &psa->psa_effective_value);
36311022STom.Erickson@Sun.COM 		if (err == ENOENT)
36411022STom.Erickson@Sun.COM 			psa->psa_effective_value = 0;
36511022STom.Erickson@Sun.COM 		break;
36611022STom.Erickson@Sun.COM 	case ZPROP_SRC_LOCAL:
36711022STom.Erickson@Sun.COM 		psa->psa_effective_value = *(uint64_t *)psa->psa_value;
36811022STom.Erickson@Sun.COM 		break;
36911022STom.Erickson@Sun.COM 	case ZPROP_SRC_RECEIVED:
37011022STom.Erickson@Sun.COM 		/*
37111022STom.Erickson@Sun.COM 		 * If there's no local setting, then the new received value will
37211022STom.Erickson@Sun.COM 		 * be the effective value.
37311022STom.Erickson@Sun.COM 		 */
37411022STom.Erickson@Sun.COM 		err = zap_lookup(mos, zapobj, propname, 8, 1,
37511022STom.Erickson@Sun.COM 		    &psa->psa_effective_value);
37611022STom.Erickson@Sun.COM 		if (err == ENOENT)
37711022STom.Erickson@Sun.COM 			psa->psa_effective_value = *(uint64_t *)psa->psa_value;
37811022STom.Erickson@Sun.COM 		break;
37911022STom.Erickson@Sun.COM 	case (ZPROP_SRC_NONE | ZPROP_SRC_RECEIVED):
38011022STom.Erickson@Sun.COM 		/*
38111022STom.Erickson@Sun.COM 		 * We're clearing the received value, so the local setting (if
38211022STom.Erickson@Sun.COM 		 * it exists) remains the effective value.
38311022STom.Erickson@Sun.COM 		 */
38411022STom.Erickson@Sun.COM 		err = zap_lookup(mos, zapobj, propname, 8, 1,
38511022STom.Erickson@Sun.COM 		    &psa->psa_effective_value);
38611022STom.Erickson@Sun.COM 		if (err == ENOENT)
38711022STom.Erickson@Sun.COM 			psa->psa_effective_value = 0;
38811022STom.Erickson@Sun.COM 		break;
38911022STom.Erickson@Sun.COM 	default:
39011022STom.Erickson@Sun.COM 		cmn_err(CE_PANIC, "unexpected property source: %d", source);
39111022STom.Erickson@Sun.COM 	}
39211022STom.Erickson@Sun.COM 
39311022STom.Erickson@Sun.COM 	strfree(recvdstr);
39411022STom.Erickson@Sun.COM 
39511022STom.Erickson@Sun.COM 	if (err == ENOENT)
39611022STom.Erickson@Sun.COM 		return (0);
39711022STom.Erickson@Sun.COM 
39811022STom.Erickson@Sun.COM 	return (err);
39911022STom.Erickson@Sun.COM }
40011022STom.Erickson@Sun.COM 
40111022STom.Erickson@Sun.COM #ifdef	ZFS_DEBUG
40211022STom.Erickson@Sun.COM void
40311022STom.Erickson@Sun.COM dsl_prop_check_prediction(dsl_dir_t *dd, dsl_prop_setarg_t *psa)
40411022STom.Erickson@Sun.COM {
40511022STom.Erickson@Sun.COM 	zfs_prop_t prop = zfs_name_to_prop(psa->psa_name);
40611022STom.Erickson@Sun.COM 	uint64_t intval;
40711022STom.Erickson@Sun.COM 	char setpoint[MAXNAMELEN];
40811022STom.Erickson@Sun.COM 	uint64_t version = spa_version(dd->dd_pool->dp_spa);
40911022STom.Erickson@Sun.COM 	int err;
41011022STom.Erickson@Sun.COM 
41111022STom.Erickson@Sun.COM 	if (version < SPA_VERSION_RECVD_PROPS) {
41211022STom.Erickson@Sun.COM 		switch (prop) {
41311022STom.Erickson@Sun.COM 		case ZFS_PROP_QUOTA:
41411022STom.Erickson@Sun.COM 		case ZFS_PROP_RESERVATION:
41511022STom.Erickson@Sun.COM 			return;
41611022STom.Erickson@Sun.COM 		}
41711022STom.Erickson@Sun.COM 	}
41811022STom.Erickson@Sun.COM 
41911022STom.Erickson@Sun.COM 	err = dsl_prop_get_dd(dd, psa->psa_name, 8, 1, &intval,
42011022STom.Erickson@Sun.COM 	    setpoint, B_FALSE);
42111022STom.Erickson@Sun.COM 	if (err == 0 && intval != psa->psa_effective_value) {
42211022STom.Erickson@Sun.COM 		cmn_err(CE_PANIC, "%s property, source: %x, "
42311022STom.Erickson@Sun.COM 		    "predicted effective value: %llu, "
42411022STom.Erickson@Sun.COM 		    "actual effective value: %llu (setpoint: %s)",
42511022STom.Erickson@Sun.COM 		    psa->psa_name, psa->psa_source,
42611022STom.Erickson@Sun.COM 		    (unsigned long long)psa->psa_effective_value,
42711022STom.Erickson@Sun.COM 		    (unsigned long long)intval, setpoint);
42811022STom.Erickson@Sun.COM 	}
42911022STom.Erickson@Sun.COM }
43011022STom.Erickson@Sun.COM #endif
43111022STom.Erickson@Sun.COM 
432789Sahrens /*
433789Sahrens  * Unregister this callback.  Return 0 on success, ENOENT if ddname is
434789Sahrens  * invalid, ENOMSG if no matching callback registered.
435789Sahrens  */
436789Sahrens int
437789Sahrens dsl_prop_unregister(dsl_dataset_t *ds, const char *propname,
438789Sahrens     dsl_prop_changed_cb_t *callback, void *cbarg)
439789Sahrens {
4402082Seschrock 	dsl_dir_t *dd = ds->ds_dir;
441789Sahrens 	dsl_prop_cb_record_t *cbr;
442789Sahrens 
443789Sahrens 	mutex_enter(&dd->dd_lock);
444789Sahrens 	for (cbr = list_head(&dd->dd_prop_cbs);
445789Sahrens 	    cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
4462082Seschrock 		if (cbr->cbr_ds == ds &&
447789Sahrens 		    cbr->cbr_func == callback &&
4482082Seschrock 		    cbr->cbr_arg == cbarg &&
4492082Seschrock 		    strcmp(cbr->cbr_propname, propname) == 0)
450789Sahrens 			break;
451789Sahrens 	}
452789Sahrens 
453789Sahrens 	if (cbr == NULL) {
454789Sahrens 		mutex_exit(&dd->dd_lock);
455789Sahrens 		return (ENOMSG);
456789Sahrens 	}
457789Sahrens 
458789Sahrens 	list_remove(&dd->dd_prop_cbs, cbr);
459789Sahrens 	mutex_exit(&dd->dd_lock);
460789Sahrens 	kmem_free((void*)cbr->cbr_propname, strlen(cbr->cbr_propname)+1);
461789Sahrens 	kmem_free(cbr, sizeof (dsl_prop_cb_record_t));
462789Sahrens 
463789Sahrens 	return (0);
464789Sahrens }
465789Sahrens 
4662082Seschrock /*
4672082Seschrock  * Return the number of callbacks that are registered for this dataset.
4682082Seschrock  */
4692082Seschrock int
4702082Seschrock dsl_prop_numcb(dsl_dataset_t *ds)
4712082Seschrock {
4722082Seschrock 	dsl_dir_t *dd = ds->ds_dir;
4732082Seschrock 	dsl_prop_cb_record_t *cbr;
4742082Seschrock 	int num = 0;
4752082Seschrock 
4762082Seschrock 	mutex_enter(&dd->dd_lock);
4772082Seschrock 	for (cbr = list_head(&dd->dd_prop_cbs);
4782082Seschrock 	    cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
4792082Seschrock 		if (cbr->cbr_ds == ds)
4802082Seschrock 			num++;
4812082Seschrock 	}
4822082Seschrock 	mutex_exit(&dd->dd_lock);
4832082Seschrock 
4842082Seschrock 	return (num);
4852082Seschrock }
4862082Seschrock 
487789Sahrens static void
488789Sahrens dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj,
489789Sahrens     const char *propname, uint64_t value, int first)
490789Sahrens {
491789Sahrens 	dsl_dir_t *dd;
492789Sahrens 	dsl_prop_cb_record_t *cbr;
493789Sahrens 	objset_t *mos = dp->dp_meta_objset;
4942199Sahrens 	zap_cursor_t zc;
4956047Sahrens 	zap_attribute_t *za;
496789Sahrens 	int err;
497789Sahrens 
498789Sahrens 	ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock));
4991544Seschrock 	err = dsl_dir_open_obj(dp, ddobj, NULL, FTAG, &dd);
5001544Seschrock 	if (err)
5011544Seschrock 		return;
502789Sahrens 
503789Sahrens 	if (!first) {
504789Sahrens 		/*
505789Sahrens 		 * If the prop is set here, then this change is not
506789Sahrens 		 * being inherited here or below; stop the recursion.
507789Sahrens 		 */
50811022STom.Erickson@Sun.COM 		err = zap_contains(mos, dd->dd_phys->dd_props_zapobj, propname);
509789Sahrens 		if (err == 0) {
510789Sahrens 			dsl_dir_close(dd, FTAG);
511789Sahrens 			return;
512789Sahrens 		}
513789Sahrens 		ASSERT3U(err, ==, ENOENT);
514789Sahrens 	}
515789Sahrens 
516789Sahrens 	mutex_enter(&dd->dd_lock);
5177265Sahrens 	for (cbr = list_head(&dd->dd_prop_cbs); cbr;
5187265Sahrens 	    cbr = list_next(&dd->dd_prop_cbs, cbr)) {
5197265Sahrens 		uint64_t propobj = cbr->cbr_ds->ds_phys->ds_props_obj;
5207265Sahrens 
5217265Sahrens 		if (strcmp(cbr->cbr_propname, propname) != 0)
5227265Sahrens 			continue;
5237265Sahrens 
5247265Sahrens 		/*
5257265Sahrens 		 * If the property is set on this ds, then it is not
5267265Sahrens 		 * inherited here; don't call the callback.
5277265Sahrens 		 */
52811022STom.Erickson@Sun.COM 		if (propobj && 0 == zap_contains(mos, propobj, propname))
5297265Sahrens 			continue;
5307265Sahrens 
5317265Sahrens 		cbr->cbr_func(cbr->cbr_arg, value);
532789Sahrens 	}
533789Sahrens 	mutex_exit(&dd->dd_lock);
534789Sahrens 
5356047Sahrens 	za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP);
5362199Sahrens 	for (zap_cursor_init(&zc, mos,
5372199Sahrens 	    dd->dd_phys->dd_child_dir_zapobj);
5386047Sahrens 	    zap_cursor_retrieve(&zc, za) == 0;
5392199Sahrens 	    zap_cursor_advance(&zc)) {
5406047Sahrens 		dsl_prop_changed_notify(dp, za->za_first_integer,
5412199Sahrens 		    propname, value, FALSE);
542789Sahrens 	}
5436047Sahrens 	kmem_free(za, sizeof (zap_attribute_t));
5442199Sahrens 	zap_cursor_fini(&zc);
545789Sahrens 	dsl_dir_close(dd, FTAG);
546789Sahrens }
547789Sahrens 
54811022STom.Erickson@Sun.COM void
549*12296SLin.Ling@Sun.COM dsl_prop_set_sync(void *arg1, void *arg2, dmu_tx_t *tx)
550789Sahrens {
5517265Sahrens 	dsl_dataset_t *ds = arg1;
55211022STom.Erickson@Sun.COM 	dsl_prop_setarg_t *psa = arg2;
5537265Sahrens 	objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
55411022STom.Erickson@Sun.COM 	uint64_t zapobj, intval, dummy;
5552199Sahrens 	int isint;
5564543Smarks 	char valbuf[32];
55711022STom.Erickson@Sun.COM 	char *valstr = NULL;
55811022STom.Erickson@Sun.COM 	char *inheritstr;
55911022STom.Erickson@Sun.COM 	char *recvdstr;
56011022STom.Erickson@Sun.COM 	char *tbuf = NULL;
56111022STom.Erickson@Sun.COM 	int err;
56211022STom.Erickson@Sun.COM 	uint64_t version = spa_version(ds->ds_dir->dd_pool->dp_spa);
56311022STom.Erickson@Sun.COM 	const char *propname = psa->psa_name;
56411022STom.Erickson@Sun.COM 	zprop_source_t source = psa->psa_source;
565789Sahrens 
56611022STom.Erickson@Sun.COM 	isint = (dodefault(propname, 8, 1, &intval) == 0);
56711022STom.Erickson@Sun.COM 
56811022STom.Erickson@Sun.COM 	if (ds->ds_phys != NULL && dsl_dataset_is_snapshot(ds)) {
56911022STom.Erickson@Sun.COM 		ASSERT(version >= SPA_VERSION_SNAP_PROPS);
5707265Sahrens 		if (ds->ds_phys->ds_props_obj == 0) {
5717265Sahrens 			dmu_buf_will_dirty(ds->ds_dbuf, tx);
5727265Sahrens 			ds->ds_phys->ds_props_obj =
5737265Sahrens 			    zap_create(mos,
5747265Sahrens 			    DMU_OT_DSL_PROPS, DMU_OT_NONE, 0, tx);
5757265Sahrens 		}
5767265Sahrens 		zapobj = ds->ds_phys->ds_props_obj;
5777265Sahrens 	} else {
5787265Sahrens 		zapobj = ds->ds_dir->dd_phys->dd_props_zapobj;
5797265Sahrens 	}
5807265Sahrens 
58111022STom.Erickson@Sun.COM 	if (version < SPA_VERSION_RECVD_PROPS) {
58211022STom.Erickson@Sun.COM 		zfs_prop_t prop = zfs_name_to_prop(propname);
58311022STom.Erickson@Sun.COM 		if (prop == ZFS_PROP_QUOTA || prop == ZFS_PROP_RESERVATION)
58411022STom.Erickson@Sun.COM 			return;
58511022STom.Erickson@Sun.COM 
58611022STom.Erickson@Sun.COM 		if (source & ZPROP_SRC_NONE)
58711022STom.Erickson@Sun.COM 			source = ZPROP_SRC_NONE;
58811022STom.Erickson@Sun.COM 		else if (source & ZPROP_SRC_RECEIVED)
58911022STom.Erickson@Sun.COM 			source = ZPROP_SRC_LOCAL;
590789Sahrens 	}
591789Sahrens 
59211022STom.Erickson@Sun.COM 	inheritstr = kmem_asprintf("%s%s", propname, ZPROP_INHERIT_SUFFIX);
59311022STom.Erickson@Sun.COM 	recvdstr = kmem_asprintf("%s%s", propname, ZPROP_RECVD_SUFFIX);
59411022STom.Erickson@Sun.COM 
59511022STom.Erickson@Sun.COM 	switch (source) {
59611022STom.Erickson@Sun.COM 	case ZPROP_SRC_NONE:
59711022STom.Erickson@Sun.COM 		/*
59811022STom.Erickson@Sun.COM 		 * revert to received value, if any (inherit -S)
59911022STom.Erickson@Sun.COM 		 * - remove propname
60011022STom.Erickson@Sun.COM 		 * - remove propname$inherit
60111022STom.Erickson@Sun.COM 		 */
60211022STom.Erickson@Sun.COM 		err = zap_remove(mos, zapobj, propname, tx);
60311022STom.Erickson@Sun.COM 		ASSERT(err == 0 || err == ENOENT);
60411022STom.Erickson@Sun.COM 		err = zap_remove(mos, zapobj, inheritstr, tx);
60511022STom.Erickson@Sun.COM 		ASSERT(err == 0 || err == ENOENT);
60611022STom.Erickson@Sun.COM 		break;
60711022STom.Erickson@Sun.COM 	case ZPROP_SRC_LOCAL:
60811022STom.Erickson@Sun.COM 		/*
60911022STom.Erickson@Sun.COM 		 * remove propname$inherit
61011022STom.Erickson@Sun.COM 		 * set propname -> value
61111022STom.Erickson@Sun.COM 		 */
61211022STom.Erickson@Sun.COM 		err = zap_remove(mos, zapobj, inheritstr, tx);
61311022STom.Erickson@Sun.COM 		ASSERT(err == 0 || err == ENOENT);
61411022STom.Erickson@Sun.COM 		VERIFY(0 == zap_update(mos, zapobj, propname,
61511022STom.Erickson@Sun.COM 		    psa->psa_intsz, psa->psa_numints, psa->psa_value, tx));
61611022STom.Erickson@Sun.COM 		break;
61711022STom.Erickson@Sun.COM 	case ZPROP_SRC_INHERITED:
61811022STom.Erickson@Sun.COM 		/*
61911022STom.Erickson@Sun.COM 		 * explicitly inherit
62011022STom.Erickson@Sun.COM 		 * - remove propname
62111022STom.Erickson@Sun.COM 		 * - set propname$inherit
62211022STom.Erickson@Sun.COM 		 */
62311022STom.Erickson@Sun.COM 		err = zap_remove(mos, zapobj, propname, tx);
62411022STom.Erickson@Sun.COM 		ASSERT(err == 0 || err == ENOENT);
62511022STom.Erickson@Sun.COM 		if (version >= SPA_VERSION_RECVD_PROPS &&
62611022STom.Erickson@Sun.COM 		    zap_contains(mos, zapobj, ZPROP_HAS_RECVD) == 0) {
62711022STom.Erickson@Sun.COM 			dummy = 0;
62811022STom.Erickson@Sun.COM 			err = zap_update(mos, zapobj, inheritstr,
62911022STom.Erickson@Sun.COM 			    8, 1, &dummy, tx);
63011022STom.Erickson@Sun.COM 			ASSERT(err == 0);
63111022STom.Erickson@Sun.COM 		}
63211022STom.Erickson@Sun.COM 		break;
63311022STom.Erickson@Sun.COM 	case ZPROP_SRC_RECEIVED:
63411022STom.Erickson@Sun.COM 		/*
63511022STom.Erickson@Sun.COM 		 * set propname$recvd -> value
63611022STom.Erickson@Sun.COM 		 */
63711022STom.Erickson@Sun.COM 		err = zap_update(mos, zapobj, recvdstr,
63811022STom.Erickson@Sun.COM 		    psa->psa_intsz, psa->psa_numints, psa->psa_value, tx);
63911022STom.Erickson@Sun.COM 		ASSERT(err == 0);
64011022STom.Erickson@Sun.COM 		break;
64111022STom.Erickson@Sun.COM 	case (ZPROP_SRC_NONE | ZPROP_SRC_LOCAL | ZPROP_SRC_RECEIVED):
64211022STom.Erickson@Sun.COM 		/*
64311022STom.Erickson@Sun.COM 		 * clear local and received settings
64411022STom.Erickson@Sun.COM 		 * - remove propname
64511022STom.Erickson@Sun.COM 		 * - remove propname$inherit
64611022STom.Erickson@Sun.COM 		 * - remove propname$recvd
64711022STom.Erickson@Sun.COM 		 */
64811022STom.Erickson@Sun.COM 		err = zap_remove(mos, zapobj, propname, tx);
64911022STom.Erickson@Sun.COM 		ASSERT(err == 0 || err == ENOENT);
65011022STom.Erickson@Sun.COM 		err = zap_remove(mos, zapobj, inheritstr, tx);
65111022STom.Erickson@Sun.COM 		ASSERT(err == 0 || err == ENOENT);
65211022STom.Erickson@Sun.COM 		/* FALLTHRU */
65311022STom.Erickson@Sun.COM 	case (ZPROP_SRC_NONE | ZPROP_SRC_RECEIVED):
65411022STom.Erickson@Sun.COM 		/*
65511022STom.Erickson@Sun.COM 		 * remove propname$recvd
65611022STom.Erickson@Sun.COM 		 */
65711022STom.Erickson@Sun.COM 		err = zap_remove(mos, zapobj, recvdstr, tx);
65811022STom.Erickson@Sun.COM 		ASSERT(err == 0 || err == ENOENT);
65911022STom.Erickson@Sun.COM 		break;
66011022STom.Erickson@Sun.COM 	default:
66111022STom.Erickson@Sun.COM 		cmn_err(CE_PANIC, "unexpected property source: %d", source);
66211022STom.Erickson@Sun.COM 	}
66311022STom.Erickson@Sun.COM 
66411022STom.Erickson@Sun.COM 	strfree(inheritstr);
66511022STom.Erickson@Sun.COM 	strfree(recvdstr);
66611022STom.Erickson@Sun.COM 
6672199Sahrens 	if (isint) {
66811022STom.Erickson@Sun.COM 		VERIFY(0 == dsl_prop_get_ds(ds, propname, 8, 1, &intval, NULL));
66911022STom.Erickson@Sun.COM 
67011022STom.Erickson@Sun.COM 		if (ds->ds_phys != NULL && dsl_dataset_is_snapshot(ds)) {
6717265Sahrens 			dsl_prop_cb_record_t *cbr;
6727265Sahrens 			/*
6737265Sahrens 			 * It's a snapshot; nothing can inherit this
6747265Sahrens 			 * property, so just look for callbacks on this
6757265Sahrens 			 * ds here.
6767265Sahrens 			 */
6777265Sahrens 			mutex_enter(&ds->ds_dir->dd_lock);
6787265Sahrens 			for (cbr = list_head(&ds->ds_dir->dd_prop_cbs); cbr;
6797265Sahrens 			    cbr = list_next(&ds->ds_dir->dd_prop_cbs, cbr)) {
6807265Sahrens 				if (cbr->cbr_ds == ds &&
68111022STom.Erickson@Sun.COM 				    strcmp(cbr->cbr_propname, propname) == 0)
6827265Sahrens 					cbr->cbr_func(cbr->cbr_arg, intval);
6837265Sahrens 			}
6847265Sahrens 			mutex_exit(&ds->ds_dir->dd_lock);
6857265Sahrens 		} else {
6867265Sahrens 			dsl_prop_changed_notify(ds->ds_dir->dd_pool,
68711022STom.Erickson@Sun.COM 			    ds->ds_dir->dd_object, propname, intval, TRUE);
6887265Sahrens 		}
68911022STom.Erickson@Sun.COM 
6904543Smarks 		(void) snprintf(valbuf, sizeof (valbuf),
6914543Smarks 		    "%lld", (longlong_t)intval);
6924543Smarks 		valstr = valbuf;
6934543Smarks 	} else {
69411022STom.Erickson@Sun.COM 		if (source == ZPROP_SRC_LOCAL) {
69511022STom.Erickson@Sun.COM 			valstr = (char *)psa->psa_value;
69611022STom.Erickson@Sun.COM 		} else {
69711022STom.Erickson@Sun.COM 			tbuf = kmem_alloc(ZAP_MAXVALUELEN, KM_SLEEP);
69811022STom.Erickson@Sun.COM 			if (dsl_prop_get_ds(ds, propname, 1,
69911022STom.Erickson@Sun.COM 			    ZAP_MAXVALUELEN, tbuf, NULL) == 0)
70011022STom.Erickson@Sun.COM 				valstr = tbuf;
70111022STom.Erickson@Sun.COM 		}
7024543Smarks 	}
70311022STom.Erickson@Sun.COM 
704*12296SLin.Ling@Sun.COM 	spa_history_log_internal((source == ZPROP_SRC_NONE ||
70511022STom.Erickson@Sun.COM 	    source == ZPROP_SRC_INHERITED) ? LOG_DS_INHERIT :
706*12296SLin.Ling@Sun.COM 	    LOG_DS_PROPSET, ds->ds_dir->dd_pool->dp_spa, tx,
70711022STom.Erickson@Sun.COM 	    "%s=%s dataset = %llu", propname,
70811022STom.Erickson@Sun.COM 	    (valstr == NULL ? "" : valstr), ds->ds_object);
70911022STom.Erickson@Sun.COM 
71011022STom.Erickson@Sun.COM 	if (tbuf != NULL)
71111022STom.Erickson@Sun.COM 		kmem_free(tbuf, ZAP_MAXVALUELEN);
712789Sahrens }
713789Sahrens 
7149355SMatthew.Ahrens@Sun.COM void
715*12296SLin.Ling@Sun.COM dsl_props_set_sync(void *arg1, void *arg2, dmu_tx_t *tx)
7168697SRichard.Morris@Sun.COM {
7178697SRichard.Morris@Sun.COM 	dsl_dataset_t *ds = arg1;
71811022STom.Erickson@Sun.COM 	dsl_props_arg_t *pa = arg2;
71911022STom.Erickson@Sun.COM 	nvlist_t *props = pa->pa_props;
72011022STom.Erickson@Sun.COM 	dsl_prop_setarg_t psa;
7218697SRichard.Morris@Sun.COM 	nvpair_t *elem = NULL;
7228697SRichard.Morris@Sun.COM 
72311022STom.Erickson@Sun.COM 	psa.psa_source = pa->pa_source;
7248697SRichard.Morris@Sun.COM 
72511022STom.Erickson@Sun.COM 	while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
72611022STom.Erickson@Sun.COM 		nvpair_t *pair = elem;
72711022STom.Erickson@Sun.COM 
72811022STom.Erickson@Sun.COM 		psa.psa_name = nvpair_name(pair);
7298697SRichard.Morris@Sun.COM 
73011022STom.Erickson@Sun.COM 		if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
73111022STom.Erickson@Sun.COM 			/*
73211022STom.Erickson@Sun.COM 			 * dsl_prop_get_all_impl() returns properties in this
73311022STom.Erickson@Sun.COM 			 * format.
73411022STom.Erickson@Sun.COM 			 */
73511022STom.Erickson@Sun.COM 			nvlist_t *attrs;
73611022STom.Erickson@Sun.COM 			VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
73711022STom.Erickson@Sun.COM 			VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
73811022STom.Erickson@Sun.COM 			    &pair) == 0);
73911022STom.Erickson@Sun.COM 		}
74011022STom.Erickson@Sun.COM 
74111022STom.Erickson@Sun.COM 		if (nvpair_type(pair) == DATA_TYPE_STRING) {
74211022STom.Erickson@Sun.COM 			VERIFY(nvpair_value_string(pair,
74311022STom.Erickson@Sun.COM 			    (char **)&psa.psa_value) == 0);
74411022STom.Erickson@Sun.COM 			psa.psa_intsz = 1;
74511022STom.Erickson@Sun.COM 			psa.psa_numints = strlen(psa.psa_value) + 1;
7468697SRichard.Morris@Sun.COM 		} else {
7478697SRichard.Morris@Sun.COM 			uint64_t intval;
74811022STom.Erickson@Sun.COM 			VERIFY(nvpair_value_uint64(pair, &intval) == 0);
74911022STom.Erickson@Sun.COM 			psa.psa_intsz = sizeof (intval);
75011022STom.Erickson@Sun.COM 			psa.psa_numints = 1;
75111022STom.Erickson@Sun.COM 			psa.psa_value = &intval;
7528697SRichard.Morris@Sun.COM 		}
753*12296SLin.Ling@Sun.COM 		dsl_prop_set_sync(ds, &psa, tx);
7548697SRichard.Morris@Sun.COM 	}
7558697SRichard.Morris@Sun.COM }
7568697SRichard.Morris@Sun.COM 
7575378Sck153898 void
75810242Schris.kirby@sun.com dsl_dir_prop_set_uint64_sync(dsl_dir_t *dd, const char *name, uint64_t val,
759*12296SLin.Ling@Sun.COM     dmu_tx_t *tx)
7605378Sck153898 {
7615378Sck153898 	objset_t *mos = dd->dd_pool->dp_meta_objset;
7625378Sck153898 	uint64_t zapobj = dd->dd_phys->dd_props_zapobj;
7635378Sck153898 
7645378Sck153898 	ASSERT(dmu_tx_is_syncing(tx));
7655378Sck153898 
7665378Sck153898 	VERIFY(0 == zap_update(mos, zapobj, name, sizeof (val), 1, &val, tx));
7675378Sck153898 
7685378Sck153898 	dsl_prop_changed_notify(dd->dd_pool, dd->dd_object, name, val, TRUE);
7695378Sck153898 
770*12296SLin.Ling@Sun.COM 	spa_history_log_internal(LOG_DS_PROPSET, dd->dd_pool->dp_spa, tx,
7715378Sck153898 	    "%s=%llu dataset = %llu", name, (u_longlong_t)val,
7725378Sck153898 	    dd->dd_phys->dd_head_dataset_obj);
7735378Sck153898 }
7745378Sck153898 
775789Sahrens int
77611022STom.Erickson@Sun.COM dsl_prop_set(const char *dsname, const char *propname, zprop_source_t source,
7772885Sahrens     int intsz, int numints, const void *buf)
7782885Sahrens {
7797265Sahrens 	dsl_dataset_t *ds;
7809643SEric.Taylor@Sun.COM 	uint64_t version;
7817265Sahrens 	int err;
78211022STom.Erickson@Sun.COM 	dsl_prop_setarg_t psa;
7832885Sahrens 
7842641Sahrens 	/*
7852641Sahrens 	 * We must do these checks before we get to the syncfunc, since
7862641Sahrens 	 * it can't fail.
7872641Sahrens 	 */
7882641Sahrens 	if (strlen(propname) >= ZAP_MAXNAMELEN)
7892641Sahrens 		return (ENAMETOOLONG);
7902641Sahrens 
7917265Sahrens 	err = dsl_dataset_hold(dsname, FTAG, &ds);
7921544Seschrock 	if (err)
7931544Seschrock 		return (err);
7947265Sahrens 
7959643SEric.Taylor@Sun.COM 	version = spa_version(ds->ds_dir->dd_pool->dp_spa);
7969643SEric.Taylor@Sun.COM 	if (intsz * numints >= (version < SPA_VERSION_STMF_PROP ?
7979643SEric.Taylor@Sun.COM 	    ZAP_OLDMAXVALUELEN : ZAP_MAXVALUELEN)) {
7989643SEric.Taylor@Sun.COM 		dsl_dataset_rele(ds, FTAG);
7999643SEric.Taylor@Sun.COM 		return (E2BIG);
8009643SEric.Taylor@Sun.COM 	}
8017265Sahrens 	if (dsl_dataset_is_snapshot(ds) &&
8029643SEric.Taylor@Sun.COM 	    version < SPA_VERSION_SNAP_PROPS) {
8037265Sahrens 		dsl_dataset_rele(ds, FTAG);
8047265Sahrens 		return (ENOTSUP);
8057265Sahrens 	}
8067265Sahrens 
80711022STom.Erickson@Sun.COM 	psa.psa_name = propname;
80811022STom.Erickson@Sun.COM 	psa.psa_source = source;
80911022STom.Erickson@Sun.COM 	psa.psa_intsz = intsz;
81011022STom.Erickson@Sun.COM 	psa.psa_numints = numints;
81111022STom.Erickson@Sun.COM 	psa.psa_value = buf;
81211022STom.Erickson@Sun.COM 	psa.psa_effective_value = -1ULL;
81311022STom.Erickson@Sun.COM 
8147265Sahrens 	err = dsl_sync_task_do(ds->ds_dir->dd_pool,
8157265Sahrens 	    NULL, dsl_prop_set_sync, ds, &psa, 2);
8167265Sahrens 
8177265Sahrens 	dsl_dataset_rele(ds, FTAG);
818789Sahrens 	return (err);
819789Sahrens }
8201356Seschrock 
8218697SRichard.Morris@Sun.COM int
82211022STom.Erickson@Sun.COM dsl_props_set(const char *dsname, zprop_source_t source, nvlist_t *props)
8238697SRichard.Morris@Sun.COM {
8248697SRichard.Morris@Sun.COM 	dsl_dataset_t *ds;
8259643SEric.Taylor@Sun.COM 	uint64_t version;
8268697SRichard.Morris@Sun.COM 	nvpair_t *elem = NULL;
82711022STom.Erickson@Sun.COM 	dsl_props_arg_t pa;
8288697SRichard.Morris@Sun.COM 	int err;
8298697SRichard.Morris@Sun.COM 
8309643SEric.Taylor@Sun.COM 	if (err = dsl_dataset_hold(dsname, FTAG, &ds))
8319643SEric.Taylor@Sun.COM 		return (err);
8328924SRichard.Morris@Sun.COM 	/*
8338924SRichard.Morris@Sun.COM 	 * Do these checks before the syncfunc, since it can't fail.
8348924SRichard.Morris@Sun.COM 	 */
8359643SEric.Taylor@Sun.COM 	version = spa_version(ds->ds_dir->dd_pool->dp_spa);
83611022STom.Erickson@Sun.COM 	while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
8379643SEric.Taylor@Sun.COM 		if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) {
8389643SEric.Taylor@Sun.COM 			dsl_dataset_rele(ds, FTAG);
8398924SRichard.Morris@Sun.COM 			return (ENAMETOOLONG);
8409643SEric.Taylor@Sun.COM 		}
8418697SRichard.Morris@Sun.COM 		if (nvpair_type(elem) == DATA_TYPE_STRING) {
8428697SRichard.Morris@Sun.COM 			char *valstr;
8438697SRichard.Morris@Sun.COM 			VERIFY(nvpair_value_string(elem, &valstr) == 0);
8449643SEric.Taylor@Sun.COM 			if (strlen(valstr) >= (version <
8459643SEric.Taylor@Sun.COM 			    SPA_VERSION_STMF_PROP ?
8469643SEric.Taylor@Sun.COM 			    ZAP_OLDMAXVALUELEN : ZAP_MAXVALUELEN)) {
8479643SEric.Taylor@Sun.COM 				dsl_dataset_rele(ds, FTAG);
8488924SRichard.Morris@Sun.COM 				return (E2BIG);
8499643SEric.Taylor@Sun.COM 			}
8508697SRichard.Morris@Sun.COM 		}
8518697SRichard.Morris@Sun.COM 	}
8528697SRichard.Morris@Sun.COM 
8538697SRichard.Morris@Sun.COM 	if (dsl_dataset_is_snapshot(ds) &&
8549643SEric.Taylor@Sun.COM 	    version < SPA_VERSION_SNAP_PROPS) {
8558697SRichard.Morris@Sun.COM 		dsl_dataset_rele(ds, FTAG);
8568697SRichard.Morris@Sun.COM 		return (ENOTSUP);
8578697SRichard.Morris@Sun.COM 	}
8588697SRichard.Morris@Sun.COM 
85911022STom.Erickson@Sun.COM 	pa.pa_props = props;
86011022STom.Erickson@Sun.COM 	pa.pa_source = source;
86111022STom.Erickson@Sun.COM 
8628697SRichard.Morris@Sun.COM 	err = dsl_sync_task_do(ds->ds_dir->dd_pool,
86311022STom.Erickson@Sun.COM 	    NULL, dsl_props_set_sync, ds, &pa, 2);
8648697SRichard.Morris@Sun.COM 
8658697SRichard.Morris@Sun.COM 	dsl_dataset_rele(ds, FTAG);
8668697SRichard.Morris@Sun.COM 	return (err);
8678697SRichard.Morris@Sun.COM }
8688697SRichard.Morris@Sun.COM 
86911022STom.Erickson@Sun.COM typedef enum dsl_prop_getflags {
87011022STom.Erickson@Sun.COM 	DSL_PROP_GET_INHERITING = 0x1,	/* searching parent of target ds */
87111022STom.Erickson@Sun.COM 	DSL_PROP_GET_SNAPSHOT = 0x2,	/* snapshot dataset */
87211022STom.Erickson@Sun.COM 	DSL_PROP_GET_LOCAL = 0x4,	/* local properties */
87311022STom.Erickson@Sun.COM 	DSL_PROP_GET_RECEIVED = 0x8	/* received properties */
87411022STom.Erickson@Sun.COM } dsl_prop_getflags_t;
87511022STom.Erickson@Sun.COM 
87611022STom.Erickson@Sun.COM static int
87711022STom.Erickson@Sun.COM dsl_prop_get_all_impl(objset_t *mos, uint64_t propobj,
87811022STom.Erickson@Sun.COM     const char *setpoint, dsl_prop_getflags_t flags, nvlist_t *nv)
87911022STom.Erickson@Sun.COM {
88011022STom.Erickson@Sun.COM 	zap_cursor_t zc;
88111022STom.Erickson@Sun.COM 	zap_attribute_t za;
88211022STom.Erickson@Sun.COM 	int err = 0;
88311022STom.Erickson@Sun.COM 
88411022STom.Erickson@Sun.COM 	for (zap_cursor_init(&zc, mos, propobj);
88511022STom.Erickson@Sun.COM 	    (err = zap_cursor_retrieve(&zc, &za)) == 0;
88611022STom.Erickson@Sun.COM 	    zap_cursor_advance(&zc)) {
88711022STom.Erickson@Sun.COM 		nvlist_t *propval;
88811022STom.Erickson@Sun.COM 		zfs_prop_t prop;
88911022STom.Erickson@Sun.COM 		char buf[ZAP_MAXNAMELEN];
89011022STom.Erickson@Sun.COM 		char *valstr;
89111022STom.Erickson@Sun.COM 		const char *suffix;
89211022STom.Erickson@Sun.COM 		const char *propname;
89311022STom.Erickson@Sun.COM 		const char *source;
89411022STom.Erickson@Sun.COM 
89511022STom.Erickson@Sun.COM 		suffix = strchr(za.za_name, '$');
89611022STom.Erickson@Sun.COM 
89711022STom.Erickson@Sun.COM 		if (suffix == NULL) {
89811022STom.Erickson@Sun.COM 			/*
89911022STom.Erickson@Sun.COM 			 * Skip local properties if we only want received
90011022STom.Erickson@Sun.COM 			 * properties.
90111022STom.Erickson@Sun.COM 			 */
90211022STom.Erickson@Sun.COM 			if (flags & DSL_PROP_GET_RECEIVED)
90311022STom.Erickson@Sun.COM 				continue;
90411022STom.Erickson@Sun.COM 
90511022STom.Erickson@Sun.COM 			propname = za.za_name;
90611022STom.Erickson@Sun.COM 			source = setpoint;
90711022STom.Erickson@Sun.COM 		} else if (strcmp(suffix, ZPROP_INHERIT_SUFFIX) == 0) {
90811022STom.Erickson@Sun.COM 			/* Skip explicitly inherited entries. */
90911022STom.Erickson@Sun.COM 			continue;
91011022STom.Erickson@Sun.COM 		} else if (strcmp(suffix, ZPROP_RECVD_SUFFIX) == 0) {
91111022STom.Erickson@Sun.COM 			if (flags & DSL_PROP_GET_LOCAL)
91211022STom.Erickson@Sun.COM 				continue;
91311022STom.Erickson@Sun.COM 
91411022STom.Erickson@Sun.COM 			(void) strncpy(buf, za.za_name, (suffix - za.za_name));
91511022STom.Erickson@Sun.COM 			buf[suffix - za.za_name] = '\0';
91611022STom.Erickson@Sun.COM 			propname = buf;
91711022STom.Erickson@Sun.COM 
91811022STom.Erickson@Sun.COM 			if (!(flags & DSL_PROP_GET_RECEIVED)) {
91911022STom.Erickson@Sun.COM 				/* Skip if locally overridden. */
92011022STom.Erickson@Sun.COM 				err = zap_contains(mos, propobj, propname);
92111022STom.Erickson@Sun.COM 				if (err == 0)
92211022STom.Erickson@Sun.COM 					continue;
92311022STom.Erickson@Sun.COM 				if (err != ENOENT)
92411022STom.Erickson@Sun.COM 					break;
92511022STom.Erickson@Sun.COM 
92611022STom.Erickson@Sun.COM 				/* Skip if explicitly inherited. */
92711022STom.Erickson@Sun.COM 				valstr = kmem_asprintf("%s%s", propname,
92811022STom.Erickson@Sun.COM 				    ZPROP_INHERIT_SUFFIX);
92911022STom.Erickson@Sun.COM 				err = zap_contains(mos, propobj, valstr);
93011022STom.Erickson@Sun.COM 				strfree(valstr);
93111022STom.Erickson@Sun.COM 				if (err == 0)
93211022STom.Erickson@Sun.COM 					continue;
93311022STom.Erickson@Sun.COM 				if (err != ENOENT)
93411022STom.Erickson@Sun.COM 					break;
93511022STom.Erickson@Sun.COM 			}
93611022STom.Erickson@Sun.COM 
93711022STom.Erickson@Sun.COM 			source = ((flags & DSL_PROP_GET_INHERITING) ?
93811022STom.Erickson@Sun.COM 			    setpoint : ZPROP_SOURCE_VAL_RECVD);
93911022STom.Erickson@Sun.COM 		} else {
94011022STom.Erickson@Sun.COM 			/*
94111022STom.Erickson@Sun.COM 			 * For backward compatibility, skip suffixes we don't
94211022STom.Erickson@Sun.COM 			 * recognize.
94311022STom.Erickson@Sun.COM 			 */
94411022STom.Erickson@Sun.COM 			continue;
94511022STom.Erickson@Sun.COM 		}
94611022STom.Erickson@Sun.COM 
94711022STom.Erickson@Sun.COM 		prop = zfs_name_to_prop(propname);
94811022STom.Erickson@Sun.COM 
94911022STom.Erickson@Sun.COM 		/* Skip non-inheritable properties. */
95011022STom.Erickson@Sun.COM 		if ((flags & DSL_PROP_GET_INHERITING) && prop != ZPROP_INVAL &&
95111022STom.Erickson@Sun.COM 		    !zfs_prop_inheritable(prop))
95211022STom.Erickson@Sun.COM 			continue;
95311022STom.Erickson@Sun.COM 
95411022STom.Erickson@Sun.COM 		/* Skip properties not valid for this type. */
95511022STom.Erickson@Sun.COM 		if ((flags & DSL_PROP_GET_SNAPSHOT) && prop != ZPROP_INVAL &&
95611022STom.Erickson@Sun.COM 		    !zfs_prop_valid_for_type(prop, ZFS_TYPE_SNAPSHOT))
95711022STom.Erickson@Sun.COM 			continue;
95811022STom.Erickson@Sun.COM 
95911022STom.Erickson@Sun.COM 		/* Skip properties already defined. */
96011022STom.Erickson@Sun.COM 		if (nvlist_exists(nv, propname))
96111022STom.Erickson@Sun.COM 			continue;
96211022STom.Erickson@Sun.COM 
96311022STom.Erickson@Sun.COM 		VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
96411022STom.Erickson@Sun.COM 		if (za.za_integer_length == 1) {
96511022STom.Erickson@Sun.COM 			/*
96611022STom.Erickson@Sun.COM 			 * String property
96711022STom.Erickson@Sun.COM 			 */
96811022STom.Erickson@Sun.COM 			char *tmp = kmem_alloc(za.za_num_integers,
96911022STom.Erickson@Sun.COM 			    KM_SLEEP);
97011022STom.Erickson@Sun.COM 			err = zap_lookup(mos, propobj,
97111022STom.Erickson@Sun.COM 			    za.za_name, 1, za.za_num_integers, tmp);
97211022STom.Erickson@Sun.COM 			if (err != 0) {
97311022STom.Erickson@Sun.COM 				kmem_free(tmp, za.za_num_integers);
97411022STom.Erickson@Sun.COM 				break;
97511022STom.Erickson@Sun.COM 			}
97611022STom.Erickson@Sun.COM 			VERIFY(nvlist_add_string(propval, ZPROP_VALUE,
97711022STom.Erickson@Sun.COM 			    tmp) == 0);
97811022STom.Erickson@Sun.COM 			kmem_free(tmp, za.za_num_integers);
97911022STom.Erickson@Sun.COM 		} else {
98011022STom.Erickson@Sun.COM 			/*
98111022STom.Erickson@Sun.COM 			 * Integer property
98211022STom.Erickson@Sun.COM 			 */
98311022STom.Erickson@Sun.COM 			ASSERT(za.za_integer_length == 8);
98411022STom.Erickson@Sun.COM 			(void) nvlist_add_uint64(propval, ZPROP_VALUE,
98511022STom.Erickson@Sun.COM 			    za.za_first_integer);
98611022STom.Erickson@Sun.COM 		}
98711022STom.Erickson@Sun.COM 
98811022STom.Erickson@Sun.COM 		VERIFY(nvlist_add_string(propval, ZPROP_SOURCE, source) == 0);
98911022STom.Erickson@Sun.COM 		VERIFY(nvlist_add_nvlist(nv, propname, propval) == 0);
99011022STom.Erickson@Sun.COM 		nvlist_free(propval);
99111022STom.Erickson@Sun.COM 	}
99211022STom.Erickson@Sun.COM 	zap_cursor_fini(&zc);
99311022STom.Erickson@Sun.COM 	if (err == ENOENT)
99411022STom.Erickson@Sun.COM 		err = 0;
99511022STom.Erickson@Sun.COM 	return (err);
99611022STom.Erickson@Sun.COM }
99711022STom.Erickson@Sun.COM 
9981356Seschrock /*
9991356Seschrock  * Iterate over all properties for this dataset and return them in an nvlist.
10001356Seschrock  */
100111022STom.Erickson@Sun.COM static int
100211022STom.Erickson@Sun.COM dsl_prop_get_all_ds(dsl_dataset_t *ds, nvlist_t **nvp,
100311022STom.Erickson@Sun.COM     dsl_prop_getflags_t flags)
10041356Seschrock {
10052082Seschrock 	dsl_dir_t *dd = ds->ds_dir;
10067265Sahrens 	dsl_pool_t *dp = dd->dd_pool;
10077265Sahrens 	objset_t *mos = dp->dp_meta_objset;
100811022STom.Erickson@Sun.COM 	int err = 0;
100911022STom.Erickson@Sun.COM 	char setpoint[MAXNAMELEN];
10101356Seschrock 
10111356Seschrock 	VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
10121356Seschrock 
101311022STom.Erickson@Sun.COM 	if (dsl_dataset_is_snapshot(ds))
101411022STom.Erickson@Sun.COM 		flags |= DSL_PROP_GET_SNAPSHOT;
10151356Seschrock 
10161356Seschrock 	rw_enter(&dp->dp_config_rwlock, RW_READER);
10172885Sahrens 
101811022STom.Erickson@Sun.COM 	if (ds->ds_phys->ds_props_obj != 0) {
101911022STom.Erickson@Sun.COM 		ASSERT(flags & DSL_PROP_GET_SNAPSHOT);
102011022STom.Erickson@Sun.COM 		dsl_dataset_name(ds, setpoint);
102111022STom.Erickson@Sun.COM 		err = dsl_prop_get_all_impl(mos, ds->ds_phys->ds_props_obj,
102211022STom.Erickson@Sun.COM 		    setpoint, flags, *nvp);
102311022STom.Erickson@Sun.COM 		if (err)
102411022STom.Erickson@Sun.COM 			goto out;
102511022STom.Erickson@Sun.COM 	}
10261356Seschrock 
102711022STom.Erickson@Sun.COM 	for (; dd != NULL; dd = dd->dd_parent) {
102811022STom.Erickson@Sun.COM 		if (dd != ds->ds_dir || (flags & DSL_PROP_GET_SNAPSHOT)) {
102911022STom.Erickson@Sun.COM 			if (flags & (DSL_PROP_GET_LOCAL |
103011022STom.Erickson@Sun.COM 			    DSL_PROP_GET_RECEIVED))
103111022STom.Erickson@Sun.COM 				break;
103211022STom.Erickson@Sun.COM 			flags |= DSL_PROP_GET_INHERITING;
103311022STom.Erickson@Sun.COM 		}
103411022STom.Erickson@Sun.COM 		dsl_dir_name(dd, setpoint);
103511022STom.Erickson@Sun.COM 		err = dsl_prop_get_all_impl(mos, dd->dd_phys->dd_props_zapobj,
103611022STom.Erickson@Sun.COM 		    setpoint, flags, *nvp);
103711022STom.Erickson@Sun.COM 		if (err)
103811022STom.Erickson@Sun.COM 			break;
103911022STom.Erickson@Sun.COM 	}
104011022STom.Erickson@Sun.COM out:
104111022STom.Erickson@Sun.COM 	rw_exit(&dp->dp_config_rwlock);
104211022STom.Erickson@Sun.COM 	return (err);
104311022STom.Erickson@Sun.COM }
10447265Sahrens 
104511022STom.Erickson@Sun.COM boolean_t
104611022STom.Erickson@Sun.COM dsl_prop_get_hasrecvd(objset_t *os)
104711022STom.Erickson@Sun.COM {
104811022STom.Erickson@Sun.COM 	dsl_dataset_t *ds = os->os_dsl_dataset;
104911022STom.Erickson@Sun.COM 	int rc;
105011022STom.Erickson@Sun.COM 	uint64_t dummy;
10511356Seschrock 
105211022STom.Erickson@Sun.COM 	rw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER);
105311022STom.Erickson@Sun.COM 	rc = dsl_prop_get_ds(ds, ZPROP_HAS_RECVD, 8, 1, &dummy, NULL);
105411022STom.Erickson@Sun.COM 	rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock);
105511022STom.Erickson@Sun.COM 	ASSERT(rc != 0 || spa_version(os->os_spa) >= SPA_VERSION_RECVD_PROPS);
105611022STom.Erickson@Sun.COM 	return (rc == 0);
105711022STom.Erickson@Sun.COM }
10582676Seschrock 
105911022STom.Erickson@Sun.COM static void
106011022STom.Erickson@Sun.COM dsl_prop_set_hasrecvd_impl(objset_t *os, zprop_source_t source)
106111022STom.Erickson@Sun.COM {
106211022STom.Erickson@Sun.COM 	dsl_dataset_t *ds = os->os_dsl_dataset;
106311022STom.Erickson@Sun.COM 	uint64_t dummy = 0;
106411022STom.Erickson@Sun.COM 	dsl_prop_setarg_t psa;
106511022STom.Erickson@Sun.COM 
106611022STom.Erickson@Sun.COM 	if (spa_version(os->os_spa) < SPA_VERSION_RECVD_PROPS)
106711022STom.Erickson@Sun.COM 		return;
106811022STom.Erickson@Sun.COM 
106911022STom.Erickson@Sun.COM 	dsl_prop_setarg_init_uint64(&psa, ZPROP_HAS_RECVD, source, &dummy);
107011022STom.Erickson@Sun.COM 
107111022STom.Erickson@Sun.COM 	(void) dsl_sync_task_do(ds->ds_dir->dd_pool, NULL,
107211022STom.Erickson@Sun.COM 	    dsl_prop_set_sync, ds, &psa, 2);
107311022STom.Erickson@Sun.COM }
10741356Seschrock 
107511022STom.Erickson@Sun.COM /*
107611022STom.Erickson@Sun.COM  * Call after successfully receiving properties to ensure that only the first
107711022STom.Erickson@Sun.COM  * receive on or after SPA_VERSION_RECVD_PROPS blows away local properties.
107811022STom.Erickson@Sun.COM  */
107911022STom.Erickson@Sun.COM void
108011022STom.Erickson@Sun.COM dsl_prop_set_hasrecvd(objset_t *os)
108111022STom.Erickson@Sun.COM {
108211022STom.Erickson@Sun.COM 	if (dsl_prop_get_hasrecvd(os)) {
108311022STom.Erickson@Sun.COM 		ASSERT(spa_version(os->os_spa) >= SPA_VERSION_RECVD_PROPS);
108411022STom.Erickson@Sun.COM 		return;
108511022STom.Erickson@Sun.COM 	}
108611022STom.Erickson@Sun.COM 	dsl_prop_set_hasrecvd_impl(os, ZPROP_SRC_LOCAL);
108711022STom.Erickson@Sun.COM }
10881356Seschrock 
108911022STom.Erickson@Sun.COM void
109011022STom.Erickson@Sun.COM dsl_prop_unset_hasrecvd(objset_t *os)
109111022STom.Erickson@Sun.COM {
109211022STom.Erickson@Sun.COM 	dsl_prop_set_hasrecvd_impl(os, ZPROP_SRC_NONE);
109311022STom.Erickson@Sun.COM }
109411022STom.Erickson@Sun.COM 
109511022STom.Erickson@Sun.COM int
109611022STom.Erickson@Sun.COM dsl_prop_get_all(objset_t *os, nvlist_t **nvp)
109711022STom.Erickson@Sun.COM {
109811022STom.Erickson@Sun.COM 	return (dsl_prop_get_all_ds(os->os_dsl_dataset, nvp, 0));
109911022STom.Erickson@Sun.COM }
11001356Seschrock 
110111022STom.Erickson@Sun.COM int
110211022STom.Erickson@Sun.COM dsl_prop_get_received(objset_t *os, nvlist_t **nvp)
110311022STom.Erickson@Sun.COM {
110411022STom.Erickson@Sun.COM 	/*
110511022STom.Erickson@Sun.COM 	 * Received properties are not distinguishable from local properties
110611022STom.Erickson@Sun.COM 	 * until the dataset has received properties on or after
110711022STom.Erickson@Sun.COM 	 * SPA_VERSION_RECVD_PROPS.
110811022STom.Erickson@Sun.COM 	 */
110911022STom.Erickson@Sun.COM 	dsl_prop_getflags_t flags = (dsl_prop_get_hasrecvd(os) ?
111011022STom.Erickson@Sun.COM 	    DSL_PROP_GET_RECEIVED : DSL_PROP_GET_LOCAL);
111111022STom.Erickson@Sun.COM 	return (dsl_prop_get_all_ds(os->os_dsl_dataset, nvp, flags));
11121356Seschrock }
11132885Sahrens 
11142885Sahrens void
11152885Sahrens dsl_prop_nvlist_add_uint64(nvlist_t *nv, zfs_prop_t prop, uint64_t value)
11162885Sahrens {
11172885Sahrens 	nvlist_t *propval;
111811022STom.Erickson@Sun.COM 	const char *propname = zfs_prop_to_name(prop);
111911022STom.Erickson@Sun.COM 	uint64_t default_value;
112011022STom.Erickson@Sun.COM 
112111022STom.Erickson@Sun.COM 	if (nvlist_lookup_nvlist(nv, propname, &propval) == 0) {
112211022STom.Erickson@Sun.COM 		VERIFY(nvlist_add_uint64(propval, ZPROP_VALUE, value) == 0);
112311022STom.Erickson@Sun.COM 		return;
112411022STom.Erickson@Sun.COM 	}
11252885Sahrens 
11262885Sahrens 	VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
11275094Slling 	VERIFY(nvlist_add_uint64(propval, ZPROP_VALUE, value) == 0);
112811022STom.Erickson@Sun.COM 	/* Indicate the default source if we can. */
112911022STom.Erickson@Sun.COM 	if (dodefault(propname, 8, 1, &default_value) == 0 &&
113011022STom.Erickson@Sun.COM 	    value == default_value) {
113111022STom.Erickson@Sun.COM 		VERIFY(nvlist_add_string(propval, ZPROP_SOURCE, "") == 0);
113211022STom.Erickson@Sun.COM 	}
113311022STom.Erickson@Sun.COM 	VERIFY(nvlist_add_nvlist(nv, propname, propval) == 0);
11342885Sahrens 	nvlist_free(propval);
11352885Sahrens }
11362885Sahrens 
11372885Sahrens void
11382885Sahrens dsl_prop_nvlist_add_string(nvlist_t *nv, zfs_prop_t prop, const char *value)
11392885Sahrens {
11402885Sahrens 	nvlist_t *propval;
114111022STom.Erickson@Sun.COM 	const char *propname = zfs_prop_to_name(prop);
114211022STom.Erickson@Sun.COM 
114311022STom.Erickson@Sun.COM 	if (nvlist_lookup_nvlist(nv, propname, &propval) == 0) {
114411022STom.Erickson@Sun.COM 		VERIFY(nvlist_add_string(propval, ZPROP_VALUE, value) == 0);
114511022STom.Erickson@Sun.COM 		return;
114611022STom.Erickson@Sun.COM 	}
11472885Sahrens 
11482885Sahrens 	VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
11495094Slling 	VERIFY(nvlist_add_string(propval, ZPROP_VALUE, value) == 0);
115011022STom.Erickson@Sun.COM 	VERIFY(nvlist_add_nvlist(nv, propname, propval) == 0);
11512885Sahrens 	nvlist_free(propval);
11522885Sahrens }
1153