1eda14cbcSMatt Macy /* 2eda14cbcSMatt Macy * CDDL HEADER START 3eda14cbcSMatt Macy * 4eda14cbcSMatt Macy * The contents of this file are subject to the terms of the 5eda14cbcSMatt Macy * Common Development and Distribution License (the "License"). 6eda14cbcSMatt Macy * You may not use this file except in compliance with the License. 7eda14cbcSMatt Macy * 8eda14cbcSMatt Macy * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9271171e0SMartin Matuska * or https://opensource.org/licenses/CDDL-1.0. 10eda14cbcSMatt Macy * See the License for the specific language governing permissions 11eda14cbcSMatt Macy * and limitations under the License. 12eda14cbcSMatt Macy * 13eda14cbcSMatt Macy * When distributing Covered Code, include this CDDL HEADER in each 14eda14cbcSMatt Macy * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15eda14cbcSMatt Macy * If applicable, add the following below this CDDL HEADER, with the 16eda14cbcSMatt Macy * fields enclosed by brackets "[]" replaced with your own identifying 17eda14cbcSMatt Macy * information: Portions Copyright [yyyy] [name of copyright owner] 18eda14cbcSMatt Macy * 19eda14cbcSMatt Macy * CDDL HEADER END 20eda14cbcSMatt Macy */ 21eda14cbcSMatt Macy /* 22eda14cbcSMatt Macy * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 23eda14cbcSMatt Macy * Copyright (c) 2012, 2015 by Delphix. All rights reserved. 24eda14cbcSMatt Macy * Copyright (c) 2013 Martin Matuska. All rights reserved. 25eda14cbcSMatt Macy * Copyright 2019 Joyent, Inc. 26dbd5678dSMartin Matuska * Copyright (c) 2022 Hewlett Packard Enterprise Development LP. 27eda14cbcSMatt Macy */ 28eda14cbcSMatt Macy 29eda14cbcSMatt Macy #include <sys/zfs_context.h> 30eda14cbcSMatt Macy #include <sys/dmu.h> 31eda14cbcSMatt Macy #include <sys/dmu_objset.h> 32eda14cbcSMatt Macy #include <sys/dmu_tx.h> 33eda14cbcSMatt Macy #include <sys/dsl_dataset.h> 34eda14cbcSMatt Macy #include <sys/dsl_dir.h> 35eda14cbcSMatt Macy #include <sys/dsl_prop.h> 36eda14cbcSMatt Macy #include <sys/dsl_synctask.h> 37eda14cbcSMatt Macy #include <sys/spa.h> 38eda14cbcSMatt Macy #include <sys/zap.h> 39eda14cbcSMatt Macy #include <sys/fs/zfs.h> 40eda14cbcSMatt Macy 41eda14cbcSMatt Macy #include "zfs_prop.h" 42eda14cbcSMatt Macy 43eda14cbcSMatt Macy #define ZPROP_INHERIT_SUFFIX "$inherit" 44eda14cbcSMatt Macy #define ZPROP_RECVD_SUFFIX "$recvd" 45dbd5678dSMartin Matuska #define ZPROP_IUV_SUFFIX "$iuv" 46eda14cbcSMatt Macy 47eda14cbcSMatt Macy static int 48eda14cbcSMatt Macy dodefault(zfs_prop_t prop, int intsz, int numints, void *buf) 49eda14cbcSMatt Macy { 50eda14cbcSMatt Macy /* 51eda14cbcSMatt Macy * The setonce properties are read-only, BUT they still 52eda14cbcSMatt Macy * have a default value that can be used as the initial 53eda14cbcSMatt Macy * value. 54eda14cbcSMatt Macy */ 55eda14cbcSMatt Macy if (prop == ZPROP_INVAL || 56eda14cbcSMatt Macy (zfs_prop_readonly(prop) && !zfs_prop_setonce(prop))) 57eda14cbcSMatt Macy return (SET_ERROR(ENOENT)); 58eda14cbcSMatt Macy 59eda14cbcSMatt Macy if (zfs_prop_get_type(prop) == PROP_TYPE_STRING) { 60eda14cbcSMatt Macy if (intsz != 1) 61eda14cbcSMatt Macy return (SET_ERROR(EOVERFLOW)); 62be181ee2SMartin Matuska (void) strlcpy(buf, zfs_prop_default_string(prop), 63eda14cbcSMatt Macy numints); 64eda14cbcSMatt Macy } else { 65eda14cbcSMatt Macy if (intsz != 8 || numints < 1) 66eda14cbcSMatt Macy return (SET_ERROR(EOVERFLOW)); 67eda14cbcSMatt Macy 68eda14cbcSMatt Macy *(uint64_t *)buf = zfs_prop_default_numeric(prop); 69eda14cbcSMatt Macy } 70eda14cbcSMatt Macy 71eda14cbcSMatt Macy return (0); 72eda14cbcSMatt Macy } 73eda14cbcSMatt Macy 74dbd5678dSMartin Matuska static int 75dbd5678dSMartin Matuska dsl_prop_known_index(zfs_prop_t prop, uint64_t value) 76dbd5678dSMartin Matuska { 77dbd5678dSMartin Matuska const char *str = NULL; 78dbd5678dSMartin Matuska if (prop != ZPROP_CONT && prop != ZPROP_INVAL && 79dbd5678dSMartin Matuska zfs_prop_get_type(prop) == PROP_TYPE_INDEX) 80dbd5678dSMartin Matuska return (!zfs_prop_index_to_string(prop, value, &str)); 81dbd5678dSMartin Matuska 82dbd5678dSMartin Matuska return (-1); 83dbd5678dSMartin Matuska } 84dbd5678dSMartin Matuska 85eda14cbcSMatt Macy int 86eda14cbcSMatt Macy dsl_prop_get_dd(dsl_dir_t *dd, const char *propname, 87eda14cbcSMatt Macy int intsz, int numints, void *buf, char *setpoint, boolean_t snapshot) 88eda14cbcSMatt Macy { 89eda14cbcSMatt Macy int err; 90eda14cbcSMatt Macy dsl_dir_t *target = dd; 91eda14cbcSMatt Macy objset_t *mos = dd->dd_pool->dp_meta_objset; 92eda14cbcSMatt Macy zfs_prop_t prop; 93eda14cbcSMatt Macy boolean_t inheritable; 94eda14cbcSMatt Macy boolean_t inheriting = B_FALSE; 95eda14cbcSMatt Macy char *inheritstr; 96eda14cbcSMatt Macy char *recvdstr; 97dbd5678dSMartin Matuska char *iuvstr; 98eda14cbcSMatt Macy 99eda14cbcSMatt Macy ASSERT(dsl_pool_config_held(dd->dd_pool)); 100eda14cbcSMatt Macy 101eda14cbcSMatt Macy if (setpoint) 102eda14cbcSMatt Macy setpoint[0] = '\0'; 103eda14cbcSMatt Macy 104eda14cbcSMatt Macy prop = zfs_name_to_prop(propname); 1051f1e2261SMartin Matuska inheritable = (prop == ZPROP_USERPROP || zfs_prop_inheritable(prop)); 106eda14cbcSMatt Macy inheritstr = kmem_asprintf("%s%s", propname, ZPROP_INHERIT_SUFFIX); 107eda14cbcSMatt Macy recvdstr = kmem_asprintf("%s%s", propname, ZPROP_RECVD_SUFFIX); 108dbd5678dSMartin Matuska iuvstr = kmem_asprintf("%s%s", propname, ZPROP_IUV_SUFFIX); 109eda14cbcSMatt Macy 110eda14cbcSMatt Macy /* 111eda14cbcSMatt Macy * Note: dd may become NULL, therefore we shouldn't dereference it 112eda14cbcSMatt Macy * after this loop. 113eda14cbcSMatt Macy */ 114eda14cbcSMatt Macy for (; dd != NULL; dd = dd->dd_parent) { 115eda14cbcSMatt Macy if (dd != target || snapshot) { 116eda14cbcSMatt Macy if (!inheritable) { 117eda14cbcSMatt Macy err = SET_ERROR(ENOENT); 118eda14cbcSMatt Macy break; 119eda14cbcSMatt Macy } 120eda14cbcSMatt Macy inheriting = B_TRUE; 121eda14cbcSMatt Macy } 122eda14cbcSMatt Macy 123dbd5678dSMartin Matuska /* Check for a iuv value. */ 124dbd5678dSMartin Matuska err = zap_lookup(mos, dsl_dir_phys(dd)->dd_props_zapobj, 125dbd5678dSMartin Matuska iuvstr, intsz, numints, buf); 12615f0b8c3SMartin Matuska if (err == 0 && dsl_prop_known_index(prop, 127dbd5678dSMartin Matuska *(uint64_t *)buf) != 1) 128dbd5678dSMartin Matuska err = ENOENT; 129dbd5678dSMartin Matuska if (err != ENOENT) { 130dbd5678dSMartin Matuska if (setpoint != NULL && err == 0) 131dbd5678dSMartin Matuska dsl_dir_name(dd, setpoint); 132dbd5678dSMartin Matuska break; 133dbd5678dSMartin Matuska } 134dbd5678dSMartin Matuska 135eda14cbcSMatt Macy /* Check for a local value. */ 136eda14cbcSMatt Macy err = zap_lookup(mos, dsl_dir_phys(dd)->dd_props_zapobj, 137eda14cbcSMatt Macy propname, intsz, numints, buf); 138eda14cbcSMatt Macy if (err != ENOENT) { 139eda14cbcSMatt Macy if (setpoint != NULL && err == 0) 140eda14cbcSMatt Macy dsl_dir_name(dd, setpoint); 141eda14cbcSMatt Macy break; 142eda14cbcSMatt Macy } 143eda14cbcSMatt Macy 144eda14cbcSMatt Macy /* 145eda14cbcSMatt Macy * Skip the check for a received value if there is an explicit 146eda14cbcSMatt Macy * inheritance entry. 147eda14cbcSMatt Macy */ 148eda14cbcSMatt Macy err = zap_contains(mos, dsl_dir_phys(dd)->dd_props_zapobj, 149eda14cbcSMatt Macy inheritstr); 150eda14cbcSMatt Macy if (err != 0 && err != ENOENT) 151eda14cbcSMatt Macy break; 152eda14cbcSMatt Macy 153eda14cbcSMatt Macy if (err == ENOENT) { 154eda14cbcSMatt Macy /* Check for a received value. */ 155eda14cbcSMatt Macy err = zap_lookup(mos, dsl_dir_phys(dd)->dd_props_zapobj, 156eda14cbcSMatt Macy recvdstr, intsz, numints, buf); 157eda14cbcSMatt Macy if (err != ENOENT) { 158eda14cbcSMatt Macy if (setpoint != NULL && err == 0) { 159eda14cbcSMatt Macy if (inheriting) { 160eda14cbcSMatt Macy dsl_dir_name(dd, setpoint); 161eda14cbcSMatt Macy } else { 162eda14cbcSMatt Macy (void) strlcpy(setpoint, 163eda14cbcSMatt Macy ZPROP_SOURCE_VAL_RECVD, 164eda14cbcSMatt Macy MAXNAMELEN); 165eda14cbcSMatt Macy } 166eda14cbcSMatt Macy } 167eda14cbcSMatt Macy break; 168eda14cbcSMatt Macy } 169eda14cbcSMatt Macy } 170eda14cbcSMatt Macy 171eda14cbcSMatt Macy /* 172eda14cbcSMatt Macy * If we found an explicit inheritance entry, err is zero even 173eda14cbcSMatt Macy * though we haven't yet found the value, so reinitializing err 174eda14cbcSMatt Macy * at the end of the loop (instead of at the beginning) ensures 175eda14cbcSMatt Macy * that err has a valid post-loop value. 176eda14cbcSMatt Macy */ 177eda14cbcSMatt Macy err = SET_ERROR(ENOENT); 178eda14cbcSMatt Macy } 179eda14cbcSMatt Macy 180eda14cbcSMatt Macy if (err == ENOENT) 181eda14cbcSMatt Macy err = dodefault(prop, intsz, numints, buf); 182eda14cbcSMatt Macy 183eda14cbcSMatt Macy kmem_strfree(inheritstr); 184eda14cbcSMatt Macy kmem_strfree(recvdstr); 185dbd5678dSMartin Matuska kmem_strfree(iuvstr); 186eda14cbcSMatt Macy 187eda14cbcSMatt Macy return (err); 188eda14cbcSMatt Macy } 189eda14cbcSMatt Macy 190eda14cbcSMatt Macy int 191eda14cbcSMatt Macy dsl_prop_get_ds(dsl_dataset_t *ds, const char *propname, 192eda14cbcSMatt Macy int intsz, int numints, void *buf, char *setpoint) 193eda14cbcSMatt Macy { 194eda14cbcSMatt Macy zfs_prop_t prop = zfs_name_to_prop(propname); 195eda14cbcSMatt Macy boolean_t inheritable; 196eda14cbcSMatt Macy uint64_t zapobj; 197eda14cbcSMatt Macy 198eda14cbcSMatt Macy ASSERT(dsl_pool_config_held(ds->ds_dir->dd_pool)); 1991f1e2261SMartin Matuska inheritable = (prop == ZPROP_USERPROP || zfs_prop_inheritable(prop)); 200eda14cbcSMatt Macy zapobj = dsl_dataset_phys(ds)->ds_props_obj; 201eda14cbcSMatt Macy 202eda14cbcSMatt Macy if (zapobj != 0) { 203eda14cbcSMatt Macy objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; 204eda14cbcSMatt Macy int err; 205eda14cbcSMatt Macy 206eda14cbcSMatt Macy ASSERT(ds->ds_is_snapshot); 207eda14cbcSMatt Macy 208eda14cbcSMatt Macy /* Check for a local value. */ 209eda14cbcSMatt Macy err = zap_lookup(mos, zapobj, propname, intsz, numints, buf); 210eda14cbcSMatt Macy if (err != ENOENT) { 211eda14cbcSMatt Macy if (setpoint != NULL && err == 0) 212eda14cbcSMatt Macy dsl_dataset_name(ds, setpoint); 213eda14cbcSMatt Macy return (err); 214eda14cbcSMatt Macy } 215eda14cbcSMatt Macy 216eda14cbcSMatt Macy /* 217eda14cbcSMatt Macy * Skip the check for a received value if there is an explicit 218eda14cbcSMatt Macy * inheritance entry. 219eda14cbcSMatt Macy */ 220eda14cbcSMatt Macy if (inheritable) { 221eda14cbcSMatt Macy char *inheritstr = kmem_asprintf("%s%s", propname, 222eda14cbcSMatt Macy ZPROP_INHERIT_SUFFIX); 223eda14cbcSMatt Macy err = zap_contains(mos, zapobj, inheritstr); 224eda14cbcSMatt Macy kmem_strfree(inheritstr); 225eda14cbcSMatt Macy if (err != 0 && err != ENOENT) 226eda14cbcSMatt Macy return (err); 227eda14cbcSMatt Macy } 228eda14cbcSMatt Macy 229eda14cbcSMatt Macy if (err == ENOENT) { 230eda14cbcSMatt Macy /* Check for a received value. */ 231eda14cbcSMatt Macy char *recvdstr = kmem_asprintf("%s%s", propname, 232eda14cbcSMatt Macy ZPROP_RECVD_SUFFIX); 233eda14cbcSMatt Macy err = zap_lookup(mos, zapobj, recvdstr, 234eda14cbcSMatt Macy intsz, numints, buf); 235eda14cbcSMatt Macy kmem_strfree(recvdstr); 236eda14cbcSMatt Macy if (err != ENOENT) { 237eda14cbcSMatt Macy if (setpoint != NULL && err == 0) 238eda14cbcSMatt Macy (void) strlcpy(setpoint, 239eda14cbcSMatt Macy ZPROP_SOURCE_VAL_RECVD, 240eda14cbcSMatt Macy MAXNAMELEN); 241eda14cbcSMatt Macy return (err); 242eda14cbcSMatt Macy } 243eda14cbcSMatt Macy } 244eda14cbcSMatt Macy } 245eda14cbcSMatt Macy 246eda14cbcSMatt Macy return (dsl_prop_get_dd(ds->ds_dir, propname, 247eda14cbcSMatt Macy intsz, numints, buf, setpoint, ds->ds_is_snapshot)); 248eda14cbcSMatt Macy } 249eda14cbcSMatt Macy 250eda14cbcSMatt Macy static dsl_prop_record_t * 251eda14cbcSMatt Macy dsl_prop_record_find(dsl_dir_t *dd, const char *propname) 252eda14cbcSMatt Macy { 253eda14cbcSMatt Macy dsl_prop_record_t *pr = NULL; 254eda14cbcSMatt Macy 255eda14cbcSMatt Macy ASSERT(MUTEX_HELD(&dd->dd_lock)); 256eda14cbcSMatt Macy 257eda14cbcSMatt Macy for (pr = list_head(&dd->dd_props); 258eda14cbcSMatt Macy pr != NULL; pr = list_next(&dd->dd_props, pr)) { 259eda14cbcSMatt Macy if (strcmp(pr->pr_propname, propname) == 0) 260eda14cbcSMatt Macy break; 261eda14cbcSMatt Macy } 262eda14cbcSMatt Macy 263eda14cbcSMatt Macy return (pr); 264eda14cbcSMatt Macy } 265eda14cbcSMatt Macy 266eda14cbcSMatt Macy static dsl_prop_record_t * 267eda14cbcSMatt Macy dsl_prop_record_create(dsl_dir_t *dd, const char *propname) 268eda14cbcSMatt Macy { 269eda14cbcSMatt Macy dsl_prop_record_t *pr; 270eda14cbcSMatt Macy 271eda14cbcSMatt Macy ASSERT(MUTEX_HELD(&dd->dd_lock)); 272eda14cbcSMatt Macy 273eda14cbcSMatt Macy pr = kmem_alloc(sizeof (dsl_prop_record_t), KM_SLEEP); 274eda14cbcSMatt Macy pr->pr_propname = spa_strdup(propname); 275eda14cbcSMatt Macy list_create(&pr->pr_cbs, sizeof (dsl_prop_cb_record_t), 276eda14cbcSMatt Macy offsetof(dsl_prop_cb_record_t, cbr_pr_node)); 277eda14cbcSMatt Macy list_insert_head(&dd->dd_props, pr); 278eda14cbcSMatt Macy 279eda14cbcSMatt Macy return (pr); 280eda14cbcSMatt Macy } 281eda14cbcSMatt Macy 282eda14cbcSMatt Macy void 283eda14cbcSMatt Macy dsl_prop_init(dsl_dir_t *dd) 284eda14cbcSMatt Macy { 285eda14cbcSMatt Macy list_create(&dd->dd_props, sizeof (dsl_prop_record_t), 286eda14cbcSMatt Macy offsetof(dsl_prop_record_t, pr_node)); 287eda14cbcSMatt Macy } 288eda14cbcSMatt Macy 289eda14cbcSMatt Macy void 290eda14cbcSMatt Macy dsl_prop_fini(dsl_dir_t *dd) 291eda14cbcSMatt Macy { 292eda14cbcSMatt Macy dsl_prop_record_t *pr; 293eda14cbcSMatt Macy 294eda14cbcSMatt Macy while ((pr = list_remove_head(&dd->dd_props)) != NULL) { 295eda14cbcSMatt Macy list_destroy(&pr->pr_cbs); 296eda14cbcSMatt Macy spa_strfree((char *)pr->pr_propname); 297eda14cbcSMatt Macy kmem_free(pr, sizeof (dsl_prop_record_t)); 298eda14cbcSMatt Macy } 299eda14cbcSMatt Macy list_destroy(&dd->dd_props); 300eda14cbcSMatt Macy } 301eda14cbcSMatt Macy 302eda14cbcSMatt Macy /* 303eda14cbcSMatt Macy * Register interest in the named property. We'll call the callback 304eda14cbcSMatt Macy * once to notify it of the current property value, and again each time 305eda14cbcSMatt Macy * the property changes, until this callback is unregistered. 306eda14cbcSMatt Macy * 307eda14cbcSMatt Macy * Return 0 on success, errno if the prop is not an integer value. 308eda14cbcSMatt Macy */ 309eda14cbcSMatt Macy int 310eda14cbcSMatt Macy dsl_prop_register(dsl_dataset_t *ds, const char *propname, 311eda14cbcSMatt Macy dsl_prop_changed_cb_t *callback, void *cbarg) 312eda14cbcSMatt Macy { 313eda14cbcSMatt Macy dsl_dir_t *dd = ds->ds_dir; 314eda14cbcSMatt Macy uint64_t value; 315eda14cbcSMatt Macy dsl_prop_record_t *pr; 316eda14cbcSMatt Macy dsl_prop_cb_record_t *cbr; 317eda14cbcSMatt Macy int err; 318eda14cbcSMatt Macy dsl_pool_t *dp __maybe_unused = dd->dd_pool; 319eda14cbcSMatt Macy 320eda14cbcSMatt Macy ASSERT(dsl_pool_config_held(dp)); 321eda14cbcSMatt Macy 322eda14cbcSMatt Macy err = dsl_prop_get_int_ds(ds, propname, &value); 323eda14cbcSMatt Macy if (err != 0) 324eda14cbcSMatt Macy return (err); 325eda14cbcSMatt Macy 326eda14cbcSMatt Macy cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP); 327eda14cbcSMatt Macy cbr->cbr_ds = ds; 328eda14cbcSMatt Macy cbr->cbr_func = callback; 329eda14cbcSMatt Macy cbr->cbr_arg = cbarg; 330eda14cbcSMatt Macy 331eda14cbcSMatt Macy mutex_enter(&dd->dd_lock); 332eda14cbcSMatt Macy pr = dsl_prop_record_find(dd, propname); 333eda14cbcSMatt Macy if (pr == NULL) 334eda14cbcSMatt Macy pr = dsl_prop_record_create(dd, propname); 335eda14cbcSMatt Macy cbr->cbr_pr = pr; 336eda14cbcSMatt Macy list_insert_head(&pr->pr_cbs, cbr); 337eda14cbcSMatt Macy list_insert_head(&ds->ds_prop_cbs, cbr); 338eda14cbcSMatt Macy mutex_exit(&dd->dd_lock); 339eda14cbcSMatt Macy 340eda14cbcSMatt Macy cbr->cbr_func(cbr->cbr_arg, value); 341eda14cbcSMatt Macy return (0); 342eda14cbcSMatt Macy } 343eda14cbcSMatt Macy 344eda14cbcSMatt Macy int 345eda14cbcSMatt Macy dsl_prop_get(const char *dsname, const char *propname, 346eda14cbcSMatt Macy int intsz, int numints, void *buf, char *setpoint) 347eda14cbcSMatt Macy { 348eda14cbcSMatt Macy objset_t *os; 349eda14cbcSMatt Macy int error; 350eda14cbcSMatt Macy 351eda14cbcSMatt Macy error = dmu_objset_hold(dsname, FTAG, &os); 352eda14cbcSMatt Macy if (error != 0) 353eda14cbcSMatt Macy return (error); 354eda14cbcSMatt Macy 355eda14cbcSMatt Macy error = dsl_prop_get_ds(dmu_objset_ds(os), propname, 356eda14cbcSMatt Macy intsz, numints, buf, setpoint); 357eda14cbcSMatt Macy 358eda14cbcSMatt Macy dmu_objset_rele(os, FTAG); 359eda14cbcSMatt Macy return (error); 360eda14cbcSMatt Macy } 361eda14cbcSMatt Macy 362eda14cbcSMatt Macy /* 363eda14cbcSMatt Macy * Get the current property value. It may have changed by the time this 364eda14cbcSMatt Macy * function returns, so it is NOT safe to follow up with 365eda14cbcSMatt Macy * dsl_prop_register() and assume that the value has not changed in 366eda14cbcSMatt Macy * between. 367eda14cbcSMatt Macy * 368eda14cbcSMatt Macy * Return 0 on success, ENOENT if ddname is invalid. 369eda14cbcSMatt Macy */ 370eda14cbcSMatt Macy int 371eda14cbcSMatt Macy dsl_prop_get_integer(const char *ddname, const char *propname, 372eda14cbcSMatt Macy uint64_t *valuep, char *setpoint) 373eda14cbcSMatt Macy { 374eda14cbcSMatt Macy return (dsl_prop_get(ddname, propname, 8, 1, valuep, setpoint)); 375eda14cbcSMatt Macy } 376eda14cbcSMatt Macy 377eda14cbcSMatt Macy int 378eda14cbcSMatt Macy dsl_prop_get_int_ds(dsl_dataset_t *ds, const char *propname, 379eda14cbcSMatt Macy uint64_t *valuep) 380eda14cbcSMatt Macy { 381eda14cbcSMatt Macy return (dsl_prop_get_ds(ds, propname, 8, 1, valuep, NULL)); 382eda14cbcSMatt Macy } 383eda14cbcSMatt Macy 384eda14cbcSMatt Macy /* 385eda14cbcSMatt Macy * Predict the effective value of the given special property if it were set with 386eda14cbcSMatt Macy * the given value and source. This is not a general purpose function. It exists 387eda14cbcSMatt Macy * only to handle the special requirements of the quota and reservation 388eda14cbcSMatt Macy * properties. The fact that these properties are non-inheritable greatly 389eda14cbcSMatt Macy * simplifies the prediction logic. 390eda14cbcSMatt Macy * 391eda14cbcSMatt Macy * Returns 0 on success, a positive error code on failure, or -1 if called with 392eda14cbcSMatt Macy * a property not handled by this function. 393eda14cbcSMatt Macy */ 394eda14cbcSMatt Macy int 395eda14cbcSMatt Macy dsl_prop_predict(dsl_dir_t *dd, const char *propname, 396eda14cbcSMatt Macy zprop_source_t source, uint64_t value, uint64_t *newvalp) 397eda14cbcSMatt Macy { 398eda14cbcSMatt Macy zfs_prop_t prop = zfs_name_to_prop(propname); 399eda14cbcSMatt Macy objset_t *mos; 400eda14cbcSMatt Macy uint64_t zapobj; 401eda14cbcSMatt Macy uint64_t version; 402eda14cbcSMatt Macy char *recvdstr; 403eda14cbcSMatt Macy int err = 0; 404eda14cbcSMatt Macy 405eda14cbcSMatt Macy switch (prop) { 406eda14cbcSMatt Macy case ZFS_PROP_QUOTA: 407eda14cbcSMatt Macy case ZFS_PROP_RESERVATION: 408eda14cbcSMatt Macy case ZFS_PROP_REFQUOTA: 409eda14cbcSMatt Macy case ZFS_PROP_REFRESERVATION: 410eda14cbcSMatt Macy break; 411eda14cbcSMatt Macy default: 412eda14cbcSMatt Macy return (-1); 413eda14cbcSMatt Macy } 414eda14cbcSMatt Macy 415eda14cbcSMatt Macy mos = dd->dd_pool->dp_meta_objset; 416eda14cbcSMatt Macy zapobj = dsl_dir_phys(dd)->dd_props_zapobj; 417eda14cbcSMatt Macy recvdstr = kmem_asprintf("%s%s", propname, ZPROP_RECVD_SUFFIX); 418eda14cbcSMatt Macy 419eda14cbcSMatt Macy version = spa_version(dd->dd_pool->dp_spa); 420eda14cbcSMatt Macy if (version < SPA_VERSION_RECVD_PROPS) { 421eda14cbcSMatt Macy if (source & ZPROP_SRC_NONE) 422eda14cbcSMatt Macy source = ZPROP_SRC_NONE; 423eda14cbcSMatt Macy else if (source & ZPROP_SRC_RECEIVED) 424eda14cbcSMatt Macy source = ZPROP_SRC_LOCAL; 425eda14cbcSMatt Macy } 426eda14cbcSMatt Macy 427eda14cbcSMatt Macy switch ((int)source) { 428eda14cbcSMatt Macy case ZPROP_SRC_NONE: 429eda14cbcSMatt Macy /* Revert to the received value, if any. */ 430eda14cbcSMatt Macy err = zap_lookup(mos, zapobj, recvdstr, 8, 1, newvalp); 431eda14cbcSMatt Macy if (err == ENOENT) 432eda14cbcSMatt Macy *newvalp = 0; 433eda14cbcSMatt Macy break; 434eda14cbcSMatt Macy case ZPROP_SRC_LOCAL: 435eda14cbcSMatt Macy *newvalp = value; 436eda14cbcSMatt Macy break; 437eda14cbcSMatt Macy case ZPROP_SRC_RECEIVED: 438eda14cbcSMatt Macy /* 439eda14cbcSMatt Macy * If there's no local setting, then the new received value will 440eda14cbcSMatt Macy * be the effective value. 441eda14cbcSMatt Macy */ 442eda14cbcSMatt Macy err = zap_lookup(mos, zapobj, propname, 8, 1, newvalp); 443eda14cbcSMatt Macy if (err == ENOENT) 444eda14cbcSMatt Macy *newvalp = value; 445eda14cbcSMatt Macy break; 446eda14cbcSMatt Macy case (ZPROP_SRC_NONE | ZPROP_SRC_RECEIVED): 447eda14cbcSMatt Macy /* 448eda14cbcSMatt Macy * We're clearing the received value, so the local setting (if 449eda14cbcSMatt Macy * it exists) remains the effective value. 450eda14cbcSMatt Macy */ 451eda14cbcSMatt Macy err = zap_lookup(mos, zapobj, propname, 8, 1, newvalp); 452eda14cbcSMatt Macy if (err == ENOENT) 453eda14cbcSMatt Macy *newvalp = 0; 454eda14cbcSMatt Macy break; 455eda14cbcSMatt Macy default: 456eda14cbcSMatt Macy panic("unexpected property source: %d", source); 457eda14cbcSMatt Macy } 458eda14cbcSMatt Macy 459eda14cbcSMatt Macy kmem_strfree(recvdstr); 460eda14cbcSMatt Macy 461eda14cbcSMatt Macy if (err == ENOENT) 462eda14cbcSMatt Macy return (0); 463eda14cbcSMatt Macy 464eda14cbcSMatt Macy return (err); 465eda14cbcSMatt Macy } 466eda14cbcSMatt Macy 467eda14cbcSMatt Macy /* 468eda14cbcSMatt Macy * Unregister this callback. Return 0 on success, ENOENT if ddname is 469eda14cbcSMatt Macy * invalid, or ENOMSG if no matching callback registered. 470eda14cbcSMatt Macy * 471eda14cbcSMatt Macy * NOTE: This function is no longer used internally but has been preserved 472eda14cbcSMatt Macy * to prevent breaking external consumers (Lustre, etc). 473eda14cbcSMatt Macy */ 474eda14cbcSMatt Macy int 475eda14cbcSMatt Macy dsl_prop_unregister(dsl_dataset_t *ds, const char *propname, 476eda14cbcSMatt Macy dsl_prop_changed_cb_t *callback, void *cbarg) 477eda14cbcSMatt Macy { 478eda14cbcSMatt Macy dsl_dir_t *dd = ds->ds_dir; 479eda14cbcSMatt Macy dsl_prop_cb_record_t *cbr; 480eda14cbcSMatt Macy 481eda14cbcSMatt Macy mutex_enter(&dd->dd_lock); 482eda14cbcSMatt Macy for (cbr = list_head(&ds->ds_prop_cbs); 483eda14cbcSMatt Macy cbr; cbr = list_next(&ds->ds_prop_cbs, cbr)) { 484eda14cbcSMatt Macy if (cbr->cbr_ds == ds && 485eda14cbcSMatt Macy cbr->cbr_func == callback && 486eda14cbcSMatt Macy cbr->cbr_arg == cbarg && 487eda14cbcSMatt Macy strcmp(cbr->cbr_pr->pr_propname, propname) == 0) 488eda14cbcSMatt Macy break; 489eda14cbcSMatt Macy } 490eda14cbcSMatt Macy 491eda14cbcSMatt Macy if (cbr == NULL) { 492eda14cbcSMatt Macy mutex_exit(&dd->dd_lock); 493eda14cbcSMatt Macy return (SET_ERROR(ENOMSG)); 494eda14cbcSMatt Macy } 495eda14cbcSMatt Macy 496eda14cbcSMatt Macy list_remove(&ds->ds_prop_cbs, cbr); 497eda14cbcSMatt Macy list_remove(&cbr->cbr_pr->pr_cbs, cbr); 498eda14cbcSMatt Macy mutex_exit(&dd->dd_lock); 499eda14cbcSMatt Macy kmem_free(cbr, sizeof (dsl_prop_cb_record_t)); 500eda14cbcSMatt Macy 501eda14cbcSMatt Macy return (0); 502eda14cbcSMatt Macy } 503eda14cbcSMatt Macy 504eda14cbcSMatt Macy /* 505eda14cbcSMatt Macy * Unregister all callbacks that are registered with the 506eda14cbcSMatt Macy * given callback argument. 507eda14cbcSMatt Macy */ 508eda14cbcSMatt Macy void 509eda14cbcSMatt Macy dsl_prop_unregister_all(dsl_dataset_t *ds, void *cbarg) 510eda14cbcSMatt Macy { 511eda14cbcSMatt Macy dsl_prop_cb_record_t *cbr, *next_cbr; 512eda14cbcSMatt Macy 513eda14cbcSMatt Macy dsl_dir_t *dd = ds->ds_dir; 514eda14cbcSMatt Macy 515eda14cbcSMatt Macy mutex_enter(&dd->dd_lock); 516eda14cbcSMatt Macy next_cbr = list_head(&ds->ds_prop_cbs); 517eda14cbcSMatt Macy while (next_cbr != NULL) { 518eda14cbcSMatt Macy cbr = next_cbr; 519eda14cbcSMatt Macy next_cbr = list_next(&ds->ds_prop_cbs, cbr); 520eda14cbcSMatt Macy if (cbr->cbr_arg == cbarg) { 521eda14cbcSMatt Macy list_remove(&ds->ds_prop_cbs, cbr); 522eda14cbcSMatt Macy list_remove(&cbr->cbr_pr->pr_cbs, cbr); 523eda14cbcSMatt Macy kmem_free(cbr, sizeof (dsl_prop_cb_record_t)); 524eda14cbcSMatt Macy } 525eda14cbcSMatt Macy } 526eda14cbcSMatt Macy mutex_exit(&dd->dd_lock); 527eda14cbcSMatt Macy } 528eda14cbcSMatt Macy 529eda14cbcSMatt Macy boolean_t 530eda14cbcSMatt Macy dsl_prop_hascb(dsl_dataset_t *ds) 531eda14cbcSMatt Macy { 532eda14cbcSMatt Macy return (!list_is_empty(&ds->ds_prop_cbs)); 533eda14cbcSMatt Macy } 534eda14cbcSMatt Macy 535eda14cbcSMatt Macy static int 536eda14cbcSMatt Macy dsl_prop_notify_all_cb(dsl_pool_t *dp, dsl_dataset_t *ds, void *arg) 537eda14cbcSMatt Macy { 538e92ffd9bSMartin Matuska (void) arg; 539eda14cbcSMatt Macy dsl_dir_t *dd = ds->ds_dir; 540eda14cbcSMatt Macy dsl_prop_record_t *pr; 541eda14cbcSMatt Macy dsl_prop_cb_record_t *cbr; 542eda14cbcSMatt Macy 543eda14cbcSMatt Macy mutex_enter(&dd->dd_lock); 544eda14cbcSMatt Macy for (pr = list_head(&dd->dd_props); 545eda14cbcSMatt Macy pr; pr = list_next(&dd->dd_props, pr)) { 546eda14cbcSMatt Macy for (cbr = list_head(&pr->pr_cbs); cbr; 547eda14cbcSMatt Macy cbr = list_next(&pr->pr_cbs, cbr)) { 548eda14cbcSMatt Macy uint64_t value; 549eda14cbcSMatt Macy 550eda14cbcSMatt Macy /* 551eda14cbcSMatt Macy * Callback entries do not have holds on their 552eda14cbcSMatt Macy * datasets so that datasets with registered 553eda14cbcSMatt Macy * callbacks are still eligible for eviction. 554eda14cbcSMatt Macy * Unlike operations to update properties on a 555eda14cbcSMatt Macy * single dataset, we are performing a recursive 556eda14cbcSMatt Macy * descent of related head datasets. The caller 557eda14cbcSMatt Macy * of this function only has a dataset hold on 558eda14cbcSMatt Macy * the passed in head dataset, not the snapshots 559eda14cbcSMatt Macy * associated with this dataset. Without a hold, 560eda14cbcSMatt Macy * the dataset pointer within callback records 561eda14cbcSMatt Macy * for snapshots can be invalidated by eviction 562eda14cbcSMatt Macy * at any time. 563eda14cbcSMatt Macy * 564eda14cbcSMatt Macy * Use dsl_dataset_try_add_ref() to verify 565eda14cbcSMatt Macy * that the dataset for a snapshot has not 566eda14cbcSMatt Macy * begun eviction processing and to prevent 567eda14cbcSMatt Macy * eviction from occurring for the duration of 568eda14cbcSMatt Macy * the callback. If the hold attempt fails, 569eda14cbcSMatt Macy * this object is already being evicted and the 570eda14cbcSMatt Macy * callback can be safely ignored. 571eda14cbcSMatt Macy */ 572eda14cbcSMatt Macy if (ds != cbr->cbr_ds && 573eda14cbcSMatt Macy !dsl_dataset_try_add_ref(dp, cbr->cbr_ds, FTAG)) 574eda14cbcSMatt Macy continue; 575eda14cbcSMatt Macy 576eda14cbcSMatt Macy if (dsl_prop_get_ds(cbr->cbr_ds, 577eda14cbcSMatt Macy cbr->cbr_pr->pr_propname, sizeof (value), 1, 578eda14cbcSMatt Macy &value, NULL) == 0) 579eda14cbcSMatt Macy cbr->cbr_func(cbr->cbr_arg, value); 580eda14cbcSMatt Macy 581eda14cbcSMatt Macy if (ds != cbr->cbr_ds) 582eda14cbcSMatt Macy dsl_dataset_rele(cbr->cbr_ds, FTAG); 583eda14cbcSMatt Macy } 584eda14cbcSMatt Macy } 585eda14cbcSMatt Macy mutex_exit(&dd->dd_lock); 586eda14cbcSMatt Macy 587eda14cbcSMatt Macy return (0); 588eda14cbcSMatt Macy } 589eda14cbcSMatt Macy 590eda14cbcSMatt Macy /* 591eda14cbcSMatt Macy * Update all property values for ddobj & its descendants. This is used 592eda14cbcSMatt Macy * when renaming the dir. 593eda14cbcSMatt Macy */ 594eda14cbcSMatt Macy void 595eda14cbcSMatt Macy dsl_prop_notify_all(dsl_dir_t *dd) 596eda14cbcSMatt Macy { 597eda14cbcSMatt Macy dsl_pool_t *dp = dd->dd_pool; 598eda14cbcSMatt Macy ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); 599eda14cbcSMatt Macy (void) dmu_objset_find_dp(dp, dd->dd_object, dsl_prop_notify_all_cb, 600eda14cbcSMatt Macy NULL, DS_FIND_CHILDREN); 601eda14cbcSMatt Macy } 602eda14cbcSMatt Macy 603eda14cbcSMatt Macy static void 604eda14cbcSMatt Macy dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj, 605eda14cbcSMatt Macy const char *propname, uint64_t value, int first) 606eda14cbcSMatt Macy { 607eda14cbcSMatt Macy dsl_dir_t *dd; 608eda14cbcSMatt Macy dsl_prop_record_t *pr; 609eda14cbcSMatt Macy dsl_prop_cb_record_t *cbr; 610eda14cbcSMatt Macy objset_t *mos = dp->dp_meta_objset; 611eda14cbcSMatt Macy zap_cursor_t zc; 612eda14cbcSMatt Macy zap_attribute_t *za; 613eda14cbcSMatt Macy int err; 614eda14cbcSMatt Macy 615eda14cbcSMatt Macy ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); 616eda14cbcSMatt Macy err = dsl_dir_hold_obj(dp, ddobj, NULL, FTAG, &dd); 617eda14cbcSMatt Macy if (err) 618eda14cbcSMatt Macy return; 619eda14cbcSMatt Macy 620eda14cbcSMatt Macy if (!first) { 621eda14cbcSMatt Macy /* 622eda14cbcSMatt Macy * If the prop is set here, then this change is not 623eda14cbcSMatt Macy * being inherited here or below; stop the recursion. 624eda14cbcSMatt Macy */ 625eda14cbcSMatt Macy err = zap_contains(mos, dsl_dir_phys(dd)->dd_props_zapobj, 626eda14cbcSMatt Macy propname); 627eda14cbcSMatt Macy if (err == 0) { 628eda14cbcSMatt Macy dsl_dir_rele(dd, FTAG); 629eda14cbcSMatt Macy return; 630eda14cbcSMatt Macy } 631eda14cbcSMatt Macy ASSERT3U(err, ==, ENOENT); 632eda14cbcSMatt Macy } 633eda14cbcSMatt Macy 634eda14cbcSMatt Macy mutex_enter(&dd->dd_lock); 635eda14cbcSMatt Macy pr = dsl_prop_record_find(dd, propname); 636eda14cbcSMatt Macy if (pr != NULL) { 637eda14cbcSMatt Macy for (cbr = list_head(&pr->pr_cbs); cbr; 638eda14cbcSMatt Macy cbr = list_next(&pr->pr_cbs, cbr)) { 639eda14cbcSMatt Macy uint64_t propobj; 640eda14cbcSMatt Macy 641eda14cbcSMatt Macy /* 642eda14cbcSMatt Macy * cbr->cbr_ds may be invalidated due to eviction, 643eda14cbcSMatt Macy * requiring the use of dsl_dataset_try_add_ref(). 644eda14cbcSMatt Macy * See comment block in dsl_prop_notify_all_cb() 645eda14cbcSMatt Macy * for details. 646eda14cbcSMatt Macy */ 647eda14cbcSMatt Macy if (!dsl_dataset_try_add_ref(dp, cbr->cbr_ds, FTAG)) 648eda14cbcSMatt Macy continue; 649eda14cbcSMatt Macy 650eda14cbcSMatt Macy propobj = dsl_dataset_phys(cbr->cbr_ds)->ds_props_obj; 651eda14cbcSMatt Macy 652eda14cbcSMatt Macy /* 653eda14cbcSMatt Macy * If the property is not set on this ds, then it is 654eda14cbcSMatt Macy * inherited here; call the callback. 655eda14cbcSMatt Macy */ 656eda14cbcSMatt Macy if (propobj == 0 || 657eda14cbcSMatt Macy zap_contains(mos, propobj, propname) != 0) 658eda14cbcSMatt Macy cbr->cbr_func(cbr->cbr_arg, value); 659eda14cbcSMatt Macy 660eda14cbcSMatt Macy dsl_dataset_rele(cbr->cbr_ds, FTAG); 661eda14cbcSMatt Macy } 662eda14cbcSMatt Macy } 663eda14cbcSMatt Macy mutex_exit(&dd->dd_lock); 664eda14cbcSMatt Macy 665*7a7741afSMartin Matuska za = zap_attribute_alloc(); 666eda14cbcSMatt Macy for (zap_cursor_init(&zc, mos, 667eda14cbcSMatt Macy dsl_dir_phys(dd)->dd_child_dir_zapobj); 668eda14cbcSMatt Macy zap_cursor_retrieve(&zc, za) == 0; 669eda14cbcSMatt Macy zap_cursor_advance(&zc)) { 670eda14cbcSMatt Macy dsl_prop_changed_notify(dp, za->za_first_integer, 671eda14cbcSMatt Macy propname, value, FALSE); 672eda14cbcSMatt Macy } 673*7a7741afSMartin Matuska zap_attribute_free(za); 674eda14cbcSMatt Macy zap_cursor_fini(&zc); 675eda14cbcSMatt Macy dsl_dir_rele(dd, FTAG); 676eda14cbcSMatt Macy } 677eda14cbcSMatt Macy 678dbd5678dSMartin Matuska 679dbd5678dSMartin Matuska /* 680dbd5678dSMartin Matuska * For newer values in zfs index type properties, we add a new key 681dbd5678dSMartin Matuska * propname$iuv (iuv = Ignore Unknown Values) to the properties zap object 682dbd5678dSMartin Matuska * to store the new property value and store the default value in the 683dbd5678dSMartin Matuska * existing prop key. So that the propname$iuv key is ignored by the older zfs 684dbd5678dSMartin Matuska * versions and the default property value from the existing prop key is 685dbd5678dSMartin Matuska * used. 686dbd5678dSMartin Matuska */ 687dbd5678dSMartin Matuska static void 688dbd5678dSMartin Matuska dsl_prop_set_iuv(objset_t *mos, uint64_t zapobj, const char *propname, 689dbd5678dSMartin Matuska int intsz, int numints, const void *value, dmu_tx_t *tx) 690dbd5678dSMartin Matuska { 691dbd5678dSMartin Matuska char *iuvstr = kmem_asprintf("%s%s", propname, ZPROP_IUV_SUFFIX); 692dbd5678dSMartin Matuska boolean_t iuv = B_FALSE; 693dbd5678dSMartin Matuska zfs_prop_t prop = zfs_name_to_prop(propname); 694dbd5678dSMartin Matuska 695dbd5678dSMartin Matuska switch (prop) { 696dbd5678dSMartin Matuska case ZFS_PROP_REDUNDANT_METADATA: 697dbd5678dSMartin Matuska if (*(uint64_t *)value == ZFS_REDUNDANT_METADATA_SOME || 698dbd5678dSMartin Matuska *(uint64_t *)value == ZFS_REDUNDANT_METADATA_NONE) 699dbd5678dSMartin Matuska iuv = B_TRUE; 700dbd5678dSMartin Matuska break; 701*7a7741afSMartin Matuska case ZFS_PROP_SNAPDIR: 702*7a7741afSMartin Matuska if (*(uint64_t *)value == ZFS_SNAPDIR_DISABLED) 703*7a7741afSMartin Matuska iuv = B_TRUE; 704*7a7741afSMartin Matuska break; 705dbd5678dSMartin Matuska default: 706dbd5678dSMartin Matuska break; 707dbd5678dSMartin Matuska } 708dbd5678dSMartin Matuska 709dbd5678dSMartin Matuska if (iuv) { 710dbd5678dSMartin Matuska VERIFY0(zap_update(mos, zapobj, iuvstr, intsz, numints, 711dbd5678dSMartin Matuska value, tx)); 712dbd5678dSMartin Matuska uint64_t val = zfs_prop_default_numeric(prop); 713dbd5678dSMartin Matuska VERIFY0(zap_update(mos, zapobj, propname, intsz, numints, 714dbd5678dSMartin Matuska &val, tx)); 715dbd5678dSMartin Matuska } else { 716dbd5678dSMartin Matuska zap_remove(mos, zapobj, iuvstr, tx); 717dbd5678dSMartin Matuska } 718dbd5678dSMartin Matuska kmem_strfree(iuvstr); 719dbd5678dSMartin Matuska } 720dbd5678dSMartin Matuska 721eda14cbcSMatt Macy void 722eda14cbcSMatt Macy dsl_prop_set_sync_impl(dsl_dataset_t *ds, const char *propname, 723eda14cbcSMatt Macy zprop_source_t source, int intsz, int numints, const void *value, 724eda14cbcSMatt Macy dmu_tx_t *tx) 725eda14cbcSMatt Macy { 726eda14cbcSMatt Macy objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; 727eda14cbcSMatt Macy uint64_t zapobj, intval, dummy, count; 728eda14cbcSMatt Macy int isint; 729eda14cbcSMatt Macy char valbuf[32]; 730eda14cbcSMatt Macy const char *valstr = NULL; 731eda14cbcSMatt Macy char *inheritstr; 732eda14cbcSMatt Macy char *recvdstr; 733dbd5678dSMartin Matuska char *iuvstr; 734eda14cbcSMatt Macy char *tbuf = NULL; 735eda14cbcSMatt Macy int err; 736eda14cbcSMatt Macy uint64_t version = spa_version(ds->ds_dir->dd_pool->dp_spa); 737eda14cbcSMatt Macy 738eda14cbcSMatt Macy isint = (dodefault(zfs_name_to_prop(propname), 8, 1, &intval) == 0); 739eda14cbcSMatt Macy 740eda14cbcSMatt Macy if (ds->ds_is_snapshot) { 741eda14cbcSMatt Macy ASSERT(version >= SPA_VERSION_SNAP_PROPS); 742eda14cbcSMatt Macy if (dsl_dataset_phys(ds)->ds_props_obj == 0 && 743eda14cbcSMatt Macy (source & ZPROP_SRC_NONE) == 0) { 744eda14cbcSMatt Macy dmu_buf_will_dirty(ds->ds_dbuf, tx); 745eda14cbcSMatt Macy dsl_dataset_phys(ds)->ds_props_obj = 746eda14cbcSMatt Macy zap_create(mos, 747eda14cbcSMatt Macy DMU_OT_DSL_PROPS, DMU_OT_NONE, 0, tx); 748eda14cbcSMatt Macy } 749eda14cbcSMatt Macy zapobj = dsl_dataset_phys(ds)->ds_props_obj; 750eda14cbcSMatt Macy } else { 751eda14cbcSMatt Macy zapobj = dsl_dir_phys(ds->ds_dir)->dd_props_zapobj; 752eda14cbcSMatt Macy } 753eda14cbcSMatt Macy 754eda14cbcSMatt Macy /* If we are removing objects from a non-existent ZAP just return */ 755eda14cbcSMatt Macy if (zapobj == 0) 756eda14cbcSMatt Macy return; 757eda14cbcSMatt Macy 758eda14cbcSMatt Macy if (version < SPA_VERSION_RECVD_PROPS) { 759eda14cbcSMatt Macy if (source & ZPROP_SRC_NONE) 760eda14cbcSMatt Macy source = ZPROP_SRC_NONE; 761eda14cbcSMatt Macy else if (source & ZPROP_SRC_RECEIVED) 762eda14cbcSMatt Macy source = ZPROP_SRC_LOCAL; 763eda14cbcSMatt Macy } 764eda14cbcSMatt Macy 765eda14cbcSMatt Macy inheritstr = kmem_asprintf("%s%s", propname, ZPROP_INHERIT_SUFFIX); 766eda14cbcSMatt Macy recvdstr = kmem_asprintf("%s%s", propname, ZPROP_RECVD_SUFFIX); 767dbd5678dSMartin Matuska iuvstr = kmem_asprintf("%s%s", propname, ZPROP_IUV_SUFFIX); 768eda14cbcSMatt Macy 769eda14cbcSMatt Macy switch ((int)source) { 770eda14cbcSMatt Macy case ZPROP_SRC_NONE: 771eda14cbcSMatt Macy /* 772eda14cbcSMatt Macy * revert to received value, if any (inherit -S) 773eda14cbcSMatt Macy * - remove propname 774eda14cbcSMatt Macy * - remove propname$inherit 775eda14cbcSMatt Macy */ 776eda14cbcSMatt Macy err = zap_remove(mos, zapobj, propname, tx); 777eda14cbcSMatt Macy ASSERT(err == 0 || err == ENOENT); 778eda14cbcSMatt Macy err = zap_remove(mos, zapobj, inheritstr, tx); 779eda14cbcSMatt Macy ASSERT(err == 0 || err == ENOENT); 780eda14cbcSMatt Macy break; 781eda14cbcSMatt Macy case ZPROP_SRC_LOCAL: 782eda14cbcSMatt Macy /* 783eda14cbcSMatt Macy * remove propname$inherit 784eda14cbcSMatt Macy * set propname -> value 785dbd5678dSMartin Matuska * set propname$iuv -> new property value 786eda14cbcSMatt Macy */ 787eda14cbcSMatt Macy err = zap_remove(mos, zapobj, inheritstr, tx); 788eda14cbcSMatt Macy ASSERT(err == 0 || err == ENOENT); 789eda14cbcSMatt Macy VERIFY0(zap_update(mos, zapobj, propname, 790eda14cbcSMatt Macy intsz, numints, value, tx)); 791dbd5678dSMartin Matuska (void) dsl_prop_set_iuv(mos, zapobj, propname, intsz, 792dbd5678dSMartin Matuska numints, value, tx); 793eda14cbcSMatt Macy break; 794eda14cbcSMatt Macy case ZPROP_SRC_INHERITED: 795eda14cbcSMatt Macy /* 796eda14cbcSMatt Macy * explicitly inherit 797eda14cbcSMatt Macy * - remove propname 798eda14cbcSMatt Macy * - set propname$inherit 799eda14cbcSMatt Macy */ 800eda14cbcSMatt Macy err = zap_remove(mos, zapobj, propname, tx); 801eda14cbcSMatt Macy ASSERT(err == 0 || err == ENOENT); 802dbd5678dSMartin Matuska err = zap_remove(mos, zapobj, iuvstr, tx); 803dbd5678dSMartin Matuska ASSERT(err == 0 || err == ENOENT); 804eda14cbcSMatt Macy if (version >= SPA_VERSION_RECVD_PROPS && 805eda14cbcSMatt Macy dsl_prop_get_int_ds(ds, ZPROP_HAS_RECVD, &dummy) == 0) { 806eda14cbcSMatt Macy dummy = 0; 807eda14cbcSMatt Macy VERIFY0(zap_update(mos, zapobj, inheritstr, 808eda14cbcSMatt Macy 8, 1, &dummy, tx)); 809eda14cbcSMatt Macy } 810eda14cbcSMatt Macy break; 811eda14cbcSMatt Macy case ZPROP_SRC_RECEIVED: 812eda14cbcSMatt Macy /* 813eda14cbcSMatt Macy * set propname$recvd -> value 814eda14cbcSMatt Macy */ 815eda14cbcSMatt Macy err = zap_update(mos, zapobj, recvdstr, 816eda14cbcSMatt Macy intsz, numints, value, tx); 817eda14cbcSMatt Macy ASSERT(err == 0); 818eda14cbcSMatt Macy break; 819eda14cbcSMatt Macy case (ZPROP_SRC_NONE | ZPROP_SRC_LOCAL | ZPROP_SRC_RECEIVED): 820eda14cbcSMatt Macy /* 821eda14cbcSMatt Macy * clear local and received settings 822eda14cbcSMatt Macy * - remove propname 823eda14cbcSMatt Macy * - remove propname$inherit 824eda14cbcSMatt Macy * - remove propname$recvd 825eda14cbcSMatt Macy */ 826eda14cbcSMatt Macy err = zap_remove(mos, zapobj, propname, tx); 827eda14cbcSMatt Macy ASSERT(err == 0 || err == ENOENT); 828eda14cbcSMatt Macy err = zap_remove(mos, zapobj, inheritstr, tx); 829eda14cbcSMatt Macy ASSERT(err == 0 || err == ENOENT); 830c03c5b1cSMartin Matuska zfs_fallthrough; 831eda14cbcSMatt Macy case (ZPROP_SRC_NONE | ZPROP_SRC_RECEIVED): 832eda14cbcSMatt Macy /* 833eda14cbcSMatt Macy * remove propname$recvd 834eda14cbcSMatt Macy */ 835eda14cbcSMatt Macy err = zap_remove(mos, zapobj, recvdstr, tx); 836eda14cbcSMatt Macy ASSERT(err == 0 || err == ENOENT); 837eda14cbcSMatt Macy break; 838eda14cbcSMatt Macy default: 839eda14cbcSMatt Macy cmn_err(CE_PANIC, "unexpected property source: %d", source); 840eda14cbcSMatt Macy } 841eda14cbcSMatt Macy 842eda14cbcSMatt Macy kmem_strfree(inheritstr); 843eda14cbcSMatt Macy kmem_strfree(recvdstr); 844dbd5678dSMartin Matuska kmem_strfree(iuvstr); 845eda14cbcSMatt Macy 846eda14cbcSMatt Macy /* 847eda14cbcSMatt Macy * If we are left with an empty snap zap we can destroy it. 848eda14cbcSMatt Macy * This will prevent unnecessary calls to zap_lookup() in 849eda14cbcSMatt Macy * the "zfs list" and "zfs get" code paths. 850eda14cbcSMatt Macy */ 851eda14cbcSMatt Macy if (ds->ds_is_snapshot && 852eda14cbcSMatt Macy zap_count(mos, zapobj, &count) == 0 && count == 0) { 853eda14cbcSMatt Macy dmu_buf_will_dirty(ds->ds_dbuf, tx); 854eda14cbcSMatt Macy dsl_dataset_phys(ds)->ds_props_obj = 0; 855eda14cbcSMatt Macy zap_destroy(mos, zapobj, tx); 856eda14cbcSMatt Macy } 857eda14cbcSMatt Macy 858eda14cbcSMatt Macy if (isint) { 859eda14cbcSMatt Macy VERIFY0(dsl_prop_get_int_ds(ds, propname, &intval)); 860eda14cbcSMatt Macy 861eda14cbcSMatt Macy if (ds->ds_is_snapshot) { 862eda14cbcSMatt Macy dsl_prop_cb_record_t *cbr; 863eda14cbcSMatt Macy /* 864eda14cbcSMatt Macy * It's a snapshot; nothing can inherit this 865eda14cbcSMatt Macy * property, so just look for callbacks on this 866eda14cbcSMatt Macy * ds here. 867eda14cbcSMatt Macy */ 868eda14cbcSMatt Macy mutex_enter(&ds->ds_dir->dd_lock); 869eda14cbcSMatt Macy for (cbr = list_head(&ds->ds_prop_cbs); cbr; 870eda14cbcSMatt Macy cbr = list_next(&ds->ds_prop_cbs, cbr)) { 871eda14cbcSMatt Macy if (strcmp(cbr->cbr_pr->pr_propname, 872eda14cbcSMatt Macy propname) == 0) 873eda14cbcSMatt Macy cbr->cbr_func(cbr->cbr_arg, intval); 874eda14cbcSMatt Macy } 875eda14cbcSMatt Macy mutex_exit(&ds->ds_dir->dd_lock); 876eda14cbcSMatt Macy } else { 877eda14cbcSMatt Macy dsl_prop_changed_notify(ds->ds_dir->dd_pool, 878eda14cbcSMatt Macy ds->ds_dir->dd_object, propname, intval, TRUE); 879eda14cbcSMatt Macy } 880eda14cbcSMatt Macy 881eda14cbcSMatt Macy (void) snprintf(valbuf, sizeof (valbuf), 882eda14cbcSMatt Macy "%lld", (longlong_t)intval); 883eda14cbcSMatt Macy valstr = valbuf; 884eda14cbcSMatt Macy } else { 885eda14cbcSMatt Macy if (source == ZPROP_SRC_LOCAL) { 886eda14cbcSMatt Macy valstr = value; 887eda14cbcSMatt Macy } else { 888eda14cbcSMatt Macy tbuf = kmem_alloc(ZAP_MAXVALUELEN, KM_SLEEP); 889eda14cbcSMatt Macy if (dsl_prop_get_ds(ds, propname, 1, 890eda14cbcSMatt Macy ZAP_MAXVALUELEN, tbuf, NULL) == 0) 891eda14cbcSMatt Macy valstr = tbuf; 892eda14cbcSMatt Macy } 893eda14cbcSMatt Macy } 894eda14cbcSMatt Macy 895eda14cbcSMatt Macy spa_history_log_internal_ds(ds, (source == ZPROP_SRC_NONE || 896eda14cbcSMatt Macy source == ZPROP_SRC_INHERITED) ? "inherit" : "set", tx, 897eda14cbcSMatt Macy "%s=%s", propname, (valstr == NULL ? "" : valstr)); 898eda14cbcSMatt Macy 899eda14cbcSMatt Macy if (tbuf != NULL) 900eda14cbcSMatt Macy kmem_free(tbuf, ZAP_MAXVALUELEN); 901eda14cbcSMatt Macy } 902eda14cbcSMatt Macy 903eda14cbcSMatt Macy int 904eda14cbcSMatt Macy dsl_prop_set_int(const char *dsname, const char *propname, 905eda14cbcSMatt Macy zprop_source_t source, uint64_t value) 906eda14cbcSMatt Macy { 907eda14cbcSMatt Macy nvlist_t *nvl = fnvlist_alloc(); 908eda14cbcSMatt Macy int error; 909eda14cbcSMatt Macy 910eda14cbcSMatt Macy fnvlist_add_uint64(nvl, propname, value); 911eda14cbcSMatt Macy error = dsl_props_set(dsname, source, nvl); 912eda14cbcSMatt Macy fnvlist_free(nvl); 913eda14cbcSMatt Macy return (error); 914eda14cbcSMatt Macy } 915eda14cbcSMatt Macy 916eda14cbcSMatt Macy int 917eda14cbcSMatt Macy dsl_prop_set_string(const char *dsname, const char *propname, 918eda14cbcSMatt Macy zprop_source_t source, const char *value) 919eda14cbcSMatt Macy { 920eda14cbcSMatt Macy nvlist_t *nvl = fnvlist_alloc(); 921eda14cbcSMatt Macy int error; 922eda14cbcSMatt Macy 923eda14cbcSMatt Macy fnvlist_add_string(nvl, propname, value); 924eda14cbcSMatt Macy error = dsl_props_set(dsname, source, nvl); 925eda14cbcSMatt Macy fnvlist_free(nvl); 926eda14cbcSMatt Macy return (error); 927eda14cbcSMatt Macy } 928eda14cbcSMatt Macy 929eda14cbcSMatt Macy int 930eda14cbcSMatt Macy dsl_prop_inherit(const char *dsname, const char *propname, 931eda14cbcSMatt Macy zprop_source_t source) 932eda14cbcSMatt Macy { 933eda14cbcSMatt Macy nvlist_t *nvl = fnvlist_alloc(); 934eda14cbcSMatt Macy int error; 935eda14cbcSMatt Macy 936eda14cbcSMatt Macy fnvlist_add_boolean(nvl, propname); 937eda14cbcSMatt Macy error = dsl_props_set(dsname, source, nvl); 938eda14cbcSMatt Macy fnvlist_free(nvl); 939eda14cbcSMatt Macy return (error); 940eda14cbcSMatt Macy } 941eda14cbcSMatt Macy 942eda14cbcSMatt Macy int 943eda14cbcSMatt Macy dsl_props_set_check(void *arg, dmu_tx_t *tx) 944eda14cbcSMatt Macy { 945eda14cbcSMatt Macy dsl_props_set_arg_t *dpsa = arg; 946eda14cbcSMatt Macy dsl_pool_t *dp = dmu_tx_pool(tx); 947eda14cbcSMatt Macy dsl_dataset_t *ds; 948eda14cbcSMatt Macy uint64_t version; 949eda14cbcSMatt Macy nvpair_t *elem = NULL; 950eda14cbcSMatt Macy int err; 951eda14cbcSMatt Macy 952eda14cbcSMatt Macy err = dsl_dataset_hold(dp, dpsa->dpsa_dsname, FTAG, &ds); 953eda14cbcSMatt Macy if (err != 0) 954eda14cbcSMatt Macy return (err); 955eda14cbcSMatt Macy 956eda14cbcSMatt Macy version = spa_version(ds->ds_dir->dd_pool->dp_spa); 957eda14cbcSMatt Macy while ((elem = nvlist_next_nvpair(dpsa->dpsa_props, elem)) != NULL) { 958eda14cbcSMatt Macy if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) { 959eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG); 960eda14cbcSMatt Macy return (SET_ERROR(ENAMETOOLONG)); 961eda14cbcSMatt Macy } 962eda14cbcSMatt Macy if (nvpair_type(elem) == DATA_TYPE_STRING) { 9632a58b312SMartin Matuska const char *valstr = fnvpair_value_string(elem); 964eda14cbcSMatt Macy if (strlen(valstr) >= (version < 965eda14cbcSMatt Macy SPA_VERSION_STMF_PROP ? 966eda14cbcSMatt Macy ZAP_OLDMAXVALUELEN : ZAP_MAXVALUELEN)) { 967eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG); 968eda14cbcSMatt Macy return (SET_ERROR(E2BIG)); 969eda14cbcSMatt Macy } 970eda14cbcSMatt Macy } 971eda14cbcSMatt Macy } 972eda14cbcSMatt Macy 973eda14cbcSMatt Macy if (ds->ds_is_snapshot && version < SPA_VERSION_SNAP_PROPS) { 974eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG); 975eda14cbcSMatt Macy return (SET_ERROR(ENOTSUP)); 976eda14cbcSMatt Macy } 977eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG); 978eda14cbcSMatt Macy return (0); 979eda14cbcSMatt Macy } 980eda14cbcSMatt Macy 981eda14cbcSMatt Macy void 982eda14cbcSMatt Macy dsl_props_set_sync_impl(dsl_dataset_t *ds, zprop_source_t source, 983eda14cbcSMatt Macy nvlist_t *props, dmu_tx_t *tx) 984eda14cbcSMatt Macy { 985eda14cbcSMatt Macy nvpair_t *elem = NULL; 986eda14cbcSMatt Macy 987eda14cbcSMatt Macy while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { 988eda14cbcSMatt Macy nvpair_t *pair = elem; 989eda14cbcSMatt Macy const char *name = nvpair_name(pair); 990eda14cbcSMatt Macy 991eda14cbcSMatt Macy if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 992eda14cbcSMatt Macy /* 993eda14cbcSMatt Macy * This usually happens when we reuse the nvlist_t data 994eda14cbcSMatt Macy * returned by the counterpart dsl_prop_get_all_impl(). 995eda14cbcSMatt Macy * For instance we do this to restore the original 996eda14cbcSMatt Macy * received properties when an error occurs in the 997eda14cbcSMatt Macy * zfs_ioc_recv() codepath. 998eda14cbcSMatt Macy */ 999eda14cbcSMatt Macy nvlist_t *attrs = fnvpair_value_nvlist(pair); 1000eda14cbcSMatt Macy pair = fnvlist_lookup_nvpair(attrs, ZPROP_VALUE); 1001eda14cbcSMatt Macy } 1002eda14cbcSMatt Macy 1003eda14cbcSMatt Macy if (nvpair_type(pair) == DATA_TYPE_STRING) { 1004eda14cbcSMatt Macy const char *value = fnvpair_value_string(pair); 1005eda14cbcSMatt Macy dsl_prop_set_sync_impl(ds, name, 1006eda14cbcSMatt Macy source, 1, strlen(value) + 1, value, tx); 1007eda14cbcSMatt Macy } else if (nvpair_type(pair) == DATA_TYPE_UINT64) { 1008eda14cbcSMatt Macy uint64_t intval = fnvpair_value_uint64(pair); 1009eda14cbcSMatt Macy dsl_prop_set_sync_impl(ds, name, 1010eda14cbcSMatt Macy source, sizeof (intval), 1, &intval, tx); 1011eda14cbcSMatt Macy } else if (nvpair_type(pair) == DATA_TYPE_BOOLEAN) { 1012eda14cbcSMatt Macy dsl_prop_set_sync_impl(ds, name, 1013eda14cbcSMatt Macy source, 0, 0, NULL, tx); 1014eda14cbcSMatt Macy } else { 1015eda14cbcSMatt Macy panic("invalid nvpair type"); 1016eda14cbcSMatt Macy } 1017eda14cbcSMatt Macy } 1018eda14cbcSMatt Macy } 1019eda14cbcSMatt Macy 1020eda14cbcSMatt Macy void 1021eda14cbcSMatt Macy dsl_props_set_sync(void *arg, dmu_tx_t *tx) 1022eda14cbcSMatt Macy { 1023eda14cbcSMatt Macy dsl_props_set_arg_t *dpsa = arg; 1024eda14cbcSMatt Macy dsl_pool_t *dp = dmu_tx_pool(tx); 1025eda14cbcSMatt Macy dsl_dataset_t *ds; 1026eda14cbcSMatt Macy 1027eda14cbcSMatt Macy VERIFY0(dsl_dataset_hold(dp, dpsa->dpsa_dsname, FTAG, &ds)); 1028eda14cbcSMatt Macy dsl_props_set_sync_impl(ds, dpsa->dpsa_source, dpsa->dpsa_props, tx); 1029eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG); 1030eda14cbcSMatt Macy } 1031eda14cbcSMatt Macy 1032eda14cbcSMatt Macy /* 1033eda14cbcSMatt Macy * All-or-nothing; if any prop can't be set, nothing will be modified. 1034eda14cbcSMatt Macy */ 1035eda14cbcSMatt Macy int 1036eda14cbcSMatt Macy dsl_props_set(const char *dsname, zprop_source_t source, nvlist_t *props) 1037eda14cbcSMatt Macy { 1038eda14cbcSMatt Macy dsl_props_set_arg_t dpsa; 1039eda14cbcSMatt Macy int nblks = 0; 1040eda14cbcSMatt Macy 1041eda14cbcSMatt Macy dpsa.dpsa_dsname = dsname; 1042eda14cbcSMatt Macy dpsa.dpsa_source = source; 1043eda14cbcSMatt Macy dpsa.dpsa_props = props; 1044eda14cbcSMatt Macy 1045eda14cbcSMatt Macy /* 1046eda14cbcSMatt Macy * If the source includes NONE, then we will only be removing entries 1047eda14cbcSMatt Macy * from the ZAP object. In that case don't check for ENOSPC. 1048eda14cbcSMatt Macy */ 1049eda14cbcSMatt Macy if ((source & ZPROP_SRC_NONE) == 0) 1050eda14cbcSMatt Macy nblks = 2 * fnvlist_num_pairs(props); 1051eda14cbcSMatt Macy 1052eda14cbcSMatt Macy return (dsl_sync_task(dsname, dsl_props_set_check, dsl_props_set_sync, 1053eda14cbcSMatt Macy &dpsa, nblks, ZFS_SPACE_CHECK_RESERVED)); 1054eda14cbcSMatt Macy } 1055eda14cbcSMatt Macy 1056eda14cbcSMatt Macy typedef enum dsl_prop_getflags { 1057eda14cbcSMatt Macy DSL_PROP_GET_INHERITING = 0x1, /* searching parent of target ds */ 1058eda14cbcSMatt Macy DSL_PROP_GET_SNAPSHOT = 0x2, /* snapshot dataset */ 1059eda14cbcSMatt Macy DSL_PROP_GET_LOCAL = 0x4, /* local properties */ 1060eda14cbcSMatt Macy DSL_PROP_GET_RECEIVED = 0x8, /* received properties */ 1061eda14cbcSMatt Macy } dsl_prop_getflags_t; 1062eda14cbcSMatt Macy 1063eda14cbcSMatt Macy static int 1064eda14cbcSMatt Macy dsl_prop_get_all_impl(objset_t *mos, uint64_t propobj, 1065eda14cbcSMatt Macy const char *setpoint, dsl_prop_getflags_t flags, nvlist_t *nv) 1066eda14cbcSMatt Macy { 1067eda14cbcSMatt Macy zap_cursor_t zc; 1068*7a7741afSMartin Matuska zap_attribute_t *za = zap_attribute_alloc(); 1069eda14cbcSMatt Macy int err = 0; 1070eda14cbcSMatt Macy 1071eda14cbcSMatt Macy for (zap_cursor_init(&zc, mos, propobj); 1072*7a7741afSMartin Matuska (err = zap_cursor_retrieve(&zc, za)) == 0; 1073eda14cbcSMatt Macy zap_cursor_advance(&zc)) { 1074eda14cbcSMatt Macy nvlist_t *propval; 1075eda14cbcSMatt Macy zfs_prop_t prop; 1076eda14cbcSMatt Macy char buf[ZAP_MAXNAMELEN]; 1077eda14cbcSMatt Macy char *valstr; 1078eda14cbcSMatt Macy const char *suffix; 1079eda14cbcSMatt Macy const char *propname; 1080eda14cbcSMatt Macy const char *source; 1081eda14cbcSMatt Macy 1082*7a7741afSMartin Matuska suffix = strchr(za->za_name, '$'); 1083eda14cbcSMatt Macy 1084eda14cbcSMatt Macy if (suffix == NULL) { 1085eda14cbcSMatt Macy /* 1086eda14cbcSMatt Macy * Skip local properties if we only want received 1087eda14cbcSMatt Macy * properties. 1088eda14cbcSMatt Macy */ 1089eda14cbcSMatt Macy if (flags & DSL_PROP_GET_RECEIVED) 1090eda14cbcSMatt Macy continue; 1091eda14cbcSMatt Macy 1092*7a7741afSMartin Matuska propname = za->za_name; 1093eda14cbcSMatt Macy source = setpoint; 1094dbd5678dSMartin Matuska 1095dbd5678dSMartin Matuska /* Skip if iuv entries are preset. */ 1096dbd5678dSMartin Matuska valstr = kmem_asprintf("%s%s", propname, 1097dbd5678dSMartin Matuska ZPROP_IUV_SUFFIX); 1098dbd5678dSMartin Matuska err = zap_contains(mos, propobj, valstr); 1099dbd5678dSMartin Matuska kmem_strfree(valstr); 1100dbd5678dSMartin Matuska if (err == 0) 1101dbd5678dSMartin Matuska continue; 1102eda14cbcSMatt Macy } else if (strcmp(suffix, ZPROP_INHERIT_SUFFIX) == 0) { 1103eda14cbcSMatt Macy /* Skip explicitly inherited entries. */ 1104eda14cbcSMatt Macy continue; 1105eda14cbcSMatt Macy } else if (strcmp(suffix, ZPROP_RECVD_SUFFIX) == 0) { 1106eda14cbcSMatt Macy if (flags & DSL_PROP_GET_LOCAL) 1107eda14cbcSMatt Macy continue; 1108eda14cbcSMatt Macy 1109*7a7741afSMartin Matuska (void) strlcpy(buf, za->za_name, 1110*7a7741afSMartin Matuska MIN(sizeof (buf), suffix - za->za_name + 1)); 1111eda14cbcSMatt Macy propname = buf; 1112eda14cbcSMatt Macy 1113eda14cbcSMatt Macy if (!(flags & DSL_PROP_GET_RECEIVED)) { 1114eda14cbcSMatt Macy /* Skip if locally overridden. */ 1115eda14cbcSMatt Macy err = zap_contains(mos, propobj, propname); 1116eda14cbcSMatt Macy if (err == 0) 1117eda14cbcSMatt Macy continue; 1118eda14cbcSMatt Macy if (err != ENOENT) 1119eda14cbcSMatt Macy break; 1120eda14cbcSMatt Macy 1121eda14cbcSMatt Macy /* Skip if explicitly inherited. */ 1122eda14cbcSMatt Macy valstr = kmem_asprintf("%s%s", propname, 1123eda14cbcSMatt Macy ZPROP_INHERIT_SUFFIX); 1124eda14cbcSMatt Macy err = zap_contains(mos, propobj, valstr); 1125eda14cbcSMatt Macy kmem_strfree(valstr); 1126eda14cbcSMatt Macy if (err == 0) 1127eda14cbcSMatt Macy continue; 1128eda14cbcSMatt Macy if (err != ENOENT) 1129eda14cbcSMatt Macy break; 1130eda14cbcSMatt Macy } 1131eda14cbcSMatt Macy 1132eda14cbcSMatt Macy source = ((flags & DSL_PROP_GET_INHERITING) ? 1133eda14cbcSMatt Macy setpoint : ZPROP_SOURCE_VAL_RECVD); 1134dbd5678dSMartin Matuska } else if (strcmp(suffix, ZPROP_IUV_SUFFIX) == 0) { 1135*7a7741afSMartin Matuska (void) strlcpy(buf, za->za_name, 1136*7a7741afSMartin Matuska MIN(sizeof (buf), suffix - za->za_name + 1)); 1137dbd5678dSMartin Matuska propname = buf; 1138dbd5678dSMartin Matuska source = setpoint; 1139dbd5678dSMartin Matuska prop = zfs_name_to_prop(propname); 1140dbd5678dSMartin Matuska 1141dbd5678dSMartin Matuska if (dsl_prop_known_index(prop, 1142*7a7741afSMartin Matuska za->za_first_integer) != 1) 1143dbd5678dSMartin Matuska continue; 1144eda14cbcSMatt Macy } else { 1145eda14cbcSMatt Macy /* 1146eda14cbcSMatt Macy * For backward compatibility, skip suffixes we don't 1147eda14cbcSMatt Macy * recognize. 1148eda14cbcSMatt Macy */ 1149eda14cbcSMatt Macy continue; 1150eda14cbcSMatt Macy } 1151eda14cbcSMatt Macy 1152eda14cbcSMatt Macy prop = zfs_name_to_prop(propname); 1153eda14cbcSMatt Macy 1154eda14cbcSMatt Macy /* Skip non-inheritable properties. */ 11551f1e2261SMartin Matuska if ((flags & DSL_PROP_GET_INHERITING) && 11561f1e2261SMartin Matuska prop != ZPROP_USERPROP && !zfs_prop_inheritable(prop)) 1157eda14cbcSMatt Macy continue; 1158eda14cbcSMatt Macy 1159eda14cbcSMatt Macy /* Skip properties not valid for this type. */ 11601f1e2261SMartin Matuska if ((flags & DSL_PROP_GET_SNAPSHOT) && prop != ZPROP_USERPROP && 1161eda14cbcSMatt Macy !zfs_prop_valid_for_type(prop, ZFS_TYPE_SNAPSHOT, B_FALSE)) 1162eda14cbcSMatt Macy continue; 1163eda14cbcSMatt Macy 1164eda14cbcSMatt Macy /* Skip properties already defined. */ 1165eda14cbcSMatt Macy if (nvlist_exists(nv, propname)) 1166eda14cbcSMatt Macy continue; 1167eda14cbcSMatt Macy 1168eda14cbcSMatt Macy VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0); 1169*7a7741afSMartin Matuska if (za->za_integer_length == 1) { 1170eda14cbcSMatt Macy /* 1171eda14cbcSMatt Macy * String property 1172eda14cbcSMatt Macy */ 1173*7a7741afSMartin Matuska char *tmp = kmem_alloc(za->za_num_integers, 1174eda14cbcSMatt Macy KM_SLEEP); 1175eda14cbcSMatt Macy err = zap_lookup(mos, propobj, 1176*7a7741afSMartin Matuska za->za_name, 1, za->za_num_integers, tmp); 1177eda14cbcSMatt Macy if (err != 0) { 1178*7a7741afSMartin Matuska kmem_free(tmp, za->za_num_integers); 1179eda14cbcSMatt Macy break; 1180eda14cbcSMatt Macy } 1181eda14cbcSMatt Macy VERIFY(nvlist_add_string(propval, ZPROP_VALUE, 1182eda14cbcSMatt Macy tmp) == 0); 1183*7a7741afSMartin Matuska kmem_free(tmp, za->za_num_integers); 1184eda14cbcSMatt Macy } else { 1185eda14cbcSMatt Macy /* 1186eda14cbcSMatt Macy * Integer property 1187eda14cbcSMatt Macy */ 1188*7a7741afSMartin Matuska ASSERT(za->za_integer_length == 8); 1189eda14cbcSMatt Macy (void) nvlist_add_uint64(propval, ZPROP_VALUE, 1190*7a7741afSMartin Matuska za->za_first_integer); 1191eda14cbcSMatt Macy } 1192eda14cbcSMatt Macy 1193eda14cbcSMatt Macy VERIFY(nvlist_add_string(propval, ZPROP_SOURCE, source) == 0); 1194eda14cbcSMatt Macy VERIFY(nvlist_add_nvlist(nv, propname, propval) == 0); 1195eda14cbcSMatt Macy nvlist_free(propval); 1196eda14cbcSMatt Macy } 1197eda14cbcSMatt Macy zap_cursor_fini(&zc); 1198*7a7741afSMartin Matuska zap_attribute_free(za); 1199eda14cbcSMatt Macy if (err == ENOENT) 1200eda14cbcSMatt Macy err = 0; 1201eda14cbcSMatt Macy return (err); 1202eda14cbcSMatt Macy } 1203eda14cbcSMatt Macy 1204eda14cbcSMatt Macy /* 1205eda14cbcSMatt Macy * Iterate over all properties for this dataset and return them in an nvlist. 1206eda14cbcSMatt Macy */ 1207eda14cbcSMatt Macy static int 1208eda14cbcSMatt Macy dsl_prop_get_all_ds(dsl_dataset_t *ds, nvlist_t **nvp, 1209eda14cbcSMatt Macy dsl_prop_getflags_t flags) 1210eda14cbcSMatt Macy { 1211eda14cbcSMatt Macy dsl_dir_t *dd = ds->ds_dir; 1212eda14cbcSMatt Macy dsl_pool_t *dp = dd->dd_pool; 1213eda14cbcSMatt Macy objset_t *mos = dp->dp_meta_objset; 1214eda14cbcSMatt Macy int err = 0; 1215eda14cbcSMatt Macy char setpoint[ZFS_MAX_DATASET_NAME_LEN]; 1216eda14cbcSMatt Macy 1217eda14cbcSMatt Macy VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 1218eda14cbcSMatt Macy 1219eda14cbcSMatt Macy if (ds->ds_is_snapshot) 1220eda14cbcSMatt Macy flags |= DSL_PROP_GET_SNAPSHOT; 1221eda14cbcSMatt Macy 1222eda14cbcSMatt Macy ASSERT(dsl_pool_config_held(dp)); 1223eda14cbcSMatt Macy 1224eda14cbcSMatt Macy if (dsl_dataset_phys(ds)->ds_props_obj != 0) { 1225eda14cbcSMatt Macy ASSERT(flags & DSL_PROP_GET_SNAPSHOT); 1226eda14cbcSMatt Macy dsl_dataset_name(ds, setpoint); 1227eda14cbcSMatt Macy err = dsl_prop_get_all_impl(mos, 1228eda14cbcSMatt Macy dsl_dataset_phys(ds)->ds_props_obj, setpoint, flags, *nvp); 1229eda14cbcSMatt Macy if (err) 1230eda14cbcSMatt Macy goto out; 1231eda14cbcSMatt Macy } 1232eda14cbcSMatt Macy 1233eda14cbcSMatt Macy for (; dd != NULL; dd = dd->dd_parent) { 1234eda14cbcSMatt Macy if (dd != ds->ds_dir || (flags & DSL_PROP_GET_SNAPSHOT)) { 1235eda14cbcSMatt Macy if (flags & (DSL_PROP_GET_LOCAL | 1236eda14cbcSMatt Macy DSL_PROP_GET_RECEIVED)) 1237eda14cbcSMatt Macy break; 1238eda14cbcSMatt Macy flags |= DSL_PROP_GET_INHERITING; 1239eda14cbcSMatt Macy } 1240eda14cbcSMatt Macy dsl_dir_name(dd, setpoint); 1241eda14cbcSMatt Macy err = dsl_prop_get_all_impl(mos, 1242eda14cbcSMatt Macy dsl_dir_phys(dd)->dd_props_zapobj, setpoint, flags, *nvp); 1243eda14cbcSMatt Macy if (err) 1244eda14cbcSMatt Macy break; 1245eda14cbcSMatt Macy } 1246eda14cbcSMatt Macy 1247eda14cbcSMatt Macy out: 1248eda14cbcSMatt Macy if (err) { 1249eda14cbcSMatt Macy nvlist_free(*nvp); 1250eda14cbcSMatt Macy *nvp = NULL; 1251eda14cbcSMatt Macy } 1252eda14cbcSMatt Macy return (err); 1253eda14cbcSMatt Macy } 1254eda14cbcSMatt Macy 1255eda14cbcSMatt Macy boolean_t 1256eda14cbcSMatt Macy dsl_prop_get_hasrecvd(const char *dsname) 1257eda14cbcSMatt Macy { 1258eda14cbcSMatt Macy uint64_t dummy; 1259eda14cbcSMatt Macy 1260eda14cbcSMatt Macy return (0 == 1261eda14cbcSMatt Macy dsl_prop_get_integer(dsname, ZPROP_HAS_RECVD, &dummy, NULL)); 1262eda14cbcSMatt Macy } 1263eda14cbcSMatt Macy 1264eda14cbcSMatt Macy static int 1265eda14cbcSMatt Macy dsl_prop_set_hasrecvd_impl(const char *dsname, zprop_source_t source) 1266eda14cbcSMatt Macy { 1267eda14cbcSMatt Macy uint64_t version; 1268eda14cbcSMatt Macy spa_t *spa; 1269eda14cbcSMatt Macy int error = 0; 1270eda14cbcSMatt Macy 1271eda14cbcSMatt Macy VERIFY0(spa_open(dsname, &spa, FTAG)); 1272eda14cbcSMatt Macy version = spa_version(spa); 1273eda14cbcSMatt Macy spa_close(spa, FTAG); 1274eda14cbcSMatt Macy 1275eda14cbcSMatt Macy if (version >= SPA_VERSION_RECVD_PROPS) 1276eda14cbcSMatt Macy error = dsl_prop_set_int(dsname, ZPROP_HAS_RECVD, source, 0); 1277eda14cbcSMatt Macy return (error); 1278eda14cbcSMatt Macy } 1279eda14cbcSMatt Macy 1280eda14cbcSMatt Macy /* 1281eda14cbcSMatt Macy * Call after successfully receiving properties to ensure that only the first 1282eda14cbcSMatt Macy * receive on or after SPA_VERSION_RECVD_PROPS blows away local properties. 1283eda14cbcSMatt Macy */ 1284eda14cbcSMatt Macy int 1285eda14cbcSMatt Macy dsl_prop_set_hasrecvd(const char *dsname) 1286eda14cbcSMatt Macy { 1287eda14cbcSMatt Macy int error = 0; 1288eda14cbcSMatt Macy if (!dsl_prop_get_hasrecvd(dsname)) 1289eda14cbcSMatt Macy error = dsl_prop_set_hasrecvd_impl(dsname, ZPROP_SRC_LOCAL); 1290eda14cbcSMatt Macy return (error); 1291eda14cbcSMatt Macy } 1292eda14cbcSMatt Macy 1293eda14cbcSMatt Macy void 1294eda14cbcSMatt Macy dsl_prop_unset_hasrecvd(const char *dsname) 1295eda14cbcSMatt Macy { 1296eda14cbcSMatt Macy VERIFY0(dsl_prop_set_hasrecvd_impl(dsname, ZPROP_SRC_NONE)); 1297eda14cbcSMatt Macy } 1298eda14cbcSMatt Macy 1299eda14cbcSMatt Macy int 1300eda14cbcSMatt Macy dsl_prop_get_all(objset_t *os, nvlist_t **nvp) 1301eda14cbcSMatt Macy { 1302eda14cbcSMatt Macy return (dsl_prop_get_all_ds(os->os_dsl_dataset, nvp, 0)); 1303eda14cbcSMatt Macy } 1304eda14cbcSMatt Macy 1305eda14cbcSMatt Macy int 1306eda14cbcSMatt Macy dsl_prop_get_received(const char *dsname, nvlist_t **nvp) 1307eda14cbcSMatt Macy { 1308eda14cbcSMatt Macy objset_t *os; 1309eda14cbcSMatt Macy int error; 1310eda14cbcSMatt Macy 1311eda14cbcSMatt Macy /* 1312eda14cbcSMatt Macy * Received properties are not distinguishable from local properties 1313eda14cbcSMatt Macy * until the dataset has received properties on or after 1314eda14cbcSMatt Macy * SPA_VERSION_RECVD_PROPS. 1315eda14cbcSMatt Macy */ 1316eda14cbcSMatt Macy dsl_prop_getflags_t flags = (dsl_prop_get_hasrecvd(dsname) ? 1317eda14cbcSMatt Macy DSL_PROP_GET_RECEIVED : DSL_PROP_GET_LOCAL); 1318eda14cbcSMatt Macy 1319eda14cbcSMatt Macy error = dmu_objset_hold(dsname, FTAG, &os); 1320eda14cbcSMatt Macy if (error != 0) 1321eda14cbcSMatt Macy return (error); 1322eda14cbcSMatt Macy error = dsl_prop_get_all_ds(os->os_dsl_dataset, nvp, flags); 1323eda14cbcSMatt Macy dmu_objset_rele(os, FTAG); 1324eda14cbcSMatt Macy return (error); 1325eda14cbcSMatt Macy } 1326eda14cbcSMatt Macy 1327eda14cbcSMatt Macy void 1328eda14cbcSMatt Macy dsl_prop_nvlist_add_uint64(nvlist_t *nv, zfs_prop_t prop, uint64_t value) 1329eda14cbcSMatt Macy { 1330eda14cbcSMatt Macy nvlist_t *propval; 1331eda14cbcSMatt Macy const char *propname = zfs_prop_to_name(prop); 1332eda14cbcSMatt Macy uint64_t default_value; 1333eda14cbcSMatt Macy 1334eda14cbcSMatt Macy if (nvlist_lookup_nvlist(nv, propname, &propval) == 0) { 1335eda14cbcSMatt Macy VERIFY(nvlist_add_uint64(propval, ZPROP_VALUE, value) == 0); 1336eda14cbcSMatt Macy return; 1337eda14cbcSMatt Macy } 1338eda14cbcSMatt Macy 1339eda14cbcSMatt Macy VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0); 1340eda14cbcSMatt Macy VERIFY(nvlist_add_uint64(propval, ZPROP_VALUE, value) == 0); 1341eda14cbcSMatt Macy /* Indicate the default source if we can. */ 1342eda14cbcSMatt Macy if (dodefault(prop, 8, 1, &default_value) == 0 && 1343eda14cbcSMatt Macy value == default_value) { 1344eda14cbcSMatt Macy VERIFY(nvlist_add_string(propval, ZPROP_SOURCE, "") == 0); 1345eda14cbcSMatt Macy } 1346eda14cbcSMatt Macy VERIFY(nvlist_add_nvlist(nv, propname, propval) == 0); 1347eda14cbcSMatt Macy nvlist_free(propval); 1348eda14cbcSMatt Macy } 1349eda14cbcSMatt Macy 1350eda14cbcSMatt Macy void 1351eda14cbcSMatt Macy dsl_prop_nvlist_add_string(nvlist_t *nv, zfs_prop_t prop, const char *value) 1352eda14cbcSMatt Macy { 1353eda14cbcSMatt Macy nvlist_t *propval; 1354eda14cbcSMatt Macy const char *propname = zfs_prop_to_name(prop); 1355eda14cbcSMatt Macy 1356eda14cbcSMatt Macy if (nvlist_lookup_nvlist(nv, propname, &propval) == 0) { 1357eda14cbcSMatt Macy VERIFY(nvlist_add_string(propval, ZPROP_VALUE, value) == 0); 1358eda14cbcSMatt Macy return; 1359eda14cbcSMatt Macy } 1360eda14cbcSMatt Macy 1361eda14cbcSMatt Macy VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0); 1362eda14cbcSMatt Macy VERIFY(nvlist_add_string(propval, ZPROP_VALUE, value) == 0); 1363eda14cbcSMatt Macy VERIFY(nvlist_add_nvlist(nv, propname, propval) == 0); 1364eda14cbcSMatt Macy nvlist_free(propval); 1365eda14cbcSMatt Macy } 1366eda14cbcSMatt Macy 1367eda14cbcSMatt Macy #if defined(_KERNEL) 1368eda14cbcSMatt Macy EXPORT_SYMBOL(dsl_prop_register); 1369eda14cbcSMatt Macy EXPORT_SYMBOL(dsl_prop_unregister); 1370eda14cbcSMatt Macy EXPORT_SYMBOL(dsl_prop_unregister_all); 1371eda14cbcSMatt Macy EXPORT_SYMBOL(dsl_prop_get); 1372eda14cbcSMatt Macy EXPORT_SYMBOL(dsl_prop_get_integer); 1373eda14cbcSMatt Macy EXPORT_SYMBOL(dsl_prop_get_all); 1374eda14cbcSMatt Macy EXPORT_SYMBOL(dsl_prop_get_received); 1375eda14cbcSMatt Macy EXPORT_SYMBOL(dsl_prop_get_ds); 1376eda14cbcSMatt Macy EXPORT_SYMBOL(dsl_prop_get_int_ds); 1377eda14cbcSMatt Macy EXPORT_SYMBOL(dsl_prop_get_dd); 1378eda14cbcSMatt Macy EXPORT_SYMBOL(dsl_props_set); 1379eda14cbcSMatt Macy EXPORT_SYMBOL(dsl_prop_set_int); 1380eda14cbcSMatt Macy EXPORT_SYMBOL(dsl_prop_set_string); 1381eda14cbcSMatt Macy EXPORT_SYMBOL(dsl_prop_inherit); 1382eda14cbcSMatt Macy EXPORT_SYMBOL(dsl_prop_predict); 1383eda14cbcSMatt Macy EXPORT_SYMBOL(dsl_prop_nvlist_add_uint64); 1384eda14cbcSMatt Macy EXPORT_SYMBOL(dsl_prop_nvlist_add_string); 1385eda14cbcSMatt Macy #endif 1386