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/spa.h> 35 #include <sys/zio_checksum.h> /* for the default checksum value */ 36 #include <sys/zap.h> 37 #include <sys/fs/zfs.h> 38 39 #include "zfs_prop.h" 40 41 static int 42 dodefault(const char *propname, int intsz, int numint, void *buf) 43 { 44 zfs_prop_t prop; 45 46 if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL || 47 zfs_prop_readonly(prop)) 48 return (ENOENT); 49 50 if (zfs_prop_get_type(prop) == prop_type_string) { 51 if (intsz != 1) 52 return (EOVERFLOW); 53 (void) strncpy(buf, zfs_prop_default_string(prop), numint); 54 } else { 55 if (intsz != 8 || numint < 1) 56 return (EOVERFLOW); 57 58 *(uint64_t *)buf = zfs_prop_default_numeric(prop); 59 } 60 61 return (0); 62 } 63 64 static int 65 dsl_prop_get_impl(dsl_pool_t *dp, uint64_t ddobj, const char *propname, 66 int intsz, int numint, void *buf, char *setpoint) 67 { 68 int err = 0; 69 objset_t *mos = dp->dp_meta_objset; 70 71 if (setpoint) 72 setpoint[0] = '\0'; 73 74 ASSERT(RW_LOCK_HELD(&dp->dp_config_rwlock)); 75 76 while (ddobj != 0) { 77 dsl_dir_t *dd; 78 err = dsl_dir_open_obj(dp, ddobj, NULL, FTAG, &dd); 79 if (err) 80 break; 81 err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, 82 propname, intsz, numint, buf); 83 if (err != ENOENT) { 84 if (setpoint) 85 dsl_dir_name(dd, setpoint); 86 dsl_dir_close(dd, FTAG); 87 break; 88 } 89 ASSERT3U(err, ==, ENOENT); 90 ddobj = dd->dd_phys->dd_parent_obj; 91 dsl_dir_close(dd, FTAG); 92 } 93 if (err == ENOENT) 94 err = dodefault(propname, intsz, numint, buf); 95 96 return (err); 97 } 98 99 /* 100 * Register interest in the named property. We'll call the callback 101 * once to notify it of the current property value, and again each time 102 * the property changes, until this callback is unregistered. 103 * 104 * Return 0 on success, errno if the prop is not an integer value. 105 */ 106 int 107 dsl_prop_register(dsl_dataset_t *ds, const char *propname, 108 dsl_prop_changed_cb_t *callback, void *cbarg) 109 { 110 dsl_dir_t *dd; 111 uint64_t value; 112 dsl_prop_cb_record_t *cbr; 113 int err; 114 115 dd = ds->ds_dir; 116 117 rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER); 118 119 err = dsl_prop_get_impl(dd->dd_pool, dd->dd_object, propname, 120 8, 1, &value, NULL); 121 if (err == ENOENT) { 122 err = 0; 123 value = DSL_PROP_VALUE_UNDEFINED; 124 } 125 if (err != 0) { 126 rw_exit(&dd->dd_pool->dp_config_rwlock); 127 return (err); 128 } 129 130 cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP); 131 cbr->cbr_propname = kmem_alloc(strlen(propname)+1, KM_SLEEP); 132 (void) strcpy((char *)cbr->cbr_propname, propname); 133 cbr->cbr_func = callback; 134 cbr->cbr_arg = cbarg; 135 mutex_enter(&dd->dd_lock); 136 list_insert_head(&dd->dd_prop_cbs, cbr); 137 mutex_exit(&dd->dd_lock); 138 139 cbr->cbr_func(cbr->cbr_arg, value); 140 141 VERIFY(0 == dsl_dir_open_obj(dd->dd_pool, dd->dd_object, 142 NULL, cbr, &dd)); 143 rw_exit(&dd->dd_pool->dp_config_rwlock); 144 /* Leave dataset open until this callback is unregistered */ 145 return (0); 146 } 147 148 int 149 dsl_prop_get_ds(dsl_dir_t *dd, const char *propname, 150 int intsz, int numints, void *buf, char *setpoint) 151 { 152 int err; 153 154 rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER); 155 err = dsl_prop_get_impl(dd->dd_pool, dd->dd_object, 156 propname, intsz, numints, buf, setpoint); 157 rw_exit(&dd->dd_pool->dp_config_rwlock); 158 159 return (err); 160 } 161 162 int 163 dsl_prop_get(const char *ddname, const char *propname, 164 int intsz, int numints, void *buf, char *setpoint) 165 { 166 dsl_dir_t *dd; 167 const char *tail; 168 int err; 169 170 err = dsl_dir_open(ddname, FTAG, &dd, &tail); 171 if (err) 172 return (err); 173 if (tail && tail[0] != '@') { 174 dsl_dir_close(dd, FTAG); 175 return (ENOENT); 176 } 177 178 err = dsl_prop_get_ds(dd, propname, intsz, numints, buf, setpoint); 179 180 dsl_dir_close(dd, FTAG); 181 return (err); 182 } 183 184 /* 185 * Return 0 on success, ENOENT if ddname is invalid, EOVERFLOW if 186 * valuelen not big enough. 187 */ 188 int 189 dsl_prop_get_string(const char *ddname, const char *propname, 190 char *value, int valuelen, char *setpoint) 191 { 192 return (dsl_prop_get(ddname, propname, 1, valuelen, value, setpoint)); 193 } 194 195 /* 196 * Get the current property value. It may have changed by the time this 197 * function returns, so it is NOT safe to follow up with 198 * dsl_prop_register() and assume that the value has not changed in 199 * between. 200 * 201 * Return 0 on success, ENOENT if ddname is invalid. 202 */ 203 int 204 dsl_prop_get_integer(const char *ddname, const char *propname, 205 uint64_t *valuep, char *setpoint) 206 { 207 return (dsl_prop_get(ddname, propname, 8, 1, valuep, setpoint)); 208 } 209 210 int 211 dsl_prop_get_ds_integer(dsl_dir_t *dd, const char *propname, 212 uint64_t *valuep, char *setpoint) 213 { 214 return (dsl_prop_get_ds(dd, propname, 8, 1, valuep, setpoint)); 215 } 216 217 /* 218 * Unregister this callback. Return 0 on success, ENOENT if ddname is 219 * invalid, ENOMSG if no matching callback registered. 220 */ 221 int 222 dsl_prop_unregister(dsl_dataset_t *ds, const char *propname, 223 dsl_prop_changed_cb_t *callback, void *cbarg) 224 { 225 dsl_dir_t *dd; 226 dsl_prop_cb_record_t *cbr; 227 228 dd = ds->ds_dir; 229 230 mutex_enter(&dd->dd_lock); 231 for (cbr = list_head(&dd->dd_prop_cbs); 232 cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) { 233 if (strcmp(cbr->cbr_propname, propname) == 0 && 234 cbr->cbr_func == callback && 235 cbr->cbr_arg == cbarg) 236 break; 237 } 238 239 if (cbr == NULL) { 240 mutex_exit(&dd->dd_lock); 241 return (ENOMSG); 242 } 243 244 list_remove(&dd->dd_prop_cbs, cbr); 245 mutex_exit(&dd->dd_lock); 246 kmem_free((void*)cbr->cbr_propname, strlen(cbr->cbr_propname)+1); 247 kmem_free(cbr, sizeof (dsl_prop_cb_record_t)); 248 249 /* Clean up from dsl_prop_register */ 250 dsl_dir_close(dd, cbr); 251 return (0); 252 } 253 254 static void 255 dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj, 256 const char *propname, uint64_t value, int first) 257 { 258 dsl_dir_t *dd; 259 dsl_prop_cb_record_t *cbr; 260 objset_t *mos = dp->dp_meta_objset; 261 int err; 262 263 ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock)); 264 err = dsl_dir_open_obj(dp, ddobj, NULL, FTAG, &dd); 265 if (err) 266 return; 267 268 if (!first) { 269 /* 270 * If the prop is set here, then this change is not 271 * being inherited here or below; stop the recursion. 272 */ 273 err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, propname, 274 8, 1, &value); 275 if (err == 0) { 276 dsl_dir_close(dd, FTAG); 277 return; 278 } 279 ASSERT3U(err, ==, ENOENT); 280 } 281 282 mutex_enter(&dd->dd_lock); 283 for (cbr = list_head(&dd->dd_prop_cbs); 284 cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) { 285 if (strcmp(cbr->cbr_propname, propname) == 0) { 286 cbr->cbr_func(cbr->cbr_arg, value); 287 } 288 } 289 mutex_exit(&dd->dd_lock); 290 291 if (dd->dd_phys->dd_child_dir_zapobj) { 292 zap_cursor_t zc; 293 zap_attribute_t za; 294 295 for (zap_cursor_init(&zc, mos, 296 dd->dd_phys->dd_child_dir_zapobj); 297 zap_cursor_retrieve(&zc, &za) == 0; 298 zap_cursor_advance(&zc)) { 299 /* XXX recursion could blow stack; esp. za! */ 300 dsl_prop_changed_notify(dp, za.za_first_integer, 301 propname, value, FALSE); 302 } 303 zap_cursor_fini(&zc); 304 } 305 dsl_dir_close(dd, FTAG); 306 } 307 308 struct prop_set_arg { 309 const char *name; 310 int intsz; 311 int numints; 312 const void *buf; 313 }; 314 315 static int 316 dsl_prop_set_sync(dsl_dir_t *dd, void *arg, dmu_tx_t *tx) 317 { 318 struct prop_set_arg *psa = arg; 319 objset_t *mos = dd->dd_pool->dp_meta_objset; 320 uint64_t zapobj = dd->dd_phys->dd_props_zapobj; 321 uint64_t intval; 322 int err, isint; 323 324 rw_enter(&dd->dd_pool->dp_config_rwlock, RW_WRITER); 325 326 isint = (dodefault(psa->name, 8, 1, &intval) == 0); 327 328 if (psa->numints == 0) { 329 err = zap_remove(mos, zapobj, psa->name, tx); 330 if (err == ENOENT) /* that's fine. */ 331 err = 0; 332 if (err == 0 && isint) { 333 err = dsl_prop_get_impl(dd->dd_pool, 334 dd->dd_phys->dd_parent_obj, psa->name, 335 8, 1, &intval, NULL); 336 } 337 } else { 338 err = 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 (err == 0 && isint) { 345 dsl_prop_changed_notify(dd->dd_pool, 346 dd->dd_object, psa->name, intval, TRUE); 347 } 348 rw_exit(&dd->dd_pool->dp_config_rwlock); 349 350 return (err); 351 } 352 353 int 354 dsl_prop_set(const char *ddname, const char *propname, 355 int intsz, int numints, const void *buf) 356 { 357 dsl_dir_t *dd; 358 int err; 359 struct prop_set_arg psa; 360 361 err = dsl_dir_open(ddname, FTAG, &dd, NULL); 362 if (err) 363 return (err); 364 365 psa.name = propname; 366 psa.intsz = intsz; 367 psa.numints = numints; 368 psa.buf = buf; 369 err = dsl_dir_sync_task(dd, dsl_prop_set_sync, &psa, 1<<20); 370 371 dsl_dir_close(dd, FTAG); 372 373 return (err); 374 } 375 376 /* 377 * Iterate over all properties for this dataset and return them in an nvlist. 378 */ 379 int 380 dsl_prop_get_all(objset_t *os, nvlist_t **nvp) 381 { 382 dsl_dataset_t *ds = os->os->os_dsl_dataset; 383 dsl_dir_t *dd, *parent; 384 int err = 0; 385 dsl_pool_t *dp; 386 objset_t *mos; 387 zap_cursor_t zc; 388 zap_attribute_t za; 389 char setpoint[MAXNAMELEN]; 390 char *tmp; 391 nvlist_t *prop; 392 393 if (dsl_dataset_is_snapshot(ds)) { 394 VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 395 return (0); 396 } 397 398 dd = ds->ds_dir; 399 400 VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 401 402 dp = dd->dd_pool; 403 mos = dp->dp_meta_objset; 404 405 rw_enter(&dp->dp_config_rwlock, RW_READER); 406 while (dd != NULL) { 407 dsl_dir_name(dd, setpoint); 408 409 for (zap_cursor_init(&zc, mos, dd->dd_phys->dd_props_zapobj); 410 (err = zap_cursor_retrieve(&zc, &za)) == 0; 411 zap_cursor_advance(&zc)) { 412 if (nvlist_lookup_nvlist(*nvp, za.za_name, &prop) == 0) 413 continue; 414 415 VERIFY(nvlist_alloc(&prop, NV_UNIQUE_NAME, 416 KM_SLEEP) == 0); 417 if (za.za_integer_length == 1) { 418 /* 419 * String property 420 */ 421 422 tmp = kmem_alloc(za.za_num_integers, KM_SLEEP); 423 err = zap_lookup(mos, 424 dd->dd_phys->dd_props_zapobj, 425 za.za_name, 1, za.za_num_integers, 426 tmp); 427 if (err != 0) { 428 kmem_free(tmp, za.za_num_integers); 429 break; 430 } 431 VERIFY(nvlist_add_string(prop, 432 ZFS_PROP_VALUE, tmp) == 0); 433 kmem_free(tmp, za.za_num_integers); 434 } else { 435 /* 436 * Integer property 437 */ 438 ASSERT(za.za_integer_length == 8); 439 (void) nvlist_add_uint64(prop, ZFS_PROP_VALUE, 440 za.za_first_integer); 441 } 442 443 VERIFY(nvlist_add_string(prop, 444 ZFS_PROP_SOURCE, setpoint) == 0); 445 VERIFY(nvlist_add_nvlist(*nvp, za.za_name, 446 prop) == 0); 447 nvlist_free(prop); 448 } 449 zap_cursor_fini(&zc); 450 451 if (err != ENOENT) { 452 if (dd != ds->ds_dir) 453 dsl_dir_close(dd, FTAG); 454 break; 455 } else { 456 err = 0; 457 } 458 459 /* 460 * Continue to parent. 461 */ 462 if (dd->dd_phys->dd_parent_obj == 0) 463 parent = NULL; 464 else 465 err = dsl_dir_open_obj(dp, 466 dd->dd_phys->dd_parent_obj, NULL, FTAG, &parent); 467 if (dd != ds->ds_dir) 468 dsl_dir_close(dd, FTAG); 469 if (err) 470 break; 471 dd = parent; 472 } 473 rw_exit(&dp->dp_config_rwlock); 474 475 return (err); 476 } 477