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