Home | History | Annotate | Download | only in zfs
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <sys/dmu.h>
     27 #include <sys/dmu_objset.h>
     28 #include <sys/dmu_tx.h>
     29 #include <sys/dsl_dataset.h>
     30 #include <sys/dsl_dir.h>
     31 #include <sys/dsl_prop.h>
     32 #include <sys/dsl_synctask.h>
     33 #include <sys/spa.h>
     34 #include <sys/zap.h>
     35 #include <sys/fs/zfs.h>
     36 
     37 #include "zfs_prop.h"
     38 
     39 static int
     40 dodefault(const char *propname, int intsz, int numint, void *buf)
     41 {
     42 	zfs_prop_t prop;
     43 
     44 	/*
     45 	 * The setonce properties are read-only, BUT they still
     46 	 * have a default value that can be used as the initial
     47 	 * value.
     48 	 */
     49 	if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL ||
     50 	    (zfs_prop_readonly(prop) && !zfs_prop_setonce(prop)))
     51 		return (ENOENT);
     52 
     53 	if (zfs_prop_get_type(prop) == PROP_TYPE_STRING) {
     54 		if (intsz != 1)
     55 			return (EOVERFLOW);
     56 		(void) strncpy(buf, zfs_prop_default_string(prop),
     57 		    numint);
     58 	} else {
     59 		if (intsz != 8 || numint < 1)
     60 			return (EOVERFLOW);
     61 
     62 		*(uint64_t *)buf = zfs_prop_default_numeric(prop);
     63 	}
     64 
     65 	return (0);
     66 }
     67 
     68 int
     69 dsl_prop_get_dd(dsl_dir_t *dd, const char *propname,
     70     int intsz, int numint, void *buf, char *setpoint)
     71 {
     72 	int err = ENOENT;
     73 	objset_t *mos = dd->dd_pool->dp_meta_objset;
     74 	zfs_prop_t prop;
     75 
     76 	ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock));
     77 
     78 	if (setpoint)
     79 		setpoint[0] = '\0';
     80 
     81 	prop = zfs_name_to_prop(propname);
     82 
     83 	/*
     84 	 * Note: dd may be NULL, therefore we shouldn't dereference it
     85 	 * ouside this loop.
     86 	 */
     87 	for (; dd != NULL; dd = dd->dd_parent) {
     88 		ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock));
     89 		err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj,
     90 		    propname, intsz, numint, buf);
     91 		if (err != ENOENT) {
     92 			if (setpoint)
     93 				dsl_dir_name(dd, setpoint);
     94 			break;
     95 		}
     96 
     97 		/*
     98 		 * Break out of this loop for non-inheritable properties.
     99 		 */
    100 		if (prop != ZPROP_INVAL && !zfs_prop_inheritable(prop))
    101 			break;
    102 	}
    103 	if (err == ENOENT)
    104 		err = dodefault(propname, intsz, numint, buf);
    105 
    106 	return (err);
    107 }
    108 
    109 int
    110 dsl_prop_get_ds(dsl_dataset_t *ds, const char *propname,
    111     int intsz, int numint, void *buf, char *setpoint)
    112 {
    113 	ASSERT(RW_LOCK_HELD(&ds->ds_dir->dd_pool->dp_config_rwlock));
    114 
    115 	if (ds->ds_phys->ds_props_obj) {
    116 		int err = zap_lookup(ds->ds_dir->dd_pool->dp_meta_objset,
    117 		    ds->ds_phys->ds_props_obj, propname, intsz, numint, buf);
    118 		if (err != ENOENT) {
    119 			if (setpoint)
    120 				dsl_dataset_name(ds, setpoint);
    121 			return (err);
    122 		}
    123 	}
    124 
    125 	return (dsl_prop_get_dd(ds->ds_dir, propname,
    126 	    intsz, numint, buf, setpoint));
    127 }
    128 
    129 /*
    130  * Register interest in the named property.  We'll call the callback
    131  * once to notify it of the current property value, and again each time
    132  * the property changes, until this callback is unregistered.
    133  *
    134  * Return 0 on success, errno if the prop is not an integer value.
    135  */
    136 int
    137 dsl_prop_register(dsl_dataset_t *ds, const char *propname,
    138     dsl_prop_changed_cb_t *callback, void *cbarg)
    139 {
    140 	dsl_dir_t *dd = ds->ds_dir;
    141 	dsl_pool_t *dp = dd->dd_pool;
    142 	uint64_t value;
    143 	dsl_prop_cb_record_t *cbr;
    144 	int err;
    145 	int need_rwlock;
    146 
    147 	need_rwlock = !RW_WRITE_HELD(&dp->dp_config_rwlock);
    148 	if (need_rwlock)
    149 		rw_enter(&dp->dp_config_rwlock, RW_READER);
    150 
    151 	err = dsl_prop_get_ds(ds, propname, 8, 1, &value, NULL);
    152 	if (err != 0) {
    153 		if (need_rwlock)
    154 			rw_exit(&dp->dp_config_rwlock);
    155 		return (err);
    156 	}
    157 
    158 	cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP);
    159 	cbr->cbr_ds = ds;
    160 	cbr->cbr_propname = kmem_alloc(strlen(propname)+1, KM_SLEEP);
    161 	(void) strcpy((char *)cbr->cbr_propname, propname);
    162 	cbr->cbr_func = callback;
    163 	cbr->cbr_arg = cbarg;
    164 	mutex_enter(&dd->dd_lock);
    165 	list_insert_head(&dd->dd_prop_cbs, cbr);
    166 	mutex_exit(&dd->dd_lock);
    167 
    168 	cbr->cbr_func(cbr->cbr_arg, value);
    169 
    170 	VERIFY(0 == dsl_dir_open_obj(dp, dd->dd_object,
    171 	    NULL, cbr, &dd));
    172 	if (need_rwlock)
    173 		rw_exit(&dp->dp_config_rwlock);
    174 	/* Leave dir open until this callback is unregistered */
    175 	return (0);
    176 }
    177 
    178 int
    179 dsl_prop_get(const char *dsname, const char *propname,
    180     int intsz, int numints, void *buf, char *setpoint)
    181 {
    182 	dsl_dataset_t *ds;
    183 	int err;
    184 
    185 	err = dsl_dataset_hold(dsname, FTAG, &ds);
    186 	if (err)
    187 		return (err);
    188 
    189 	rw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER);
    190 	err = dsl_prop_get_ds(ds, propname, intsz, numints, buf, setpoint);
    191 	rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock);
    192 
    193 	dsl_dataset_rele(ds, FTAG);
    194 	return (err);
    195 }
    196 
    197 /*
    198  * Get the current property value.  It may have changed by the time this
    199  * function returns, so it is NOT safe to follow up with
    200  * dsl_prop_register() and assume that the value has not changed in
    201  * between.
    202  *
    203  * Return 0 on success, ENOENT if ddname is invalid.
    204  */
    205 int
    206 dsl_prop_get_integer(const char *ddname, const char *propname,
    207     uint64_t *valuep, char *setpoint)
    208 {
    209 	return (dsl_prop_get(ddname, propname, 8, 1, valuep, setpoint));
    210 }
    211 
    212 /*
    213  * Unregister this callback.  Return 0 on success, ENOENT if ddname is
    214  * invalid, ENOMSG if no matching callback registered.
    215  */
    216 int
    217 dsl_prop_unregister(dsl_dataset_t *ds, const char *propname,
    218     dsl_prop_changed_cb_t *callback, void *cbarg)
    219 {
    220 	dsl_dir_t *dd = ds->ds_dir;
    221 	dsl_prop_cb_record_t *cbr;
    222 
    223 	mutex_enter(&dd->dd_lock);
    224 	for (cbr = list_head(&dd->dd_prop_cbs);
    225 	    cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
    226 		if (cbr->cbr_ds == ds &&
    227 		    cbr->cbr_func == callback &&
    228 		    cbr->cbr_arg == cbarg &&
    229 		    strcmp(cbr->cbr_propname, propname) == 0)
    230 			break;
    231 	}
    232 
    233 	if (cbr == NULL) {
    234 		mutex_exit(&dd->dd_lock);
    235 		return (ENOMSG);
    236 	}
    237 
    238 	list_remove(&dd->dd_prop_cbs, cbr);
    239 	mutex_exit(&dd->dd_lock);
    240 	kmem_free((void*)cbr->cbr_propname, strlen(cbr->cbr_propname)+1);
    241 	kmem_free(cbr, sizeof (dsl_prop_cb_record_t));
    242 
    243 	/* Clean up from dsl_prop_register */
    244 	dsl_dir_close(dd, cbr);
    245 	return (0);
    246 }
    247 
    248 /*
    249  * Return the number of callbacks that are registered for this dataset.
    250  */
    251 int
    252 dsl_prop_numcb(dsl_dataset_t *ds)
    253 {
    254 	dsl_dir_t *dd = ds->ds_dir;
    255 	dsl_prop_cb_record_t *cbr;
    256 	int num = 0;
    257 
    258 	mutex_enter(&dd->dd_lock);
    259 	for (cbr = list_head(&dd->dd_prop_cbs);
    260 	    cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
    261 		if (cbr->cbr_ds == ds)
    262 			num++;
    263 	}
    264 	mutex_exit(&dd->dd_lock);
    265 
    266 	return (num);
    267 }
    268 
    269 static void
    270 dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj,
    271     const char *propname, uint64_t value, int first)
    272 {
    273 	dsl_dir_t *dd;
    274 	dsl_prop_cb_record_t *cbr;
    275 	objset_t *mos = dp->dp_meta_objset;
    276 	zap_cursor_t zc;
    277 	zap_attribute_t *za;
    278 	int err;
    279 	uint64_t dummyval;
    280 
    281 	ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock));
    282 	err = dsl_dir_open_obj(dp, ddobj, NULL, FTAG, &dd);
    283 	if (err)
    284 		return;
    285 
    286 	if (!first) {
    287 		/*
    288 		 * If the prop is set here, then this change is not
    289 		 * being inherited here or below; stop the recursion.
    290 		 */
    291 		err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, propname,
    292 		    8, 1, &dummyval);
    293 		if (err == 0) {
    294 			dsl_dir_close(dd, FTAG);
    295 			return;
    296 		}
    297 		ASSERT3U(err, ==, ENOENT);
    298 	}
    299 
    300 	mutex_enter(&dd->dd_lock);
    301 	for (cbr = list_head(&dd->dd_prop_cbs); cbr;
    302 	    cbr = list_next(&dd->dd_prop_cbs, cbr)) {
    303 		uint64_t propobj = cbr->cbr_ds->ds_phys->ds_props_obj;
    304 
    305 		if (strcmp(cbr->cbr_propname, propname) != 0)
    306 			continue;
    307 
    308 		/*
    309 		 * If the property is set on this ds, then it is not
    310 		 * inherited here; don't call the callback.
    311 		 */
    312 		if (propobj && 0 == zap_lookup(mos, propobj, propname,
    313 		    8, 1, &dummyval))
    314 			continue;
    315 
    316 		cbr->cbr_func(cbr->cbr_arg, value);
    317 	}
    318 	mutex_exit(&dd->dd_lock);
    319 
    320 	za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP);
    321 	for (zap_cursor_init(&zc, mos,
    322 	    dd->dd_phys->dd_child_dir_zapobj);
    323 	    zap_cursor_retrieve(&zc, za) == 0;
    324 	    zap_cursor_advance(&zc)) {
    325 		dsl_prop_changed_notify(dp, za->za_first_integer,
    326 		    propname, value, FALSE);
    327 	}
    328 	kmem_free(za, sizeof (zap_attribute_t));
    329 	zap_cursor_fini(&zc);
    330 	dsl_dir_close(dd, FTAG);
    331 }
    332 
    333 struct prop_set_arg {
    334 	const char *name;
    335 	int intsz;
    336 	int numints;
    337 	const void *buf;
    338 };
    339 
    340 
    341 static void
    342 dsl_prop_set_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
    343 {
    344 	dsl_dataset_t *ds = arg1;
    345 	struct prop_set_arg *psa = arg2;
    346 	objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
    347 	uint64_t zapobj, intval;
    348 	int isint;
    349 	char valbuf[32];
    350 	char *valstr;
    351 
    352 	isint = (dodefault(psa->name, 8, 1, &intval) == 0);
    353 
    354 	if (dsl_dataset_is_snapshot(ds)) {
    355 		ASSERT(spa_version(ds->ds_dir->dd_pool->dp_spa) >=
    356 		    SPA_VERSION_SNAP_PROPS);
    357 		if (ds->ds_phys->ds_props_obj == 0) {
    358 			dmu_buf_will_dirty(ds->ds_dbuf, tx);
    359 			ds->ds_phys->ds_props_obj =
    360 			    zap_create(mos,
    361 			    DMU_OT_DSL_PROPS, DMU_OT_NONE, 0, tx);
    362 		}
    363 		zapobj = ds->ds_phys->ds_props_obj;
    364 	} else {
    365 		zapobj = ds->ds_dir->dd_phys->dd_props_zapobj;
    366 	}
    367 
    368 	if (psa->numints == 0) {
    369 		int err = zap_remove(mos, zapobj, psa->name, tx);
    370 		ASSERT(err == 0 || err == ENOENT);
    371 		if (isint) {
    372 			VERIFY(0 == dsl_prop_get_ds(ds,
    373 			    psa->name, 8, 1, &intval, NULL));
    374 		}
    375 	} else {
    376 		VERIFY(0 == zap_update(mos, zapobj, psa->name,
    377 		    psa->intsz, psa->numints, psa->buf, tx));
    378 		if (isint)
    379 			intval = *(uint64_t *)psa->buf;
    380 	}
    381 
    382 	if (isint) {
    383 		if (dsl_dataset_is_snapshot(ds)) {
    384 			dsl_prop_cb_record_t *cbr;
    385 			/*
    386 			 * It's a snapshot; nothing can inherit this
    387 			 * property, so just look for callbacks on this
    388 			 * ds here.
    389 			 */
    390 			mutex_enter(&ds->ds_dir->dd_lock);
    391 			for (cbr = list_head(&ds->ds_dir->dd_prop_cbs); cbr;
    392 			    cbr = list_next(&ds->ds_dir->dd_prop_cbs, cbr)) {
    393 				if (cbr->cbr_ds == ds &&
    394 				    strcmp(cbr->cbr_propname, psa->name) == 0)
    395 					cbr->cbr_func(cbr->cbr_arg, intval);
    396 			}
    397 			mutex_exit(&ds->ds_dir->dd_lock);
    398 		} else {
    399 			dsl_prop_changed_notify(ds->ds_dir->dd_pool,
    400 			    ds->ds_dir->dd_object, psa->name, intval, TRUE);
    401 		}
    402 	}
    403 	if (isint) {
    404 		(void) snprintf(valbuf, sizeof (valbuf),
    405 		    "%lld", (longlong_t)intval);
    406 		valstr = valbuf;
    407 	} else {
    408 		valstr = (char *)psa->buf;
    409 	}
    410 	spa_history_internal_log((psa->numints == 0) ? LOG_DS_INHERIT :
    411 	    LOG_DS_PROPSET, ds->ds_dir->dd_pool->dp_spa, tx, cr,
    412 	    "%s=%s dataset = %llu", psa->name, valstr, ds->ds_object);
    413 }
    414 
    415 void
    416 dsl_props_set_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
    417 {
    418 	dsl_dataset_t *ds = arg1;
    419 	nvlist_t *nvl = arg2;
    420 	nvpair_t *elem = NULL;
    421 
    422 	while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
    423 		struct prop_set_arg psa;
    424 
    425 		psa.name = nvpair_name(elem);
    426 
    427 		if (nvpair_type(elem) == DATA_TYPE_STRING) {
    428 			VERIFY(nvpair_value_string(elem,
    429 			    (char **)&psa.buf) == 0);
    430 			psa.intsz = 1;
    431 			psa.numints = strlen(psa.buf) + 1;
    432 		} else {
    433 			uint64_t intval;
    434 			VERIFY(nvpair_value_uint64(elem, &intval) == 0);
    435 			psa.intsz = sizeof (intval);
    436 			psa.numints = 1;
    437 			psa.buf = &intval;
    438 		}
    439 		dsl_prop_set_sync(ds, &psa, cr, tx);
    440 	}
    441 }
    442 
    443 void
    444 dsl_dir_prop_set_uint64_sync(dsl_dir_t *dd, const char *name, uint64_t val,
    445     cred_t *cr, dmu_tx_t *tx)
    446 {
    447 	objset_t *mos = dd->dd_pool->dp_meta_objset;
    448 	uint64_t zapobj = dd->dd_phys->dd_props_zapobj;
    449 
    450 	ASSERT(dmu_tx_is_syncing(tx));
    451 
    452 	VERIFY(0 == zap_update(mos, zapobj, name, sizeof (val), 1, &val, tx));
    453 
    454 	dsl_prop_changed_notify(dd->dd_pool, dd->dd_object, name, val, TRUE);
    455 
    456 	spa_history_internal_log(LOG_DS_PROPSET, dd->dd_pool->dp_spa, tx, cr,
    457 	    "%s=%llu dataset = %llu", name, (u_longlong_t)val,
    458 	    dd->dd_phys->dd_head_dataset_obj);
    459 }
    460 
    461 int
    462 dsl_prop_set(const char *dsname, const char *propname,
    463     int intsz, int numints, const void *buf)
    464 {
    465 	dsl_dataset_t *ds;
    466 	uint64_t version;
    467 	int err;
    468 	struct prop_set_arg psa;
    469 
    470 	/*
    471 	 * We must do these checks before we get to the syncfunc, since
    472 	 * it can't fail.
    473 	 */
    474 	if (strlen(propname) >= ZAP_MAXNAMELEN)
    475 		return (ENAMETOOLONG);
    476 
    477 	err = dsl_dataset_hold(dsname, FTAG, &ds);
    478 	if (err)
    479 		return (err);
    480 
    481 	version = spa_version(ds->ds_dir->dd_pool->dp_spa);
    482 	if (intsz * numints >= (version < SPA_VERSION_STMF_PROP ?
    483 	    ZAP_OLDMAXVALUELEN : ZAP_MAXVALUELEN)) {
    484 		dsl_dataset_rele(ds, FTAG);
    485 		return (E2BIG);
    486 	}
    487 	if (dsl_dataset_is_snapshot(ds) &&
    488 	    version < SPA_VERSION_SNAP_PROPS) {
    489 		dsl_dataset_rele(ds, FTAG);
    490 		return (ENOTSUP);
    491 	}
    492 
    493 	psa.name = propname;
    494 	psa.intsz = intsz;
    495 	psa.numints = numints;
    496 	psa.buf = buf;
    497 	err = dsl_sync_task_do(ds->ds_dir->dd_pool,
    498 	    NULL, dsl_prop_set_sync, ds, &psa, 2);
    499 
    500 	dsl_dataset_rele(ds, FTAG);
    501 	return (err);
    502 }
    503 
    504 int
    505 dsl_props_set(const char *dsname, nvlist_t *nvl)
    506 {
    507 	dsl_dataset_t *ds;
    508 	uint64_t version;
    509 	nvpair_t *elem = NULL;
    510 	int err;
    511 
    512 	if (err = dsl_dataset_hold(dsname, FTAG, &ds))
    513 		return (err);
    514 	/*
    515 	 * Do these checks before the syncfunc, since it can't fail.
    516 	 */
    517 	version = spa_version(ds->ds_dir->dd_pool->dp_spa);
    518 	while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
    519 		if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) {
    520 			dsl_dataset_rele(ds, FTAG);
    521 			return (ENAMETOOLONG);
    522 		}
    523 		if (nvpair_type(elem) == DATA_TYPE_STRING) {
    524 			char *valstr;
    525 			VERIFY(nvpair_value_string(elem, &valstr) == 0);
    526 			if (strlen(valstr) >= (version <
    527 			    SPA_VERSION_STMF_PROP ?
    528 			    ZAP_OLDMAXVALUELEN : ZAP_MAXVALUELEN)) {
    529 				dsl_dataset_rele(ds, FTAG);
    530 				return (E2BIG);
    531 			}
    532 		}
    533 	}
    534 
    535 	if (dsl_dataset_is_snapshot(ds) &&
    536 	    version < SPA_VERSION_SNAP_PROPS) {
    537 		dsl_dataset_rele(ds, FTAG);
    538 		return (ENOTSUP);
    539 	}
    540 
    541 	err = dsl_sync_task_do(ds->ds_dir->dd_pool,
    542 	    NULL, dsl_props_set_sync, ds, nvl, 2);
    543 
    544 	dsl_dataset_rele(ds, FTAG);
    545 	return (err);
    546 }
    547 
    548 /*
    549  * Iterate over all properties for this dataset and return them in an nvlist.
    550  */
    551 int
    552 dsl_prop_get_all(objset_t *os, nvlist_t **nvp, boolean_t local)
    553 {
    554 	dsl_dataset_t *ds = os->os_dsl_dataset;
    555 	dsl_dir_t *dd = ds->ds_dir;
    556 	boolean_t snapshot = dsl_dataset_is_snapshot(ds);
    557 	int err = 0;
    558 	dsl_pool_t *dp = dd->dd_pool;
    559 	objset_t *mos = dp->dp_meta_objset;
    560 	uint64_t propobj = ds->ds_phys->ds_props_obj;
    561 
    562 	VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
    563 
    564 	if (local && snapshot && !propobj)
    565 		return (0);
    566 
    567 	rw_enter(&dp->dp_config_rwlock, RW_READER);
    568 	while (dd != NULL) {
    569 		char setpoint[MAXNAMELEN];
    570 		zap_cursor_t zc;
    571 		zap_attribute_t za;
    572 		dsl_dir_t *dd_next;
    573 
    574 		if (propobj) {
    575 			dsl_dataset_name(ds, setpoint);
    576 			dd_next = dd;
    577 		} else {
    578 			dsl_dir_name(dd, setpoint);
    579 			propobj = dd->dd_phys->dd_props_zapobj;
    580 			dd_next = dd->dd_parent;
    581 		}
    582 
    583 		for (zap_cursor_init(&zc, mos, propobj);
    584 		    (err = zap_cursor_retrieve(&zc, &za)) == 0;
    585 		    zap_cursor_advance(&zc)) {
    586 			nvlist_t *propval;
    587 			zfs_prop_t prop = zfs_name_to_prop(za.za_name);
    588 
    589 			/* Skip non-inheritable properties. */
    590 			if (prop != ZPROP_INVAL &&
    591 			    !zfs_prop_inheritable(prop) &&
    592 			    (dd != ds->ds_dir || (snapshot && dd != dd_next)))
    593 				continue;
    594 
    595 			/* Skip properties not valid for this type. */
    596 			if (snapshot && prop != ZPROP_INVAL &&
    597 			    !zfs_prop_valid_for_type(prop, ZFS_TYPE_SNAPSHOT))
    598 				continue;
    599 
    600 			/* Skip properties already defined */
    601 			if (nvlist_lookup_nvlist(*nvp, za.za_name,
    602 			    &propval) == 0)
    603 				continue;
    604 
    605 			VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME,
    606 			    KM_SLEEP) == 0);
    607 			if (za.za_integer_length == 1) {
    608 				/*
    609 				 * String property
    610 				 */
    611 				char *tmp = kmem_alloc(za.za_num_integers,
    612 				    KM_SLEEP);
    613 				err = zap_lookup(mos, propobj,
    614 				    za.za_name, 1, za.za_num_integers, tmp);
    615 				if (err != 0) {
    616 					kmem_free(tmp, za.za_num_integers);
    617 					break;
    618 				}
    619 				VERIFY(nvlist_add_string(propval, ZPROP_VALUE,
    620 				    tmp) == 0);
    621 				kmem_free(tmp, za.za_num_integers);
    622 			} else {
    623 				/*
    624 				 * Integer property
    625 				 */
    626 				ASSERT(za.za_integer_length == 8);
    627 				(void) nvlist_add_uint64(propval, ZPROP_VALUE,
    628 				    za.za_first_integer);
    629 			}
    630 
    631 			VERIFY(nvlist_add_string(propval, ZPROP_SOURCE,
    632 			    setpoint) == 0);
    633 			VERIFY(nvlist_add_nvlist(*nvp, za.za_name,
    634 			    propval) == 0);
    635 			nvlist_free(propval);
    636 		}
    637 		zap_cursor_fini(&zc);
    638 
    639 		if (err != ENOENT)
    640 			break;
    641 		err = 0;
    642 		/*
    643 		 * If we are just after the props that have been set
    644 		 * locally, then we are done after the first iteration.
    645 		 */
    646 		if (local)
    647 			break;
    648 		dd = dd_next;
    649 		propobj = 0;
    650 	}
    651 	rw_exit(&dp->dp_config_rwlock);
    652 
    653 	return (err);
    654 }
    655 
    656 void
    657 dsl_prop_nvlist_add_uint64(nvlist_t *nv, zfs_prop_t prop, uint64_t value)
    658 {
    659 	nvlist_t *propval;
    660 
    661 	VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
    662 	VERIFY(nvlist_add_uint64(propval, ZPROP_VALUE, value) == 0);
    663 	VERIFY(nvlist_add_nvlist(nv, zfs_prop_to_name(prop), propval) == 0);
    664 	nvlist_free(propval);
    665 }
    666 
    667 void
    668 dsl_prop_nvlist_add_string(nvlist_t *nv, zfs_prop_t prop, const char *value)
    669 {
    670 	nvlist_t *propval;
    671 
    672 	VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
    673 	VERIFY(nvlist_add_string(propval, ZPROP_VALUE, value) == 0);
    674 	VERIFY(nvlist_add_nvlist(nv, zfs_prop_to_name(prop), propval) == 0);
    675 	nvlist_free(propval);
    676 }
    677