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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/dmu.h> 30 #include <sys/dmu_objset.h> 31 #include <sys/dmu_tx.h> 32 #include <sys/dsl_dataset.h> 33 #include <sys/dsl_dir.h> 34 #include <sys/dsl_prop.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_pool_t *dp, uint64_t ddobj, const char *propname, 67 int intsz, int numint, void *buf, char *setpoint) 68 { 69 int err = 0; 70 objset_t *mos = dp->dp_meta_objset; 71 72 if (setpoint) 73 setpoint[0] = '\0'; 74 75 ASSERT(RW_LOCK_HELD(&dp->dp_config_rwlock)); 76 77 while (ddobj != 0) { 78 dsl_dir_t *dd = dsl_dir_open_obj(dp, ddobj, NULL, FTAG); 79 err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, 80 propname, intsz, numint, buf); 81 if (err != ENOENT) { 82 if (setpoint) 83 dsl_dir_name(dd, setpoint); 84 dsl_dir_close(dd, FTAG); 85 break; 86 } 87 ASSERT3U(err, ==, ENOENT); 88 ddobj = dd->dd_phys->dd_parent_obj; 89 dsl_dir_close(dd, FTAG); 90 } 91 if (err == ENOENT) 92 err = dodefault(propname, intsz, numint, buf); 93 94 return (err); 95 } 96 97 /* 98 * Register interest in the named property. We'll call the callback 99 * once to notify it of the current property value, and again each time 100 * the property changes, until this callback is unregistered. 101 * 102 * Return 0 on success, errno if the prop is not an integer value. 103 */ 104 int 105 dsl_prop_register(dsl_dataset_t *ds, const char *propname, 106 dsl_prop_changed_cb_t *callback, void *cbarg) 107 { 108 dsl_dir_t *dd; 109 uint64_t value; 110 dsl_prop_cb_record_t *cbr; 111 int err; 112 113 dd = ds->ds_dir; 114 115 rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER); 116 117 err = dsl_prop_get_impl(dd->dd_pool, dd->dd_object, propname, 118 8, 1, &value, NULL); 119 if (err == ENOENT) { 120 err = 0; 121 value = DSL_PROP_VALUE_UNDEFINED; 122 } 123 if (err != 0) { 124 rw_exit(&dd->dd_pool->dp_config_rwlock); 125 return (err); 126 } 127 128 cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP); 129 cbr->cbr_propname = kmem_alloc(strlen(propname)+1, KM_SLEEP); 130 (void) strcpy((char *)cbr->cbr_propname, propname); 131 cbr->cbr_func = callback; 132 cbr->cbr_arg = cbarg; 133 mutex_enter(&dd->dd_lock); 134 list_insert_head(&dd->dd_prop_cbs, cbr); 135 mutex_exit(&dd->dd_lock); 136 137 cbr->cbr_func(cbr->cbr_arg, value); 138 139 (void) dsl_dir_open_obj(dd->dd_pool, dd->dd_object, NULL, cbr); 140 rw_exit(&dd->dd_pool->dp_config_rwlock); 141 /* Leave dataset open until this callback is unregistered */ 142 return (0); 143 } 144 145 int 146 dsl_prop_get_ds(dsl_dir_t *dd, const char *propname, 147 int intsz, int numints, void *buf, char *setpoint) 148 { 149 int err; 150 151 rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER); 152 err = dsl_prop_get_impl(dd->dd_pool, dd->dd_object, 153 propname, intsz, numints, buf, setpoint); 154 rw_exit(&dd->dd_pool->dp_config_rwlock); 155 156 return (err); 157 } 158 159 int 160 dsl_prop_get(const char *ddname, const char *propname, 161 int intsz, int numints, void *buf, char *setpoint) 162 { 163 dsl_dir_t *dd; 164 const char *tail; 165 int err; 166 167 dd = dsl_dir_open(ddname, FTAG, &tail); 168 if (dd == NULL) 169 return (ENOENT); 170 if (tail && tail[0] != '@') { 171 dsl_dir_close(dd, FTAG); 172 return (ENOENT); 173 } 174 175 err = dsl_prop_get_ds(dd, propname, intsz, numints, buf, setpoint); 176 177 dsl_dir_close(dd, FTAG); 178 return (err); 179 } 180 181 /* 182 * Return 0 on success, ENOENT if ddname is invalid, EOVERFLOW if 183 * valuelen not big enough. 184 */ 185 int 186 dsl_prop_get_string(const char *ddname, const char *propname, 187 char *value, int valuelen, char *setpoint) 188 { 189 return (dsl_prop_get(ddname, propname, 1, valuelen, value, setpoint)); 190 } 191 192 /* 193 * Get the current property value. It may have changed by the time this 194 * function returns, so it is NOT safe to follow up with 195 * dsl_prop_register() and assume that the value has not changed in 196 * between. 197 * 198 * Return 0 on success, ENOENT if ddname is invalid. 199 */ 200 int 201 dsl_prop_get_integer(const char *ddname, const char *propname, 202 uint64_t *valuep, char *setpoint) 203 { 204 return (dsl_prop_get(ddname, propname, 8, 1, valuep, setpoint)); 205 } 206 207 int 208 dsl_prop_get_ds_integer(dsl_dir_t *dd, const char *propname, 209 uint64_t *valuep, char *setpoint) 210 { 211 return (dsl_prop_get_ds(dd, propname, 8, 1, valuep, setpoint)); 212 } 213 214 /* 215 * Unregister this callback. Return 0 on success, ENOENT if ddname is 216 * invalid, ENOMSG if no matching callback registered. 217 */ 218 int 219 dsl_prop_unregister(dsl_dataset_t *ds, const char *propname, 220 dsl_prop_changed_cb_t *callback, void *cbarg) 221 { 222 dsl_dir_t *dd; 223 dsl_prop_cb_record_t *cbr; 224 225 dd = ds->ds_dir; 226 227 mutex_enter(&dd->dd_lock); 228 for (cbr = list_head(&dd->dd_prop_cbs); 229 cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) { 230 if (strcmp(cbr->cbr_propname, propname) == 0 && 231 cbr->cbr_func == callback && 232 cbr->cbr_arg == cbarg) 233 break; 234 } 235 236 if (cbr == NULL) { 237 mutex_exit(&dd->dd_lock); 238 return (ENOMSG); 239 } 240 241 list_remove(&dd->dd_prop_cbs, cbr); 242 mutex_exit(&dd->dd_lock); 243 kmem_free((void*)cbr->cbr_propname, strlen(cbr->cbr_propname)+1); 244 kmem_free(cbr, sizeof (dsl_prop_cb_record_t)); 245 246 /* Clean up from dsl_prop_register */ 247 dsl_dir_close(dd, cbr); 248 return (0); 249 } 250 251 static void 252 dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj, 253 const char *propname, uint64_t value, int first) 254 { 255 dsl_dir_t *dd; 256 dsl_prop_cb_record_t *cbr; 257 objset_t *mos = dp->dp_meta_objset; 258 int err; 259 260 ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock)); 261 dd = dsl_dir_open_obj(dp, ddobj, NULL, FTAG); 262 263 if (!first) { 264 /* 265 * If the prop is set here, then this change is not 266 * being inherited here or below; stop the recursion. 267 */ 268 err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, propname, 269 8, 1, &value); 270 if (err == 0) { 271 dsl_dir_close(dd, FTAG); 272 return; 273 } 274 ASSERT3U(err, ==, ENOENT); 275 } 276 277 mutex_enter(&dd->dd_lock); 278 for (cbr = list_head(&dd->dd_prop_cbs); 279 cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) { 280 if (strcmp(cbr->cbr_propname, propname) == 0) { 281 cbr->cbr_func(cbr->cbr_arg, value); 282 } 283 } 284 mutex_exit(&dd->dd_lock); 285 286 if (dd->dd_phys->dd_child_dir_zapobj) { 287 zap_cursor_t zc; 288 zap_attribute_t za; 289 290 for (zap_cursor_init(&zc, mos, 291 dd->dd_phys->dd_child_dir_zapobj); 292 zap_cursor_retrieve(&zc, &za) == 0; 293 zap_cursor_advance(&zc)) { 294 /* XXX recursion could blow stack; esp. za! */ 295 dsl_prop_changed_notify(dp, za.za_first_integer, 296 propname, value, FALSE); 297 } 298 zap_cursor_fini(&zc); 299 } 300 dsl_dir_close(dd, FTAG); 301 } 302 303 struct prop_set_arg { 304 const char *name; 305 int intsz; 306 int numints; 307 const void *buf; 308 }; 309 310 static int 311 dsl_prop_set_sync(dsl_dir_t *dd, void *arg, dmu_tx_t *tx) 312 { 313 struct prop_set_arg *psa = arg; 314 objset_t *mos = dd->dd_pool->dp_meta_objset; 315 uint64_t zapobj = dd->dd_phys->dd_props_zapobj; 316 uint64_t intval; 317 int err, isint; 318 319 rw_enter(&dd->dd_pool->dp_config_rwlock, RW_WRITER); 320 321 isint = (dodefault(psa->name, 8, 1, &intval) == 0); 322 323 if (psa->numints == 0) { 324 err = zap_remove(mos, zapobj, psa->name, tx); 325 if (err == ENOENT) /* that's fine. */ 326 err = 0; 327 if (err == 0 && isint) { 328 err = dsl_prop_get_impl(dd->dd_pool, 329 dd->dd_phys->dd_parent_obj, psa->name, 330 8, 1, &intval, NULL); 331 } 332 } else { 333 err = zap_update(mos, zapobj, psa->name, 334 psa->intsz, psa->numints, psa->buf, tx); 335 if (isint) 336 intval = *(uint64_t *)psa->buf; 337 } 338 339 if (err == 0 && isint) { 340 dsl_prop_changed_notify(dd->dd_pool, 341 dd->dd_object, psa->name, intval, TRUE); 342 } 343 rw_exit(&dd->dd_pool->dp_config_rwlock); 344 345 return (err); 346 } 347 348 int 349 dsl_prop_set(const char *ddname, const char *propname, 350 int intsz, int numints, const void *buf) 351 { 352 dsl_dir_t *dd; 353 int err; 354 struct prop_set_arg psa; 355 356 dd = dsl_dir_open(ddname, FTAG, NULL); 357 if (dd == NULL) 358 return (ENOENT); 359 360 psa.name = propname; 361 psa.intsz = intsz; 362 psa.numints = numints; 363 psa.buf = buf; 364 err = dsl_dir_sync_task(dd, dsl_prop_set_sync, &psa, 0); 365 366 dsl_dir_close(dd, FTAG); 367 368 return (err); 369 } 370 371 /* 372 * Iterate over all properties for this dataset and return them in an nvlist. 373 */ 374 int 375 dsl_prop_get_all(objset_t *os, nvlist_t **nvp) 376 { 377 dsl_dataset_t *ds = os->os->os_dsl_dataset; 378 dsl_dir_t *dd, *parent; 379 int err = 0; 380 dsl_pool_t *dp; 381 objset_t *mos; 382 zap_cursor_t zc; 383 zap_attribute_t za; 384 char setpoint[MAXNAMELEN]; 385 char *tmp; 386 nvlist_t *prop; 387 388 if (dsl_dataset_is_snapshot(ds)) { 389 VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 390 return (0); 391 } 392 393 dd = ds->ds_dir; 394 395 VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 396 397 dp = dd->dd_pool; 398 mos = dp->dp_meta_objset; 399 400 rw_enter(&dp->dp_config_rwlock, RW_READER); 401 while (dd != NULL) { 402 dsl_dir_name(dd, setpoint); 403 404 for (zap_cursor_init(&zc, mos, dd->dd_phys->dd_props_zapobj); 405 (err = zap_cursor_retrieve(&zc, &za)) == 0; 406 zap_cursor_advance(&zc)) { 407 if (nvlist_lookup_nvlist(*nvp, za.za_name, &prop) == 0) 408 continue; 409 410 VERIFY(nvlist_alloc(&prop, NV_UNIQUE_NAME, 411 KM_SLEEP) == 0); 412 if (za.za_integer_length == 1) { 413 /* 414 * String property 415 */ 416 417 tmp = kmem_alloc(za.za_num_integers, KM_SLEEP); 418 err = zap_lookup(mos, 419 dd->dd_phys->dd_props_zapobj, 420 za.za_name, 1, za.za_num_integers, 421 tmp); 422 if (err != 0) { 423 kmem_free(tmp, za.za_num_integers); 424 break; 425 } 426 VERIFY(nvlist_add_string(prop, 427 ZFS_PROP_VALUE, tmp) == 0); 428 kmem_free(tmp, za.za_num_integers); 429 } else { 430 /* 431 * Integer property 432 */ 433 ASSERT(za.za_integer_length == 8); 434 (void) nvlist_add_uint64(prop, ZFS_PROP_VALUE, 435 za.za_first_integer); 436 } 437 438 VERIFY(nvlist_add_string(prop, 439 ZFS_PROP_SOURCE, setpoint) == 0); 440 VERIFY(nvlist_add_nvlist(*nvp, za.za_name, 441 prop) == 0); 442 nvlist_free(prop); 443 } 444 zap_cursor_fini(&zc); 445 446 if (err != ENOENT) { 447 if (dd != ds->ds_dir) 448 dsl_dir_close(dd, FTAG); 449 break; 450 } else { 451 err = 0; 452 } 453 454 /* 455 * Continue to parent. 456 */ 457 if (dd->dd_phys->dd_parent_obj == 0) 458 parent = NULL; 459 else 460 parent = dsl_dir_open_obj(dp, 461 dd->dd_phys->dd_parent_obj, NULL, FTAG); 462 if (dd != ds->ds_dir) 463 dsl_dir_close(dd, FTAG); 464 dd = parent; 465 } 466 rw_exit(&dp->dp_config_rwlock); 467 468 return (err); 469 } 470