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