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