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 2006 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 int 166 dsl_prop_get(const char *ddname, const char *propname, 167 int intsz, int numints, void *buf, char *setpoint) 168 { 169 dsl_dir_t *dd; 170 const char *tail; 171 int err; 172 173 err = dsl_dir_open(ddname, FTAG, &dd, &tail); 174 if (err) 175 return (err); 176 if (tail && tail[0] != '@') { 177 dsl_dir_close(dd, FTAG); 178 return (ENOENT); 179 } 180 181 err = dsl_prop_get_ds(dd, propname, intsz, numints, buf, setpoint); 182 183 dsl_dir_close(dd, FTAG); 184 return (err); 185 } 186 187 /* 188 * Get the current property value. It may have changed by the time this 189 * function returns, so it is NOT safe to follow up with 190 * dsl_prop_register() and assume that the value has not changed in 191 * between. 192 * 193 * Return 0 on success, ENOENT if ddname is invalid. 194 */ 195 int 196 dsl_prop_get_integer(const char *ddname, const char *propname, 197 uint64_t *valuep, char *setpoint) 198 { 199 return (dsl_prop_get(ddname, propname, 8, 1, valuep, setpoint)); 200 } 201 202 /* 203 * Unregister this callback. Return 0 on success, ENOENT if ddname is 204 * invalid, ENOMSG if no matching callback registered. 205 */ 206 int 207 dsl_prop_unregister(dsl_dataset_t *ds, const char *propname, 208 dsl_prop_changed_cb_t *callback, void *cbarg) 209 { 210 dsl_dir_t *dd = ds->ds_dir; 211 dsl_prop_cb_record_t *cbr; 212 213 mutex_enter(&dd->dd_lock); 214 for (cbr = list_head(&dd->dd_prop_cbs); 215 cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) { 216 if (cbr->cbr_ds == ds && 217 cbr->cbr_func == callback && 218 cbr->cbr_arg == cbarg && 219 strcmp(cbr->cbr_propname, propname) == 0) 220 break; 221 } 222 223 if (cbr == NULL) { 224 mutex_exit(&dd->dd_lock); 225 return (ENOMSG); 226 } 227 228 list_remove(&dd->dd_prop_cbs, cbr); 229 mutex_exit(&dd->dd_lock); 230 kmem_free((void*)cbr->cbr_propname, strlen(cbr->cbr_propname)+1); 231 kmem_free(cbr, sizeof (dsl_prop_cb_record_t)); 232 233 /* Clean up from dsl_prop_register */ 234 dsl_dir_close(dd, cbr); 235 return (0); 236 } 237 238 /* 239 * Return the number of callbacks that are registered for this dataset. 240 */ 241 int 242 dsl_prop_numcb(dsl_dataset_t *ds) 243 { 244 dsl_dir_t *dd = ds->ds_dir; 245 dsl_prop_cb_record_t *cbr; 246 int num = 0; 247 248 mutex_enter(&dd->dd_lock); 249 for (cbr = list_head(&dd->dd_prop_cbs); 250 cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) { 251 if (cbr->cbr_ds == ds) 252 num++; 253 } 254 mutex_exit(&dd->dd_lock); 255 256 return (num); 257 } 258 259 static void 260 dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj, 261 const char *propname, uint64_t value, int first) 262 { 263 dsl_dir_t *dd; 264 dsl_prop_cb_record_t *cbr; 265 objset_t *mos = dp->dp_meta_objset; 266 zap_cursor_t zc; 267 zap_attribute_t za; 268 int err; 269 270 ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock)); 271 err = dsl_dir_open_obj(dp, ddobj, NULL, FTAG, &dd); 272 if (err) 273 return; 274 275 if (!first) { 276 /* 277 * If the prop is set here, then this change is not 278 * being inherited here or below; stop the recursion. 279 */ 280 err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, propname, 281 8, 1, &value); 282 if (err == 0) { 283 dsl_dir_close(dd, FTAG); 284 return; 285 } 286 ASSERT3U(err, ==, ENOENT); 287 } 288 289 mutex_enter(&dd->dd_lock); 290 for (cbr = list_head(&dd->dd_prop_cbs); 291 cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) { 292 if (strcmp(cbr->cbr_propname, propname) == 0) { 293 cbr->cbr_func(cbr->cbr_arg, value); 294 } 295 } 296 mutex_exit(&dd->dd_lock); 297 298 for (zap_cursor_init(&zc, mos, 299 dd->dd_phys->dd_child_dir_zapobj); 300 zap_cursor_retrieve(&zc, &za) == 0; 301 zap_cursor_advance(&zc)) { 302 /* XXX recursion could blow stack; esp. za! */ 303 dsl_prop_changed_notify(dp, za.za_first_integer, 304 propname, value, FALSE); 305 } 306 zap_cursor_fini(&zc); 307 dsl_dir_close(dd, FTAG); 308 } 309 310 struct prop_set_arg { 311 const char *name; 312 int intsz; 313 int numints; 314 const void *buf; 315 }; 316 317 318 static void 319 dsl_prop_set_sync(void *arg1, void *arg2, dmu_tx_t *tx) 320 { 321 dsl_dir_t *dd = arg1; 322 struct prop_set_arg *psa = arg2; 323 objset_t *mos = dd->dd_pool->dp_meta_objset; 324 uint64_t zapobj = dd->dd_phys->dd_props_zapobj; 325 uint64_t intval; 326 int isint; 327 328 isint = (dodefault(psa->name, 8, 1, &intval) == 0); 329 330 if (psa->numints == 0) { 331 int err = zap_remove(mos, zapobj, psa->name, tx); 332 ASSERT(err == 0 || err == ENOENT); 333 if (isint) { 334 VERIFY(0 == dsl_prop_get_impl(dd->dd_parent, 335 psa->name, 8, 1, &intval, NULL)); 336 } 337 } else { 338 VERIFY(0 == zap_update(mos, zapobj, psa->name, 339 psa->intsz, psa->numints, psa->buf, tx)); 340 if (isint) 341 intval = *(uint64_t *)psa->buf; 342 } 343 344 if (isint) { 345 dsl_prop_changed_notify(dd->dd_pool, 346 dd->dd_object, psa->name, intval, TRUE); 347 } 348 } 349 350 int 351 dsl_prop_set_dd(dsl_dir_t *dd, const char *propname, 352 int intsz, int numints, const void *buf) 353 { 354 struct prop_set_arg psa; 355 356 psa.name = propname; 357 psa.intsz = intsz; 358 psa.numints = numints; 359 psa.buf = buf; 360 361 return (dsl_sync_task_do(dd->dd_pool, 362 NULL, dsl_prop_set_sync, dd, &psa, 2)); 363 } 364 365 int 366 dsl_prop_set(const char *ddname, const char *propname, 367 int intsz, int numints, const void *buf) 368 { 369 dsl_dir_t *dd; 370 int err; 371 372 /* 373 * We must do these checks before we get to the syncfunc, since 374 * it can't fail. 375 */ 376 if (strlen(propname) >= ZAP_MAXNAMELEN) 377 return (ENAMETOOLONG); 378 if (intsz * numints >= ZAP_MAXVALUELEN) 379 return (E2BIG); 380 381 err = dsl_dir_open(ddname, FTAG, &dd, NULL); 382 if (err) 383 return (err); 384 err = dsl_prop_set_dd(dd, propname, intsz, numints, buf); 385 dsl_dir_close(dd, FTAG); 386 return (err); 387 } 388 389 /* 390 * Iterate over all properties for this dataset and return them in an nvlist. 391 */ 392 int 393 dsl_prop_get_all(objset_t *os, nvlist_t **nvp) 394 { 395 dsl_dataset_t *ds = os->os->os_dsl_dataset; 396 dsl_dir_t *dd = ds->ds_dir; 397 int err = 0; 398 dsl_pool_t *dp; 399 objset_t *mos; 400 401 if (dsl_dataset_is_snapshot(ds)) { 402 VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 403 return (0); 404 } 405 406 VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 407 408 dp = dd->dd_pool; 409 mos = dp->dp_meta_objset; 410 411 rw_enter(&dp->dp_config_rwlock, RW_READER); 412 for (; dd != NULL; dd = dd->dd_parent) { 413 char setpoint[MAXNAMELEN]; 414 zap_cursor_t zc; 415 zap_attribute_t za; 416 417 dsl_dir_name(dd, setpoint); 418 419 for (zap_cursor_init(&zc, mos, dd->dd_phys->dd_props_zapobj); 420 (err = zap_cursor_retrieve(&zc, &za)) == 0; 421 zap_cursor_advance(&zc)) { 422 nvlist_t *propval; 423 zfs_prop_t prop; 424 /* 425 * Skip non-inheritable properties. 426 */ 427 if ((prop = zfs_name_to_prop(za.za_name)) != 428 ZFS_PROP_INVAL && !zfs_prop_inheritable(prop) && 429 dd != ds->ds_dir) 430 continue; 431 432 if (nvlist_lookup_nvlist(*nvp, za.za_name, 433 &propval) == 0) 434 continue; 435 436 VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, 437 KM_SLEEP) == 0); 438 if (za.za_integer_length == 1) { 439 /* 440 * String property 441 */ 442 char *tmp = kmem_alloc(za.za_num_integers, 443 KM_SLEEP); 444 err = zap_lookup(mos, 445 dd->dd_phys->dd_props_zapobj, 446 za.za_name, 1, za.za_num_integers, 447 tmp); 448 if (err != 0) { 449 kmem_free(tmp, za.za_num_integers); 450 break; 451 } 452 VERIFY(nvlist_add_string(propval, 453 ZFS_PROP_VALUE, tmp) == 0); 454 kmem_free(tmp, za.za_num_integers); 455 } else { 456 /* 457 * Integer property 458 */ 459 ASSERT(za.za_integer_length == 8); 460 (void) nvlist_add_uint64(propval, 461 ZFS_PROP_VALUE, za.za_first_integer); 462 } 463 464 VERIFY(nvlist_add_string(propval, 465 ZFS_PROP_SOURCE, setpoint) == 0); 466 VERIFY(nvlist_add_nvlist(*nvp, za.za_name, 467 propval) == 0); 468 nvlist_free(propval); 469 } 470 zap_cursor_fini(&zc); 471 472 if (err != ENOENT) 473 break; 474 err = 0; 475 } 476 rw_exit(&dp->dp_config_rwlock); 477 478 return (err); 479 } 480 481 void 482 dsl_prop_nvlist_add_uint64(nvlist_t *nv, zfs_prop_t prop, uint64_t value) 483 { 484 nvlist_t *propval; 485 486 VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0); 487 VERIFY(nvlist_add_uint64(propval, ZFS_PROP_VALUE, value) == 0); 488 VERIFY(nvlist_add_nvlist(nv, zfs_prop_to_name(prop), propval) == 0); 489 nvlist_free(propval); 490 } 491 492 void 493 dsl_prop_nvlist_add_string(nvlist_t *nv, zfs_prop_t prop, const char *value) 494 { 495 nvlist_t *propval; 496 497 VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0); 498 VERIFY(nvlist_add_string(propval, ZFS_PROP_VALUE, value) == 0); 499 VERIFY(nvlist_add_nvlist(nv, zfs_prop_to_name(prop), propval) == 0); 500 nvlist_free(propval); 501 } 502