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