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 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/dmu.h> 29 #include <sys/dmu_objset.h> 30 #include <sys/dmu_tx.h> 31 #include <sys/dsl_dataset.h> 32 #include <sys/dsl_dir.h> 33 #include <sys/dsl_prop.h> 34 #include <sys/dsl_synctask.h> 35 #include <sys/spa.h> 36 #include <sys/zio_checksum.h> /* for the default checksum value */ 37 #include <sys/zap.h> 38 #include <sys/fs/zfs.h> 39 40 #include "zfs_prop.h" 41 42 static int 43 dodefault(const char *propname, int intsz, int numint, void *buf) 44 { 45 zfs_prop_t prop; 46 47 if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL || 48 zfs_prop_readonly(prop)) 49 return (ENOENT); 50 51 if (zfs_prop_get_type(prop) == prop_type_string) { 52 if (intsz != 1) 53 return (EOVERFLOW); 54 (void) strncpy(buf, zfs_prop_default_string(prop), numint); 55 } else { 56 if (intsz != 8 || numint < 1) 57 return (EOVERFLOW); 58 59 *(uint64_t *)buf = zfs_prop_default_numeric(prop); 60 } 61 62 return (0); 63 } 64 65 static int 66 dsl_prop_get_impl(dsl_dir_t *dd, const char *propname, 67 int intsz, int numint, void *buf, char *setpoint) 68 { 69 int err = ENOENT; 70 zfs_prop_t prop; 71 72 if (setpoint) 73 setpoint[0] = '\0'; 74 75 prop = zfs_name_to_prop(propname); 76 77 /* 78 * Note: dd may be NULL, therefore we shouldn't dereference it 79 * ouside this loop. 80 */ 81 for (; dd != NULL; dd = dd->dd_parent) { 82 objset_t *mos = dd->dd_pool->dp_meta_objset; 83 ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock)); 84 err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, 85 propname, intsz, numint, buf); 86 if (err != ENOENT) { 87 if (setpoint) 88 dsl_dir_name(dd, setpoint); 89 break; 90 } 91 92 /* 93 * Break out of this loop for non-inheritable properties. 94 */ 95 if (prop != ZFS_PROP_INVAL && 96 !zfs_prop_inheritable(prop)) 97 break; 98 } 99 if (err == ENOENT) 100 err = dodefault(propname, intsz, numint, buf); 101 102 return (err); 103 } 104 105 /* 106 * Register interest in the named property. We'll call the callback 107 * once to notify it of the current property value, and again each time 108 * the property changes, until this callback is unregistered. 109 * 110 * Return 0 on success, errno if the prop is not an integer value. 111 */ 112 int 113 dsl_prop_register(dsl_dataset_t *ds, const char *propname, 114 dsl_prop_changed_cb_t *callback, void *cbarg) 115 { 116 dsl_dir_t *dd = ds->ds_dir; 117 uint64_t value; 118 dsl_prop_cb_record_t *cbr; 119 int err; 120 int need_rwlock; 121 122 need_rwlock = !RW_WRITE_HELD(&dd->dd_pool->dp_config_rwlock); 123 if (need_rwlock) 124 rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER); 125 126 err = dsl_prop_get_impl(dd, propname, 8, 1, &value, NULL); 127 if (err != 0) { 128 rw_exit(&dd->dd_pool->dp_config_rwlock); 129 return (err); 130 } 131 132 cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP); 133 cbr->cbr_ds = ds; 134 cbr->cbr_propname = kmem_alloc(strlen(propname)+1, KM_SLEEP); 135 (void) strcpy((char *)cbr->cbr_propname, propname); 136 cbr->cbr_func = callback; 137 cbr->cbr_arg = cbarg; 138 mutex_enter(&dd->dd_lock); 139 list_insert_head(&dd->dd_prop_cbs, cbr); 140 mutex_exit(&dd->dd_lock); 141 142 cbr->cbr_func(cbr->cbr_arg, value); 143 144 VERIFY(0 == dsl_dir_open_obj(dd->dd_pool, dd->dd_object, 145 NULL, cbr, &dd)); 146 if (need_rwlock) 147 rw_exit(&dd->dd_pool->dp_config_rwlock); 148 /* Leave dataset open until this callback is unregistered */ 149 return (0); 150 } 151 152 int 153 dsl_prop_get_ds(dsl_dir_t *dd, const char *propname, 154 int intsz, int numints, void *buf, char *setpoint) 155 { 156 int err; 157 158 rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER); 159 err = dsl_prop_get_impl(dd, propname, intsz, numints, buf, setpoint); 160 rw_exit(&dd->dd_pool->dp_config_rwlock); 161 162 return (err); 163 } 164 165 /* 166 * Get property when config lock is already held. 167 */ 168 int dsl_prop_get_ds_locked(dsl_dir_t *dd, const char *propname, 169 int intsz, int numints, void *buf, char *setpoint) 170 { 171 ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock)); 172 return (dsl_prop_get_impl(dd, propname, intsz, numints, buf, setpoint)); 173 } 174 175 int 176 dsl_prop_get(const char *ddname, const char *propname, 177 int intsz, int numints, void *buf, char *setpoint) 178 { 179 dsl_dir_t *dd; 180 const char *tail; 181 int err; 182 183 err = dsl_dir_open(ddname, FTAG, &dd, &tail); 184 if (err) 185 return (err); 186 if (tail && tail[0] != '@') { 187 dsl_dir_close(dd, FTAG); 188 return (ENOENT); 189 } 190 191 err = dsl_prop_get_ds(dd, propname, intsz, numints, buf, setpoint); 192 193 dsl_dir_close(dd, 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 280 ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock)); 281 err = dsl_dir_open_obj(dp, ddobj, NULL, FTAG, &dd); 282 if (err) 283 return; 284 285 if (!first) { 286 /* 287 * If the prop is set here, then this change is not 288 * being inherited here or below; stop the recursion. 289 */ 290 err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, propname, 291 8, 1, &value); 292 if (err == 0) { 293 dsl_dir_close(dd, FTAG); 294 return; 295 } 296 ASSERT3U(err, ==, ENOENT); 297 } 298 299 mutex_enter(&dd->dd_lock); 300 for (cbr = list_head(&dd->dd_prop_cbs); 301 cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) { 302 if (strcmp(cbr->cbr_propname, propname) == 0) { 303 cbr->cbr_func(cbr->cbr_arg, value); 304 } 305 } 306 mutex_exit(&dd->dd_lock); 307 308 for (zap_cursor_init(&zc, mos, 309 dd->dd_phys->dd_child_dir_zapobj); 310 zap_cursor_retrieve(&zc, &za) == 0; 311 zap_cursor_advance(&zc)) { 312 /* XXX recursion could blow stack; esp. za! */ 313 dsl_prop_changed_notify(dp, za.za_first_integer, 314 propname, value, FALSE); 315 } 316 zap_cursor_fini(&zc); 317 dsl_dir_close(dd, FTAG); 318 } 319 320 struct prop_set_arg { 321 const char *name; 322 int intsz; 323 int numints; 324 const void *buf; 325 }; 326 327 328 static void 329 dsl_prop_set_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx) 330 { 331 dsl_dir_t *dd = arg1; 332 struct prop_set_arg *psa = arg2; 333 objset_t *mos = dd->dd_pool->dp_meta_objset; 334 uint64_t zapobj = dd->dd_phys->dd_props_zapobj; 335 uint64_t intval; 336 int isint; 337 char valbuf[32]; 338 char *valstr; 339 340 isint = (dodefault(psa->name, 8, 1, &intval) == 0); 341 342 if (psa->numints == 0) { 343 int err = zap_remove(mos, zapobj, psa->name, tx); 344 ASSERT(err == 0 || err == ENOENT); 345 if (isint) { 346 VERIFY(0 == dsl_prop_get_impl(dd->dd_parent, 347 psa->name, 8, 1, &intval, NULL)); 348 } 349 } else { 350 VERIFY(0 == zap_update(mos, zapobj, psa->name, 351 psa->intsz, psa->numints, psa->buf, tx)); 352 if (isint) 353 intval = *(uint64_t *)psa->buf; 354 } 355 356 if (isint) { 357 dsl_prop_changed_notify(dd->dd_pool, 358 dd->dd_object, psa->name, intval, TRUE); 359 } 360 if (isint) { 361 (void) snprintf(valbuf, sizeof (valbuf), 362 "%lld", (longlong_t)intval); 363 valstr = valbuf; 364 } else { 365 valstr = (char *)psa->buf; 366 } 367 spa_history_internal_log((psa->numints == 0) ? LOG_DS_INHERIT : 368 LOG_DS_PROPSET, dd->dd_pool->dp_spa, tx, cr, 369 "%s=%s dataset = %llu", psa->name, valstr, 370 dd->dd_phys->dd_head_dataset_obj); 371 } 372 373 int 374 dsl_prop_set_dd(dsl_dir_t *dd, const char *propname, 375 int intsz, int numints, const void *buf) 376 { 377 struct prop_set_arg psa; 378 379 psa.name = propname; 380 psa.intsz = intsz; 381 psa.numints = numints; 382 psa.buf = buf; 383 384 return (dsl_sync_task_do(dd->dd_pool, 385 NULL, dsl_prop_set_sync, dd, &psa, 2)); 386 } 387 388 int 389 dsl_prop_set(const char *ddname, const char *propname, 390 int intsz, int numints, const void *buf) 391 { 392 dsl_dir_t *dd; 393 int err; 394 395 /* 396 * We must do these checks before we get to the syncfunc, since 397 * it can't fail. 398 */ 399 if (strlen(propname) >= ZAP_MAXNAMELEN) 400 return (ENAMETOOLONG); 401 if (intsz * numints >= ZAP_MAXVALUELEN) 402 return (E2BIG); 403 404 err = dsl_dir_open(ddname, FTAG, &dd, NULL); 405 if (err) 406 return (err); 407 err = dsl_prop_set_dd(dd, propname, intsz, numints, buf); 408 dsl_dir_close(dd, FTAG); 409 return (err); 410 } 411 412 /* 413 * Iterate over all properties for this dataset and return them in an nvlist. 414 */ 415 int 416 dsl_prop_get_all(objset_t *os, nvlist_t **nvp) 417 { 418 dsl_dataset_t *ds = os->os->os_dsl_dataset; 419 dsl_dir_t *dd = ds->ds_dir; 420 int err = 0; 421 dsl_pool_t *dp; 422 objset_t *mos; 423 424 if (dsl_dataset_is_snapshot(ds)) { 425 VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 426 return (0); 427 } 428 429 VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 430 431 dp = dd->dd_pool; 432 mos = dp->dp_meta_objset; 433 434 rw_enter(&dp->dp_config_rwlock, RW_READER); 435 for (; dd != NULL; dd = dd->dd_parent) { 436 char setpoint[MAXNAMELEN]; 437 zap_cursor_t zc; 438 zap_attribute_t za; 439 440 dsl_dir_name(dd, setpoint); 441 442 for (zap_cursor_init(&zc, mos, dd->dd_phys->dd_props_zapobj); 443 (err = zap_cursor_retrieve(&zc, &za)) == 0; 444 zap_cursor_advance(&zc)) { 445 nvlist_t *propval; 446 zfs_prop_t prop; 447 /* 448 * Skip non-inheritable properties. 449 */ 450 if ((prop = zfs_name_to_prop(za.za_name)) != 451 ZFS_PROP_INVAL && !zfs_prop_inheritable(prop) && 452 dd != ds->ds_dir) 453 continue; 454 455 if (nvlist_lookup_nvlist(*nvp, za.za_name, 456 &propval) == 0) 457 continue; 458 459 VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, 460 KM_SLEEP) == 0); 461 if (za.za_integer_length == 1) { 462 /* 463 * String property 464 */ 465 char *tmp = kmem_alloc(za.za_num_integers, 466 KM_SLEEP); 467 err = zap_lookup(mos, 468 dd->dd_phys->dd_props_zapobj, 469 za.za_name, 1, za.za_num_integers, 470 tmp); 471 if (err != 0) { 472 kmem_free(tmp, za.za_num_integers); 473 break; 474 } 475 VERIFY(nvlist_add_string(propval, 476 ZFS_PROP_VALUE, tmp) == 0); 477 kmem_free(tmp, za.za_num_integers); 478 } else { 479 /* 480 * Integer property 481 */ 482 ASSERT(za.za_integer_length == 8); 483 (void) nvlist_add_uint64(propval, 484 ZFS_PROP_VALUE, za.za_first_integer); 485 } 486 487 VERIFY(nvlist_add_string(propval, 488 ZFS_PROP_SOURCE, setpoint) == 0); 489 VERIFY(nvlist_add_nvlist(*nvp, za.za_name, 490 propval) == 0); 491 nvlist_free(propval); 492 } 493 zap_cursor_fini(&zc); 494 495 if (err != ENOENT) 496 break; 497 err = 0; 498 } 499 rw_exit(&dp->dp_config_rwlock); 500 501 return (err); 502 } 503 504 void 505 dsl_prop_nvlist_add_uint64(nvlist_t *nv, zfs_prop_t prop, uint64_t value) 506 { 507 nvlist_t *propval; 508 509 VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0); 510 VERIFY(nvlist_add_uint64(propval, ZFS_PROP_VALUE, value) == 0); 511 VERIFY(nvlist_add_nvlist(nv, zfs_prop_to_name(prop), propval) == 0); 512 nvlist_free(propval); 513 } 514 515 void 516 dsl_prop_nvlist_add_string(nvlist_t *nv, zfs_prop_t prop, const char *value) 517 { 518 nvlist_t *propval; 519 520 VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0); 521 VERIFY(nvlist_add_string(propval, ZFS_PROP_VALUE, value) == 0); 522 VERIFY(nvlist_add_nvlist(nv, zfs_prop_to_name(prop), propval) == 0); 523 nvlist_free(propval); 524 } 525