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