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 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 if (intsz * numints >= ZAP_MAXVALUELEN) 477 return (E2BIG); 478 479 err = dsl_dataset_hold(dsname, FTAG, &ds); 480 if (err) 481 return (err); 482 483 if (dsl_dataset_is_snapshot(ds) && 484 spa_version(ds->ds_dir->dd_pool->dp_spa) < SPA_VERSION_SNAP_PROPS) { 485 dsl_dataset_rele(ds, FTAG); 486 return (ENOTSUP); 487 } 488 489 psa.name = propname; 490 psa.intsz = intsz; 491 psa.numints = numints; 492 psa.buf = buf; 493 err = dsl_sync_task_do(ds->ds_dir->dd_pool, 494 NULL, dsl_prop_set_sync, ds, &psa, 2); 495 496 dsl_dataset_rele(ds, FTAG); 497 return (err); 498 } 499 500 int 501 dsl_props_set(const char *dsname, nvlist_t *nvl) 502 { 503 dsl_dataset_t *ds; 504 nvpair_t *elem = NULL; 505 int err; 506 507 /* 508 * Do these checks before the syncfunc, since it can't fail. 509 */ 510 while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { 511 if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) 512 return (ENAMETOOLONG); 513 if (nvpair_type(elem) == DATA_TYPE_STRING) { 514 char *valstr; 515 VERIFY(nvpair_value_string(elem, &valstr) == 0); 516 if (strlen(valstr) >= ZAP_MAXVALUELEN) 517 return (E2BIG); 518 } 519 } 520 521 if (err = dsl_dataset_hold(dsname, FTAG, &ds)) 522 return (err); 523 524 if (dsl_dataset_is_snapshot(ds) && 525 spa_version(ds->ds_dir->dd_pool->dp_spa) < SPA_VERSION_SNAP_PROPS) { 526 dsl_dataset_rele(ds, FTAG); 527 return (ENOTSUP); 528 } 529 530 err = dsl_sync_task_do(ds->ds_dir->dd_pool, 531 NULL, dsl_props_set_sync, ds, nvl, 2); 532 533 dsl_dataset_rele(ds, FTAG); 534 return (err); 535 } 536 537 /* 538 * Iterate over all properties for this dataset and return them in an nvlist. 539 */ 540 int 541 dsl_prop_get_all(objset_t *os, nvlist_t **nvp, boolean_t local) 542 { 543 dsl_dataset_t *ds = os->os->os_dsl_dataset; 544 dsl_dir_t *dd = ds->ds_dir; 545 boolean_t snapshot = dsl_dataset_is_snapshot(ds); 546 int err = 0; 547 dsl_pool_t *dp = dd->dd_pool; 548 objset_t *mos = dp->dp_meta_objset; 549 uint64_t propobj = ds->ds_phys->ds_props_obj; 550 551 VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 552 553 if (local && snapshot && !propobj) 554 return (0); 555 556 rw_enter(&dp->dp_config_rwlock, RW_READER); 557 while (dd != NULL) { 558 char setpoint[MAXNAMELEN]; 559 zap_cursor_t zc; 560 zap_attribute_t za; 561 dsl_dir_t *dd_next; 562 563 if (propobj) { 564 dsl_dataset_name(ds, setpoint); 565 dd_next = dd; 566 } else { 567 dsl_dir_name(dd, setpoint); 568 propobj = dd->dd_phys->dd_props_zapobj; 569 dd_next = dd->dd_parent; 570 } 571 572 for (zap_cursor_init(&zc, mos, propobj); 573 (err = zap_cursor_retrieve(&zc, &za)) == 0; 574 zap_cursor_advance(&zc)) { 575 nvlist_t *propval; 576 zfs_prop_t prop = zfs_name_to_prop(za.za_name); 577 578 /* Skip non-inheritable properties. */ 579 if (prop != ZPROP_INVAL && 580 !zfs_prop_inheritable(prop) && 581 (dd != ds->ds_dir || (snapshot && dd != dd_next))) 582 continue; 583 584 /* Skip properties not valid for this type. */ 585 if (snapshot && prop != ZPROP_INVAL && 586 !zfs_prop_valid_for_type(prop, ZFS_TYPE_SNAPSHOT)) 587 continue; 588 589 /* Skip properties already defined */ 590 if (nvlist_lookup_nvlist(*nvp, za.za_name, 591 &propval) == 0) 592 continue; 593 594 VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, 595 KM_SLEEP) == 0); 596 if (za.za_integer_length == 1) { 597 /* 598 * String property 599 */ 600 char *tmp = kmem_alloc(za.za_num_integers, 601 KM_SLEEP); 602 err = zap_lookup(mos, propobj, 603 za.za_name, 1, za.za_num_integers, tmp); 604 if (err != 0) { 605 kmem_free(tmp, za.za_num_integers); 606 break; 607 } 608 VERIFY(nvlist_add_string(propval, ZPROP_VALUE, 609 tmp) == 0); 610 kmem_free(tmp, za.za_num_integers); 611 } else { 612 /* 613 * Integer property 614 */ 615 ASSERT(za.za_integer_length == 8); 616 (void) nvlist_add_uint64(propval, ZPROP_VALUE, 617 za.za_first_integer); 618 } 619 620 VERIFY(nvlist_add_string(propval, ZPROP_SOURCE, 621 setpoint) == 0); 622 VERIFY(nvlist_add_nvlist(*nvp, za.za_name, 623 propval) == 0); 624 nvlist_free(propval); 625 } 626 zap_cursor_fini(&zc); 627 628 if (err != ENOENT) 629 break; 630 err = 0; 631 /* 632 * If we are just after the props that have been set 633 * locally, then we are done after the first iteration. 634 */ 635 if (local) 636 break; 637 dd = dd_next; 638 propobj = 0; 639 } 640 rw_exit(&dp->dp_config_rwlock); 641 642 return (err); 643 } 644 645 void 646 dsl_prop_nvlist_add_uint64(nvlist_t *nv, zfs_prop_t prop, uint64_t value) 647 { 648 nvlist_t *propval; 649 650 VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0); 651 VERIFY(nvlist_add_uint64(propval, ZPROP_VALUE, value) == 0); 652 VERIFY(nvlist_add_nvlist(nv, zfs_prop_to_name(prop), propval) == 0); 653 nvlist_free(propval); 654 } 655 656 void 657 dsl_prop_nvlist_add_string(nvlist_t *nv, zfs_prop_t prop, const char *value) 658 { 659 nvlist_t *propval; 660 661 VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0); 662 VERIFY(nvlist_add_string(propval, ZPROP_VALUE, value) == 0); 663 VERIFY(nvlist_add_nvlist(nv, zfs_prop_to_name(prop), propval) == 0); 664 nvlist_free(propval); 665 } 666