1168404Spjd/* 2168404Spjd * CDDL HEADER START 3168404Spjd * 4168404Spjd * The contents of this file are subject to the terms of the 5168404Spjd * Common Development and Distribution License (the "License"). 6168404Spjd * You may not use this file except in compliance with the License. 7168404Spjd * 8168404Spjd * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9168404Spjd * or http://www.opensolaris.org/os/licensing. 10168404Spjd * See the License for the specific language governing permissions 11168404Spjd * and limitations under the License. 12168404Spjd * 13168404Spjd * When distributing Covered Code, include this CDDL HEADER in each 14168404Spjd * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15168404Spjd * If applicable, add the following below this CDDL HEADER, with the 16168404Spjd * fields enclosed by brackets "[]" replaced with your own identifying 17168404Spjd * information: Portions Copyright [yyyy] [name of copyright owner] 18168404Spjd * 19168404Spjd * CDDL HEADER END 20168404Spjd */ 21168404Spjd/* 22219089Spjd * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 23307108Smav * Copyright (c) 2012, 2015 by Delphix. All rights reserved. 24297507Smav * Copyright 2015, Joyent, Inc. 25168404Spjd */ 26168404Spjd 27219089Spjd#include <sys/zfs_context.h> 28168404Spjd#include <sys/dmu.h> 29168404Spjd#include <sys/dmu_objset.h> 30168404Spjd#include <sys/dmu_tx.h> 31168404Spjd#include <sys/dsl_dataset.h> 32168404Spjd#include <sys/dsl_dir.h> 33168404Spjd#include <sys/dsl_prop.h> 34168404Spjd#include <sys/dsl_synctask.h> 35168404Spjd#include <sys/spa.h> 36168404Spjd#include <sys/zap.h> 37168404Spjd#include <sys/fs/zfs.h> 38168404Spjd 39168404Spjd#include "zfs_prop.h" 40168404Spjd 41219089Spjd#define ZPROP_INHERIT_SUFFIX "$inherit" 42219089Spjd#define ZPROP_RECVD_SUFFIX "$recvd" 43219089Spjd 44168404Spjdstatic int 45297507Smavdodefault(zfs_prop_t prop, int intsz, int numints, void *buf) 46168404Spjd{ 47185029Spjd /* 48185029Spjd * The setonce properties are read-only, BUT they still 49185029Spjd * have a default value that can be used as the initial 50185029Spjd * value. 51185029Spjd */ 52297507Smav if (prop == ZPROP_INVAL || 53185029Spjd (zfs_prop_readonly(prop) && !zfs_prop_setonce(prop))) 54249195Smm return (SET_ERROR(ENOENT)); 55168404Spjd 56185029Spjd if (zfs_prop_get_type(prop) == PROP_TYPE_STRING) { 57305799Smav if (zfs_prop_default_string(prop) == NULL) 58305799Smav return (SET_ERROR(ENOENT)); 59168404Spjd if (intsz != 1) 60249195Smm return (SET_ERROR(EOVERFLOW)); 61185029Spjd (void) strncpy(buf, zfs_prop_default_string(prop), 62219089Spjd numints); 63168404Spjd } else { 64219089Spjd if (intsz != 8 || numints < 1) 65249195Smm return (SET_ERROR(EOVERFLOW)); 66168404Spjd 67168404Spjd *(uint64_t *)buf = zfs_prop_default_numeric(prop); 68168404Spjd } 69168404Spjd 70168404Spjd return (0); 71168404Spjd} 72168404Spjd 73185029Spjdint 74185029Spjddsl_prop_get_dd(dsl_dir_t *dd, const char *propname, 75219089Spjd int intsz, int numints, void *buf, char *setpoint, boolean_t snapshot) 76168404Spjd{ 77168404Spjd int err = ENOENT; 78219089Spjd dsl_dir_t *target = dd; 79185029Spjd objset_t *mos = dd->dd_pool->dp_meta_objset; 80168404Spjd zfs_prop_t prop; 81219089Spjd boolean_t inheritable; 82219089Spjd boolean_t inheriting = B_FALSE; 83219089Spjd char *inheritstr; 84219089Spjd char *recvdstr; 85168404Spjd 86248571Smm ASSERT(dsl_pool_config_held(dd->dd_pool)); 87185029Spjd 88168404Spjd if (setpoint) 89168404Spjd setpoint[0] = '\0'; 90168404Spjd 91168404Spjd prop = zfs_name_to_prop(propname); 92219089Spjd inheritable = (prop == ZPROP_INVAL || zfs_prop_inheritable(prop)); 93219089Spjd inheritstr = kmem_asprintf("%s%s", propname, ZPROP_INHERIT_SUFFIX); 94219089Spjd recvdstr = kmem_asprintf("%s%s", propname, ZPROP_RECVD_SUFFIX); 95168404Spjd 96168404Spjd /* 97219089Spjd * Note: dd may become NULL, therefore we shouldn't dereference it 98219089Spjd * after this loop. 99168404Spjd */ 100168404Spjd for (; dd != NULL; dd = dd->dd_parent) { 101219089Spjd if (dd != target || snapshot) { 102219089Spjd if (!inheritable) 103219089Spjd break; 104219089Spjd inheriting = B_TRUE; 105219089Spjd } 106219089Spjd 107219089Spjd /* Check for a local value. */ 108275782Sdelphij err = zap_lookup(mos, dsl_dir_phys(dd)->dd_props_zapobj, 109275782Sdelphij propname, intsz, numints, buf); 110168404Spjd if (err != ENOENT) { 111219089Spjd if (setpoint != NULL && err == 0) 112168404Spjd dsl_dir_name(dd, setpoint); 113168404Spjd break; 114168404Spjd } 115168404Spjd 116168404Spjd /* 117219089Spjd * Skip the check for a received value if there is an explicit 118219089Spjd * inheritance entry. 119168404Spjd */ 120275782Sdelphij err = zap_contains(mos, dsl_dir_phys(dd)->dd_props_zapobj, 121219089Spjd inheritstr); 122219089Spjd if (err != 0 && err != ENOENT) 123168404Spjd break; 124219089Spjd 125219089Spjd if (err == ENOENT) { 126219089Spjd /* Check for a received value. */ 127275782Sdelphij err = zap_lookup(mos, dsl_dir_phys(dd)->dd_props_zapobj, 128219089Spjd recvdstr, intsz, numints, buf); 129219089Spjd if (err != ENOENT) { 130219089Spjd if (setpoint != NULL && err == 0) { 131219089Spjd if (inheriting) { 132219089Spjd dsl_dir_name(dd, setpoint); 133219089Spjd } else { 134219089Spjd (void) strcpy(setpoint, 135219089Spjd ZPROP_SOURCE_VAL_RECVD); 136219089Spjd } 137219089Spjd } 138219089Spjd break; 139219089Spjd } 140219089Spjd } 141219089Spjd 142219089Spjd /* 143219089Spjd * If we found an explicit inheritance entry, err is zero even 144219089Spjd * though we haven't yet found the value, so reinitializing err 145219089Spjd * at the end of the loop (instead of at the beginning) ensures 146219089Spjd * that err has a valid post-loop value. 147219089Spjd */ 148249195Smm err = SET_ERROR(ENOENT); 149168404Spjd } 150219089Spjd 151168404Spjd if (err == ENOENT) 152297507Smav err = dodefault(prop, intsz, numints, buf); 153168404Spjd 154219089Spjd strfree(inheritstr); 155219089Spjd strfree(recvdstr); 156219089Spjd 157168404Spjd return (err); 158168404Spjd} 159168404Spjd 160185029Spjdint 161185029Spjddsl_prop_get_ds(dsl_dataset_t *ds, const char *propname, 162219089Spjd int intsz, int numints, void *buf, char *setpoint) 163185029Spjd{ 164219089Spjd zfs_prop_t prop = zfs_name_to_prop(propname); 165219089Spjd boolean_t inheritable; 166219089Spjd uint64_t zapobj; 167219089Spjd 168248571Smm ASSERT(dsl_pool_config_held(ds->ds_dir->dd_pool)); 169219089Spjd inheritable = (prop == ZPROP_INVAL || zfs_prop_inheritable(prop)); 170275782Sdelphij zapobj = dsl_dataset_phys(ds)->ds_props_obj; 171185029Spjd 172219089Spjd if (zapobj != 0) { 173219089Spjd objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; 174219089Spjd int err; 175219089Spjd 176286575Smav ASSERT(ds->ds_is_snapshot); 177219089Spjd 178219089Spjd /* Check for a local value. */ 179219089Spjd err = zap_lookup(mos, zapobj, propname, intsz, numints, buf); 180185029Spjd if (err != ENOENT) { 181219089Spjd if (setpoint != NULL && err == 0) 182185029Spjd dsl_dataset_name(ds, setpoint); 183185029Spjd return (err); 184185029Spjd } 185219089Spjd 186219089Spjd /* 187219089Spjd * Skip the check for a received value if there is an explicit 188219089Spjd * inheritance entry. 189219089Spjd */ 190219089Spjd if (inheritable) { 191219089Spjd char *inheritstr = kmem_asprintf("%s%s", propname, 192219089Spjd ZPROP_INHERIT_SUFFIX); 193219089Spjd err = zap_contains(mos, zapobj, inheritstr); 194219089Spjd strfree(inheritstr); 195219089Spjd if (err != 0 && err != ENOENT) 196219089Spjd return (err); 197219089Spjd } 198219089Spjd 199219089Spjd if (err == ENOENT) { 200219089Spjd /* Check for a received value. */ 201219089Spjd char *recvdstr = kmem_asprintf("%s%s", propname, 202219089Spjd ZPROP_RECVD_SUFFIX); 203219089Spjd err = zap_lookup(mos, zapobj, recvdstr, 204219089Spjd intsz, numints, buf); 205219089Spjd strfree(recvdstr); 206219089Spjd if (err != ENOENT) { 207219089Spjd if (setpoint != NULL && err == 0) 208219089Spjd (void) strcpy(setpoint, 209219089Spjd ZPROP_SOURCE_VAL_RECVD); 210219089Spjd return (err); 211219089Spjd } 212219089Spjd } 213185029Spjd } 214185029Spjd 215185029Spjd return (dsl_prop_get_dd(ds->ds_dir, propname, 216286575Smav intsz, numints, buf, setpoint, ds->ds_is_snapshot)); 217185029Spjd} 218185029Spjd 219288204Sdelphijstatic dsl_prop_record_t * 220288204Sdelphijdsl_prop_record_find(dsl_dir_t *dd, const char *propname) 221288204Sdelphij{ 222288204Sdelphij dsl_prop_record_t *pr = NULL; 223288204Sdelphij 224288204Sdelphij ASSERT(MUTEX_HELD(&dd->dd_lock)); 225288204Sdelphij 226288204Sdelphij for (pr = list_head(&dd->dd_props); 227288204Sdelphij pr != NULL; pr = list_next(&dd->dd_props, pr)) { 228288204Sdelphij if (strcmp(pr->pr_propname, propname) == 0) 229288204Sdelphij break; 230288204Sdelphij } 231288204Sdelphij 232288204Sdelphij return (pr); 233288204Sdelphij} 234288204Sdelphij 235288204Sdelphijstatic dsl_prop_record_t * 236288204Sdelphijdsl_prop_record_create(dsl_dir_t *dd, const char *propname) 237288204Sdelphij{ 238288204Sdelphij dsl_prop_record_t *pr; 239288204Sdelphij 240288204Sdelphij ASSERT(MUTEX_HELD(&dd->dd_lock)); 241288204Sdelphij 242288204Sdelphij pr = kmem_alloc(sizeof (dsl_prop_record_t), KM_SLEEP); 243288204Sdelphij pr->pr_propname = spa_strdup(propname); 244288204Sdelphij list_create(&pr->pr_cbs, sizeof (dsl_prop_cb_record_t), 245288204Sdelphij offsetof(dsl_prop_cb_record_t, cbr_pr_node)); 246288204Sdelphij list_insert_head(&dd->dd_props, pr); 247288204Sdelphij 248288204Sdelphij return (pr); 249288204Sdelphij} 250288204Sdelphij 251288204Sdelphijvoid 252288204Sdelphijdsl_prop_init(dsl_dir_t *dd) 253288204Sdelphij{ 254288204Sdelphij list_create(&dd->dd_props, sizeof (dsl_prop_record_t), 255288204Sdelphij offsetof(dsl_prop_record_t, pr_node)); 256288204Sdelphij} 257288204Sdelphij 258288204Sdelphijvoid 259288204Sdelphijdsl_prop_fini(dsl_dir_t *dd) 260288204Sdelphij{ 261288204Sdelphij dsl_prop_record_t *pr; 262288204Sdelphij 263288204Sdelphij while ((pr = list_remove_head(&dd->dd_props)) != NULL) { 264288204Sdelphij list_destroy(&pr->pr_cbs); 265288204Sdelphij strfree((char *)pr->pr_propname); 266288204Sdelphij kmem_free(pr, sizeof (dsl_prop_record_t)); 267288204Sdelphij } 268288204Sdelphij list_destroy(&dd->dd_props); 269288204Sdelphij} 270288204Sdelphij 271168404Spjd/* 272168404Spjd * Register interest in the named property. We'll call the callback 273168404Spjd * once to notify it of the current property value, and again each time 274168404Spjd * the property changes, until this callback is unregistered. 275168404Spjd * 276168404Spjd * Return 0 on success, errno if the prop is not an integer value. 277168404Spjd */ 278168404Spjdint 279168404Spjddsl_prop_register(dsl_dataset_t *ds, const char *propname, 280168404Spjd dsl_prop_changed_cb_t *callback, void *cbarg) 281168404Spjd{ 282168404Spjd dsl_dir_t *dd = ds->ds_dir; 283185029Spjd dsl_pool_t *dp = dd->dd_pool; 284168404Spjd uint64_t value; 285288204Sdelphij dsl_prop_record_t *pr; 286168404Spjd dsl_prop_cb_record_t *cbr; 287168404Spjd int err; 288168404Spjd 289248571Smm ASSERT(dsl_pool_config_held(dp)); 290168404Spjd 291248571Smm err = dsl_prop_get_int_ds(ds, propname, &value); 292248571Smm if (err != 0) 293168404Spjd return (err); 294168404Spjd 295168404Spjd cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP); 296168404Spjd cbr->cbr_ds = ds; 297168404Spjd cbr->cbr_func = callback; 298168404Spjd cbr->cbr_arg = cbarg; 299288204Sdelphij 300168404Spjd mutex_enter(&dd->dd_lock); 301288204Sdelphij pr = dsl_prop_record_find(dd, propname); 302288204Sdelphij if (pr == NULL) 303288204Sdelphij pr = dsl_prop_record_create(dd, propname); 304288204Sdelphij cbr->cbr_pr = pr; 305288204Sdelphij list_insert_head(&pr->pr_cbs, cbr); 306288204Sdelphij list_insert_head(&ds->ds_prop_cbs, cbr); 307168404Spjd mutex_exit(&dd->dd_lock); 308168404Spjd 309168404Spjd cbr->cbr_func(cbr->cbr_arg, value); 310168404Spjd return (0); 311168404Spjd} 312168404Spjd 313168404Spjdint 314185029Spjddsl_prop_get(const char *dsname, const char *propname, 315168404Spjd int intsz, int numints, void *buf, char *setpoint) 316168404Spjd{ 317248571Smm objset_t *os; 318248571Smm int error; 319168404Spjd 320248571Smm error = dmu_objset_hold(dsname, FTAG, &os); 321248571Smm if (error != 0) 322248571Smm return (error); 323168404Spjd 324248571Smm error = dsl_prop_get_ds(dmu_objset_ds(os), propname, 325248571Smm intsz, numints, buf, setpoint); 326168404Spjd 327248571Smm dmu_objset_rele(os, FTAG); 328248571Smm return (error); 329168404Spjd} 330168404Spjd 331168404Spjd/* 332168404Spjd * Get the current property value. It may have changed by the time this 333168404Spjd * function returns, so it is NOT safe to follow up with 334168404Spjd * dsl_prop_register() and assume that the value has not changed in 335168404Spjd * between. 336168404Spjd * 337168404Spjd * Return 0 on success, ENOENT if ddname is invalid. 338168404Spjd */ 339168404Spjdint 340168404Spjddsl_prop_get_integer(const char *ddname, const char *propname, 341168404Spjd uint64_t *valuep, char *setpoint) 342168404Spjd{ 343168404Spjd return (dsl_prop_get(ddname, propname, 8, 1, valuep, setpoint)); 344168404Spjd} 345168404Spjd 346248571Smmint 347248571Smmdsl_prop_get_int_ds(dsl_dataset_t *ds, const char *propname, 348248571Smm uint64_t *valuep) 349219089Spjd{ 350248571Smm return (dsl_prop_get_ds(ds, propname, 8, 1, valuep, NULL)); 351219089Spjd} 352219089Spjd 353168404Spjd/* 354219089Spjd * Predict the effective value of the given special property if it were set with 355219089Spjd * the given value and source. This is not a general purpose function. It exists 356219089Spjd * only to handle the special requirements of the quota and reservation 357219089Spjd * properties. The fact that these properties are non-inheritable greatly 358219089Spjd * simplifies the prediction logic. 359219089Spjd * 360219089Spjd * Returns 0 on success, a positive error code on failure, or -1 if called with 361219089Spjd * a property not handled by this function. 362219089Spjd */ 363219089Spjdint 364248571Smmdsl_prop_predict(dsl_dir_t *dd, const char *propname, 365248571Smm zprop_source_t source, uint64_t value, uint64_t *newvalp) 366219089Spjd{ 367219089Spjd zfs_prop_t prop = zfs_name_to_prop(propname); 368219089Spjd objset_t *mos; 369219089Spjd uint64_t zapobj; 370219089Spjd uint64_t version; 371219089Spjd char *recvdstr; 372219089Spjd int err = 0; 373219089Spjd 374219089Spjd switch (prop) { 375219089Spjd case ZFS_PROP_QUOTA: 376219089Spjd case ZFS_PROP_RESERVATION: 377219089Spjd case ZFS_PROP_REFQUOTA: 378219089Spjd case ZFS_PROP_REFRESERVATION: 379219089Spjd break; 380219089Spjd default: 381219089Spjd return (-1); 382219089Spjd } 383219089Spjd 384219089Spjd mos = dd->dd_pool->dp_meta_objset; 385275782Sdelphij zapobj = dsl_dir_phys(dd)->dd_props_zapobj; 386219089Spjd recvdstr = kmem_asprintf("%s%s", propname, ZPROP_RECVD_SUFFIX); 387219089Spjd 388219089Spjd version = spa_version(dd->dd_pool->dp_spa); 389219089Spjd if (version < SPA_VERSION_RECVD_PROPS) { 390219089Spjd if (source & ZPROP_SRC_NONE) 391219089Spjd source = ZPROP_SRC_NONE; 392219089Spjd else if (source & ZPROP_SRC_RECEIVED) 393219089Spjd source = ZPROP_SRC_LOCAL; 394219089Spjd } 395219089Spjd 396219089Spjd switch (source) { 397219089Spjd case ZPROP_SRC_NONE: 398219089Spjd /* Revert to the received value, if any. */ 399248571Smm err = zap_lookup(mos, zapobj, recvdstr, 8, 1, newvalp); 400219089Spjd if (err == ENOENT) 401248571Smm *newvalp = 0; 402219089Spjd break; 403219089Spjd case ZPROP_SRC_LOCAL: 404248571Smm *newvalp = value; 405219089Spjd break; 406219089Spjd case ZPROP_SRC_RECEIVED: 407219089Spjd /* 408219089Spjd * If there's no local setting, then the new received value will 409219089Spjd * be the effective value. 410219089Spjd */ 411248571Smm err = zap_lookup(mos, zapobj, propname, 8, 1, newvalp); 412219089Spjd if (err == ENOENT) 413248571Smm *newvalp = value; 414219089Spjd break; 415219089Spjd case (ZPROP_SRC_NONE | ZPROP_SRC_RECEIVED): 416219089Spjd /* 417219089Spjd * We're clearing the received value, so the local setting (if 418219089Spjd * it exists) remains the effective value. 419219089Spjd */ 420248571Smm err = zap_lookup(mos, zapobj, propname, 8, 1, newvalp); 421219089Spjd if (err == ENOENT) 422248571Smm *newvalp = 0; 423219089Spjd break; 424219089Spjd default: 425248571Smm panic("unexpected property source: %d", source); 426219089Spjd } 427219089Spjd 428219089Spjd strfree(recvdstr); 429219089Spjd 430219089Spjd if (err == ENOENT) 431219089Spjd return (0); 432219089Spjd 433219089Spjd return (err); 434219089Spjd} 435219089Spjd 436219089Spjd/* 437288204Sdelphij * Unregister all callbacks that are registered with the 438288204Sdelphij * given callback argument. 439168404Spjd */ 440288204Sdelphijvoid 441288204Sdelphijdsl_prop_unregister_all(dsl_dataset_t *ds, void *cbarg) 442168404Spjd{ 443288204Sdelphij dsl_prop_cb_record_t *cbr, *next_cbr; 444288204Sdelphij 445168404Spjd dsl_dir_t *dd = ds->ds_dir; 446168404Spjd 447168404Spjd mutex_enter(&dd->dd_lock); 448288204Sdelphij next_cbr = list_head(&ds->ds_prop_cbs); 449288204Sdelphij while (next_cbr != NULL) { 450288204Sdelphij cbr = next_cbr; 451288204Sdelphij next_cbr = list_next(&ds->ds_prop_cbs, cbr); 452288204Sdelphij if (cbr->cbr_arg == cbarg) { 453288204Sdelphij list_remove(&ds->ds_prop_cbs, cbr); 454288204Sdelphij list_remove(&cbr->cbr_pr->pr_cbs, cbr); 455288204Sdelphij kmem_free(cbr, sizeof (dsl_prop_cb_record_t)); 456288204Sdelphij } 457168404Spjd } 458168404Spjd mutex_exit(&dd->dd_lock); 459168404Spjd} 460168404Spjd 461248571Smmboolean_t 462248571Smmdsl_prop_hascb(dsl_dataset_t *ds) 463168404Spjd{ 464288204Sdelphij return (!list_is_empty(&ds->ds_prop_cbs)); 465248571Smm} 466168404Spjd 467248571Smm/* ARGSUSED */ 468248571Smmstatic int 469248571Smmdsl_prop_notify_all_cb(dsl_pool_t *dp, dsl_dataset_t *ds, void *arg) 470248571Smm{ 471248571Smm dsl_dir_t *dd = ds->ds_dir; 472288204Sdelphij dsl_prop_record_t *pr; 473248571Smm dsl_prop_cb_record_t *cbr; 474248571Smm 475248571Smm mutex_enter(&dd->dd_lock); 476288204Sdelphij for (pr = list_head(&dd->dd_props); 477288204Sdelphij pr; pr = list_next(&dd->dd_props, pr)) { 478288204Sdelphij for (cbr = list_head(&pr->pr_cbs); cbr; 479288204Sdelphij cbr = list_next(&pr->pr_cbs, cbr)) { 480288204Sdelphij uint64_t value; 481248571Smm 482288204Sdelphij /* 483288204Sdelphij * Callback entries do not have holds on their 484288204Sdelphij * datasets so that datasets with registered 485288204Sdelphij * callbacks are still eligible for eviction. 486288204Sdelphij * Unlike operations to update properties on a 487288204Sdelphij * single dataset, we are performing a recursive 488288204Sdelphij * descent of related head datasets. The caller 489288204Sdelphij * of this function only has a dataset hold on 490288204Sdelphij * the passed in head dataset, not the snapshots 491288204Sdelphij * associated with this dataset. Without a hold, 492288204Sdelphij * the dataset pointer within callback records 493288204Sdelphij * for snapshots can be invalidated by eviction 494288204Sdelphij * at any time. 495288204Sdelphij * 496288204Sdelphij * Use dsl_dataset_try_add_ref() to verify 497288204Sdelphij * that the dataset for a snapshot has not 498288204Sdelphij * begun eviction processing and to prevent 499288204Sdelphij * eviction from occurring for the duration of 500288204Sdelphij * the callback. If the hold attempt fails, 501288204Sdelphij * this object is already being evicted and the 502288204Sdelphij * callback can be safely ignored. 503288204Sdelphij */ 504288204Sdelphij if (ds != cbr->cbr_ds && 505288204Sdelphij !dsl_dataset_try_add_ref(dp, cbr->cbr_ds, FTAG)) 506288204Sdelphij continue; 507286541Smav 508288204Sdelphij if (dsl_prop_get_ds(cbr->cbr_ds, 509288204Sdelphij cbr->cbr_pr->pr_propname, sizeof (value), 1, 510288204Sdelphij &value, NULL) == 0) 511288204Sdelphij cbr->cbr_func(cbr->cbr_arg, value); 512286541Smav 513288204Sdelphij if (ds != cbr->cbr_ds) 514288204Sdelphij dsl_dataset_rele(cbr->cbr_ds, FTAG); 515288204Sdelphij } 516248571Smm } 517248571Smm mutex_exit(&dd->dd_lock); 518248571Smm 519248571Smm return (0); 520168404Spjd} 521168404Spjd 522248571Smm/* 523248571Smm * Update all property values for ddobj & its descendants. This is used 524248571Smm * when renaming the dir. 525248571Smm */ 526248571Smmvoid 527248571Smmdsl_prop_notify_all(dsl_dir_t *dd) 528248571Smm{ 529248571Smm dsl_pool_t *dp = dd->dd_pool; 530248571Smm ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); 531248571Smm (void) dmu_objset_find_dp(dp, dd->dd_object, dsl_prop_notify_all_cb, 532248571Smm NULL, DS_FIND_CHILDREN); 533248571Smm} 534248571Smm 535168404Spjdstatic void 536168404Spjddsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj, 537168404Spjd const char *propname, uint64_t value, int first) 538168404Spjd{ 539168404Spjd dsl_dir_t *dd; 540288204Sdelphij dsl_prop_record_t *pr; 541168404Spjd dsl_prop_cb_record_t *cbr; 542168404Spjd objset_t *mos = dp->dp_meta_objset; 543168404Spjd zap_cursor_t zc; 544185029Spjd zap_attribute_t *za; 545168404Spjd int err; 546168404Spjd 547248571Smm ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); 548248571Smm err = dsl_dir_hold_obj(dp, ddobj, NULL, FTAG, &dd); 549168404Spjd if (err) 550168404Spjd return; 551168404Spjd 552168404Spjd if (!first) { 553168404Spjd /* 554168404Spjd * If the prop is set here, then this change is not 555168404Spjd * being inherited here or below; stop the recursion. 556168404Spjd */ 557275782Sdelphij err = zap_contains(mos, dsl_dir_phys(dd)->dd_props_zapobj, 558275782Sdelphij propname); 559168404Spjd if (err == 0) { 560248571Smm dsl_dir_rele(dd, FTAG); 561168404Spjd return; 562168404Spjd } 563168404Spjd ASSERT3U(err, ==, ENOENT); 564168404Spjd } 565168404Spjd 566168404Spjd mutex_enter(&dd->dd_lock); 567288204Sdelphij pr = dsl_prop_record_find(dd, propname); 568288204Sdelphij if (pr != NULL) { 569288204Sdelphij for (cbr = list_head(&pr->pr_cbs); cbr; 570288204Sdelphij cbr = list_next(&pr->pr_cbs, cbr)) { 571288204Sdelphij uint64_t propobj; 572185029Spjd 573288204Sdelphij /* 574288204Sdelphij * cbr->cbr_ds may be invalidated due to eviction, 575288204Sdelphij * requiring the use of dsl_dataset_try_add_ref(). 576288204Sdelphij * See comment block in dsl_prop_notify_all_cb() 577288204Sdelphij * for details. 578288204Sdelphij */ 579288204Sdelphij if (!dsl_dataset_try_add_ref(dp, cbr->cbr_ds, FTAG)) 580288204Sdelphij continue; 581185029Spjd 582288204Sdelphij propobj = dsl_dataset_phys(cbr->cbr_ds)->ds_props_obj; 583286541Smav 584288204Sdelphij /* 585288204Sdelphij * If the property is not set on this ds, then it is 586288204Sdelphij * inherited here; call the callback. 587288204Sdelphij */ 588288204Sdelphij if (propobj == 0 || 589288204Sdelphij zap_contains(mos, propobj, propname) != 0) 590288204Sdelphij cbr->cbr_func(cbr->cbr_arg, value); 591185029Spjd 592288204Sdelphij dsl_dataset_rele(cbr->cbr_ds, FTAG); 593288204Sdelphij } 594168404Spjd } 595168404Spjd mutex_exit(&dd->dd_lock); 596168404Spjd 597185029Spjd za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); 598168404Spjd for (zap_cursor_init(&zc, mos, 599275782Sdelphij dsl_dir_phys(dd)->dd_child_dir_zapobj); 600185029Spjd zap_cursor_retrieve(&zc, za) == 0; 601168404Spjd zap_cursor_advance(&zc)) { 602185029Spjd dsl_prop_changed_notify(dp, za->za_first_integer, 603168404Spjd propname, value, FALSE); 604168404Spjd } 605185029Spjd kmem_free(za, sizeof (zap_attribute_t)); 606168404Spjd zap_cursor_fini(&zc); 607248571Smm dsl_dir_rele(dd, FTAG); 608168404Spjd} 609168404Spjd 610219089Spjdvoid 611248571Smmdsl_prop_set_sync_impl(dsl_dataset_t *ds, const char *propname, 612248571Smm zprop_source_t source, int intsz, int numints, const void *value, 613248571Smm dmu_tx_t *tx) 614168404Spjd{ 615185029Spjd objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; 616219089Spjd uint64_t zapobj, intval, dummy; 617168404Spjd int isint; 618185029Spjd char valbuf[32]; 619248571Smm const char *valstr = NULL; 620219089Spjd char *inheritstr; 621219089Spjd char *recvdstr; 622219089Spjd char *tbuf = NULL; 623219089Spjd int err; 624219089Spjd uint64_t version = spa_version(ds->ds_dir->dd_pool->dp_spa); 625168404Spjd 626297507Smav isint = (dodefault(zfs_name_to_prop(propname), 8, 1, &intval) == 0); 627168404Spjd 628286575Smav if (ds->ds_is_snapshot) { 629219089Spjd ASSERT(version >= SPA_VERSION_SNAP_PROPS); 630275782Sdelphij if (dsl_dataset_phys(ds)->ds_props_obj == 0) { 631185029Spjd dmu_buf_will_dirty(ds->ds_dbuf, tx); 632275782Sdelphij dsl_dataset_phys(ds)->ds_props_obj = 633185029Spjd zap_create(mos, 634185029Spjd DMU_OT_DSL_PROPS, DMU_OT_NONE, 0, tx); 635185029Spjd } 636275782Sdelphij zapobj = dsl_dataset_phys(ds)->ds_props_obj; 637185029Spjd } else { 638275782Sdelphij zapobj = dsl_dir_phys(ds->ds_dir)->dd_props_zapobj; 639185029Spjd } 640185029Spjd 641219089Spjd if (version < SPA_VERSION_RECVD_PROPS) { 642219089Spjd if (source & ZPROP_SRC_NONE) 643219089Spjd source = ZPROP_SRC_NONE; 644219089Spjd else if (source & ZPROP_SRC_RECEIVED) 645219089Spjd source = ZPROP_SRC_LOCAL; 646219089Spjd } 647219089Spjd 648219089Spjd inheritstr = kmem_asprintf("%s%s", propname, ZPROP_INHERIT_SUFFIX); 649219089Spjd recvdstr = kmem_asprintf("%s%s", propname, ZPROP_RECVD_SUFFIX); 650219089Spjd 651219089Spjd switch (source) { 652219089Spjd case ZPROP_SRC_NONE: 653219089Spjd /* 654219089Spjd * revert to received value, if any (inherit -S) 655219089Spjd * - remove propname 656219089Spjd * - remove propname$inherit 657219089Spjd */ 658219089Spjd err = zap_remove(mos, zapobj, propname, tx); 659168404Spjd ASSERT(err == 0 || err == ENOENT); 660219089Spjd err = zap_remove(mos, zapobj, inheritstr, tx); 661219089Spjd ASSERT(err == 0 || err == ENOENT); 662219089Spjd break; 663219089Spjd case ZPROP_SRC_LOCAL: 664219089Spjd /* 665219089Spjd * remove propname$inherit 666219089Spjd * set propname -> value 667219089Spjd */ 668219089Spjd err = zap_remove(mos, zapobj, inheritstr, tx); 669219089Spjd ASSERT(err == 0 || err == ENOENT); 670248571Smm VERIFY0(zap_update(mos, zapobj, propname, 671248571Smm intsz, numints, value, tx)); 672219089Spjd break; 673219089Spjd case ZPROP_SRC_INHERITED: 674219089Spjd /* 675219089Spjd * explicitly inherit 676219089Spjd * - remove propname 677219089Spjd * - set propname$inherit 678219089Spjd */ 679219089Spjd err = zap_remove(mos, zapobj, propname, tx); 680219089Spjd ASSERT(err == 0 || err == ENOENT); 681219089Spjd if (version >= SPA_VERSION_RECVD_PROPS && 682248571Smm dsl_prop_get_int_ds(ds, ZPROP_HAS_RECVD, &dummy) == 0) { 683219089Spjd dummy = 0; 684248571Smm VERIFY0(zap_update(mos, zapobj, inheritstr, 685248571Smm 8, 1, &dummy, tx)); 686168404Spjd } 687219089Spjd break; 688219089Spjd case ZPROP_SRC_RECEIVED: 689219089Spjd /* 690219089Spjd * set propname$recvd -> value 691219089Spjd */ 692219089Spjd err = zap_update(mos, zapobj, recvdstr, 693248571Smm intsz, numints, value, tx); 694219089Spjd ASSERT(err == 0); 695219089Spjd break; 696219089Spjd case (ZPROP_SRC_NONE | ZPROP_SRC_LOCAL | ZPROP_SRC_RECEIVED): 697219089Spjd /* 698219089Spjd * clear local and received settings 699219089Spjd * - remove propname 700219089Spjd * - remove propname$inherit 701219089Spjd * - remove propname$recvd 702219089Spjd */ 703219089Spjd err = zap_remove(mos, zapobj, propname, tx); 704219089Spjd ASSERT(err == 0 || err == ENOENT); 705219089Spjd err = zap_remove(mos, zapobj, inheritstr, tx); 706219089Spjd ASSERT(err == 0 || err == ENOENT); 707219089Spjd /* FALLTHRU */ 708219089Spjd case (ZPROP_SRC_NONE | ZPROP_SRC_RECEIVED): 709219089Spjd /* 710219089Spjd * remove propname$recvd 711219089Spjd */ 712219089Spjd err = zap_remove(mos, zapobj, recvdstr, tx); 713219089Spjd ASSERT(err == 0 || err == ENOENT); 714219089Spjd break; 715219089Spjd default: 716219089Spjd cmn_err(CE_PANIC, "unexpected property source: %d", source); 717168404Spjd } 718168404Spjd 719219089Spjd strfree(inheritstr); 720219089Spjd strfree(recvdstr); 721219089Spjd 722168404Spjd if (isint) { 723248571Smm VERIFY0(dsl_prop_get_int_ds(ds, propname, &intval)); 724219089Spjd 725286575Smav if (ds->ds_is_snapshot) { 726185029Spjd dsl_prop_cb_record_t *cbr; 727185029Spjd /* 728185029Spjd * It's a snapshot; nothing can inherit this 729185029Spjd * property, so just look for callbacks on this 730185029Spjd * ds here. 731185029Spjd */ 732185029Spjd mutex_enter(&ds->ds_dir->dd_lock); 733288204Sdelphij for (cbr = list_head(&ds->ds_prop_cbs); cbr; 734288204Sdelphij cbr = list_next(&ds->ds_prop_cbs, cbr)) { 735288204Sdelphij if (strcmp(cbr->cbr_pr->pr_propname, 736288204Sdelphij propname) == 0) 737185029Spjd cbr->cbr_func(cbr->cbr_arg, intval); 738185029Spjd } 739185029Spjd mutex_exit(&ds->ds_dir->dd_lock); 740185029Spjd } else { 741185029Spjd dsl_prop_changed_notify(ds->ds_dir->dd_pool, 742219089Spjd ds->ds_dir->dd_object, propname, intval, TRUE); 743185029Spjd } 744219089Spjd 745185029Spjd (void) snprintf(valbuf, sizeof (valbuf), 746185029Spjd "%lld", (longlong_t)intval); 747185029Spjd valstr = valbuf; 748185029Spjd } else { 749219089Spjd if (source == ZPROP_SRC_LOCAL) { 750248571Smm valstr = value; 751219089Spjd } else { 752219089Spjd tbuf = kmem_alloc(ZAP_MAXVALUELEN, KM_SLEEP); 753219089Spjd if (dsl_prop_get_ds(ds, propname, 1, 754219089Spjd ZAP_MAXVALUELEN, tbuf, NULL) == 0) 755219089Spjd valstr = tbuf; 756219089Spjd } 757185029Spjd } 758219089Spjd 759248571Smm spa_history_log_internal_ds(ds, (source == ZPROP_SRC_NONE || 760248571Smm source == ZPROP_SRC_INHERITED) ? "inherit" : "set", tx, 761248571Smm "%s=%s", propname, (valstr == NULL ? "" : valstr)); 762219089Spjd 763219089Spjd if (tbuf != NULL) 764219089Spjd kmem_free(tbuf, ZAP_MAXVALUELEN); 765168404Spjd} 766168404Spjd 767248571Smmint 768248571Smmdsl_prop_set_int(const char *dsname, const char *propname, 769248571Smm zprop_source_t source, uint64_t value) 770209962Smm{ 771248571Smm nvlist_t *nvl = fnvlist_alloc(); 772248571Smm int error; 773209962Smm 774248571Smm fnvlist_add_uint64(nvl, propname, value); 775248571Smm error = dsl_props_set(dsname, source, nvl); 776248571Smm fnvlist_free(nvl); 777248571Smm return (error); 778209962Smm} 779209962Smm 780248571Smmint 781248571Smmdsl_prop_set_string(const char *dsname, const char *propname, 782248571Smm zprop_source_t source, const char *value) 783168404Spjd{ 784248571Smm nvlist_t *nvl = fnvlist_alloc(); 785248571Smm int error; 786168404Spjd 787248571Smm fnvlist_add_string(nvl, propname, value); 788248571Smm error = dsl_props_set(dsname, source, nvl); 789248571Smm fnvlist_free(nvl); 790248571Smm return (error); 791168404Spjd} 792168404Spjd 793168404Spjdint 794248571Smmdsl_prop_inherit(const char *dsname, const char *propname, 795248571Smm zprop_source_t source) 796168404Spjd{ 797248571Smm nvlist_t *nvl = fnvlist_alloc(); 798248571Smm int error; 799168404Spjd 800248571Smm fnvlist_add_boolean(nvl, propname); 801248571Smm error = dsl_props_set(dsname, source, nvl); 802248571Smm fnvlist_free(nvl); 803248571Smm return (error); 804248571Smm} 805168404Spjd 806248571Smmtypedef struct dsl_props_set_arg { 807248571Smm const char *dpsa_dsname; 808248571Smm zprop_source_t dpsa_source; 809248571Smm nvlist_t *dpsa_props; 810248571Smm} dsl_props_set_arg_t; 811185029Spjd 812248571Smmstatic int 813248571Smmdsl_props_set_check(void *arg, dmu_tx_t *tx) 814209962Smm{ 815248571Smm dsl_props_set_arg_t *dpsa = arg; 816248571Smm dsl_pool_t *dp = dmu_tx_pool(tx); 817209962Smm dsl_dataset_t *ds; 818219089Spjd uint64_t version; 819209962Smm nvpair_t *elem = NULL; 820209962Smm int err; 821209962Smm 822248571Smm err = dsl_dataset_hold(dp, dpsa->dpsa_dsname, FTAG, &ds); 823248571Smm if (err != 0) 824219089Spjd return (err); 825248571Smm 826219089Spjd version = spa_version(ds->ds_dir->dd_pool->dp_spa); 827248571Smm while ((elem = nvlist_next_nvpair(dpsa->dpsa_props, elem)) != NULL) { 828219089Spjd if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) { 829219089Spjd dsl_dataset_rele(ds, FTAG); 830249195Smm return (SET_ERROR(ENAMETOOLONG)); 831219089Spjd } 832209962Smm if (nvpair_type(elem) == DATA_TYPE_STRING) { 833248571Smm char *valstr = fnvpair_value_string(elem); 834219089Spjd if (strlen(valstr) >= (version < 835219089Spjd SPA_VERSION_STMF_PROP ? 836219089Spjd ZAP_OLDMAXVALUELEN : ZAP_MAXVALUELEN)) { 837219089Spjd dsl_dataset_rele(ds, FTAG); 838209962Smm return (E2BIG); 839219089Spjd } 840209962Smm } 841209962Smm } 842209962Smm 843286575Smav if (ds->ds_is_snapshot && version < SPA_VERSION_SNAP_PROPS) { 844209962Smm dsl_dataset_rele(ds, FTAG); 845249195Smm return (SET_ERROR(ENOTSUP)); 846209962Smm } 847248571Smm dsl_dataset_rele(ds, FTAG); 848248571Smm return (0); 849248571Smm} 850209962Smm 851248571Smmvoid 852248571Smmdsl_props_set_sync_impl(dsl_dataset_t *ds, zprop_source_t source, 853248571Smm nvlist_t *props, dmu_tx_t *tx) 854248571Smm{ 855248571Smm nvpair_t *elem = NULL; 856219089Spjd 857248571Smm while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { 858248571Smm nvpair_t *pair = elem; 859332533Smav const char *name = nvpair_name(pair); 860209962Smm 861248571Smm if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 862248571Smm /* 863332533Smav * This usually happens when we reuse the nvlist_t data 864332533Smav * returned by the counterpart dsl_prop_get_all_impl(). 865332533Smav * For instance we do this to restore the original 866332533Smav * received properties when an error occurs in the 867332533Smav * zfs_ioc_recv() codepath. 868248571Smm */ 869248571Smm nvlist_t *attrs = fnvpair_value_nvlist(pair); 870248571Smm pair = fnvlist_lookup_nvpair(attrs, ZPROP_VALUE); 871248571Smm } 872248571Smm 873248571Smm if (nvpair_type(pair) == DATA_TYPE_STRING) { 874248571Smm const char *value = fnvpair_value_string(pair); 875332533Smav dsl_prop_set_sync_impl(ds, name, 876248571Smm source, 1, strlen(value) + 1, value, tx); 877248571Smm } else if (nvpair_type(pair) == DATA_TYPE_UINT64) { 878248571Smm uint64_t intval = fnvpair_value_uint64(pair); 879332533Smav dsl_prop_set_sync_impl(ds, name, 880248571Smm source, sizeof (intval), 1, &intval, tx); 881248571Smm } else if (nvpair_type(pair) == DATA_TYPE_BOOLEAN) { 882332533Smav dsl_prop_set_sync_impl(ds, name, 883248571Smm source, 0, 0, NULL, tx); 884248571Smm } else { 885248571Smm panic("invalid nvpair type"); 886248571Smm } 887248571Smm } 888248571Smm} 889248571Smm 890248571Smmstatic void 891248571Smmdsl_props_set_sync(void *arg, dmu_tx_t *tx) 892248571Smm{ 893248571Smm dsl_props_set_arg_t *dpsa = arg; 894248571Smm dsl_pool_t *dp = dmu_tx_pool(tx); 895248571Smm dsl_dataset_t *ds; 896248571Smm 897248571Smm VERIFY0(dsl_dataset_hold(dp, dpsa->dpsa_dsname, FTAG, &ds)); 898248571Smm dsl_props_set_sync_impl(ds, dpsa->dpsa_source, dpsa->dpsa_props, tx); 899209962Smm dsl_dataset_rele(ds, FTAG); 900209962Smm} 901209962Smm 902248571Smm/* 903248571Smm * All-or-nothing; if any prop can't be set, nothing will be modified. 904248571Smm */ 905248571Smmint 906248571Smmdsl_props_set(const char *dsname, zprop_source_t source, nvlist_t *props) 907248571Smm{ 908248571Smm dsl_props_set_arg_t dpsa; 909248571Smm int nblks = 0; 910248571Smm 911248571Smm dpsa.dpsa_dsname = dsname; 912248571Smm dpsa.dpsa_source = source; 913248571Smm dpsa.dpsa_props = props; 914248571Smm 915248571Smm /* 916248571Smm * If the source includes NONE, then we will only be removing entries 917248571Smm * from the ZAP object. In that case don't check for ENOSPC. 918248571Smm */ 919248571Smm if ((source & ZPROP_SRC_NONE) == 0) 920248571Smm nblks = 2 * fnvlist_num_pairs(props); 921248571Smm 922248571Smm return (dsl_sync_task(dsname, dsl_props_set_check, dsl_props_set_sync, 923268473Sdelphij &dpsa, nblks, ZFS_SPACE_CHECK_RESERVED)); 924248571Smm} 925248571Smm 926219089Spjdtypedef enum dsl_prop_getflags { 927219089Spjd DSL_PROP_GET_INHERITING = 0x1, /* searching parent of target ds */ 928219089Spjd DSL_PROP_GET_SNAPSHOT = 0x2, /* snapshot dataset */ 929219089Spjd DSL_PROP_GET_LOCAL = 0x4, /* local properties */ 930219089Spjd DSL_PROP_GET_RECEIVED = 0x8 /* received properties */ 931219089Spjd} dsl_prop_getflags_t; 932219089Spjd 933219089Spjdstatic int 934219089Spjddsl_prop_get_all_impl(objset_t *mos, uint64_t propobj, 935219089Spjd const char *setpoint, dsl_prop_getflags_t flags, nvlist_t *nv) 936219089Spjd{ 937219089Spjd zap_cursor_t zc; 938219089Spjd zap_attribute_t za; 939219089Spjd int err = 0; 940219089Spjd 941219089Spjd for (zap_cursor_init(&zc, mos, propobj); 942219089Spjd (err = zap_cursor_retrieve(&zc, &za)) == 0; 943219089Spjd zap_cursor_advance(&zc)) { 944219089Spjd nvlist_t *propval; 945219089Spjd zfs_prop_t prop; 946219089Spjd char buf[ZAP_MAXNAMELEN]; 947219089Spjd char *valstr; 948219089Spjd const char *suffix; 949219089Spjd const char *propname; 950219089Spjd const char *source; 951219089Spjd 952219089Spjd suffix = strchr(za.za_name, '$'); 953219089Spjd 954219089Spjd if (suffix == NULL) { 955219089Spjd /* 956219089Spjd * Skip local properties if we only want received 957219089Spjd * properties. 958219089Spjd */ 959219089Spjd if (flags & DSL_PROP_GET_RECEIVED) 960219089Spjd continue; 961219089Spjd 962219089Spjd propname = za.za_name; 963219089Spjd source = setpoint; 964219089Spjd } else if (strcmp(suffix, ZPROP_INHERIT_SUFFIX) == 0) { 965219089Spjd /* Skip explicitly inherited entries. */ 966219089Spjd continue; 967219089Spjd } else if (strcmp(suffix, ZPROP_RECVD_SUFFIX) == 0) { 968219089Spjd if (flags & DSL_PROP_GET_LOCAL) 969219089Spjd continue; 970219089Spjd 971219089Spjd (void) strncpy(buf, za.za_name, (suffix - za.za_name)); 972219089Spjd buf[suffix - za.za_name] = '\0'; 973219089Spjd propname = buf; 974219089Spjd 975219089Spjd if (!(flags & DSL_PROP_GET_RECEIVED)) { 976219089Spjd /* Skip if locally overridden. */ 977219089Spjd err = zap_contains(mos, propobj, propname); 978219089Spjd if (err == 0) 979219089Spjd continue; 980219089Spjd if (err != ENOENT) 981219089Spjd break; 982219089Spjd 983219089Spjd /* Skip if explicitly inherited. */ 984219089Spjd valstr = kmem_asprintf("%s%s", propname, 985219089Spjd ZPROP_INHERIT_SUFFIX); 986219089Spjd err = zap_contains(mos, propobj, valstr); 987219089Spjd strfree(valstr); 988219089Spjd if (err == 0) 989219089Spjd continue; 990219089Spjd if (err != ENOENT) 991219089Spjd break; 992219089Spjd } 993219089Spjd 994219089Spjd source = ((flags & DSL_PROP_GET_INHERITING) ? 995219089Spjd setpoint : ZPROP_SOURCE_VAL_RECVD); 996219089Spjd } else { 997219089Spjd /* 998219089Spjd * For backward compatibility, skip suffixes we don't 999219089Spjd * recognize. 1000219089Spjd */ 1001219089Spjd continue; 1002219089Spjd } 1003219089Spjd 1004219089Spjd prop = zfs_name_to_prop(propname); 1005219089Spjd 1006219089Spjd /* Skip non-inheritable properties. */ 1007219089Spjd if ((flags & DSL_PROP_GET_INHERITING) && prop != ZPROP_INVAL && 1008219089Spjd !zfs_prop_inheritable(prop)) 1009219089Spjd continue; 1010219089Spjd 1011219089Spjd /* Skip properties not valid for this type. */ 1012219089Spjd if ((flags & DSL_PROP_GET_SNAPSHOT) && prop != ZPROP_INVAL && 1013219089Spjd !zfs_prop_valid_for_type(prop, ZFS_TYPE_SNAPSHOT)) 1014219089Spjd continue; 1015219089Spjd 1016219089Spjd /* Skip properties already defined. */ 1017219089Spjd if (nvlist_exists(nv, propname)) 1018219089Spjd continue; 1019219089Spjd 1020219089Spjd VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0); 1021219089Spjd if (za.za_integer_length == 1) { 1022219089Spjd /* 1023219089Spjd * String property 1024219089Spjd */ 1025219089Spjd char *tmp = kmem_alloc(za.za_num_integers, 1026219089Spjd KM_SLEEP); 1027219089Spjd err = zap_lookup(mos, propobj, 1028219089Spjd za.za_name, 1, za.za_num_integers, tmp); 1029219089Spjd if (err != 0) { 1030219089Spjd kmem_free(tmp, za.za_num_integers); 1031219089Spjd break; 1032219089Spjd } 1033219089Spjd VERIFY(nvlist_add_string(propval, ZPROP_VALUE, 1034219089Spjd tmp) == 0); 1035219089Spjd kmem_free(tmp, za.za_num_integers); 1036219089Spjd } else { 1037219089Spjd /* 1038219089Spjd * Integer property 1039219089Spjd */ 1040219089Spjd ASSERT(za.za_integer_length == 8); 1041219089Spjd (void) nvlist_add_uint64(propval, ZPROP_VALUE, 1042219089Spjd za.za_first_integer); 1043219089Spjd } 1044219089Spjd 1045219089Spjd VERIFY(nvlist_add_string(propval, ZPROP_SOURCE, source) == 0); 1046219089Spjd VERIFY(nvlist_add_nvlist(nv, propname, propval) == 0); 1047219089Spjd nvlist_free(propval); 1048219089Spjd } 1049219089Spjd zap_cursor_fini(&zc); 1050219089Spjd if (err == ENOENT) 1051219089Spjd err = 0; 1052219089Spjd return (err); 1053219089Spjd} 1054219089Spjd 1055168404Spjd/* 1056168404Spjd * Iterate over all properties for this dataset and return them in an nvlist. 1057168404Spjd */ 1058219089Spjdstatic int 1059219089Spjddsl_prop_get_all_ds(dsl_dataset_t *ds, nvlist_t **nvp, 1060219089Spjd dsl_prop_getflags_t flags) 1061168404Spjd{ 1062168404Spjd dsl_dir_t *dd = ds->ds_dir; 1063185029Spjd dsl_pool_t *dp = dd->dd_pool; 1064185029Spjd objset_t *mos = dp->dp_meta_objset; 1065219089Spjd int err = 0; 1066307108Smav char setpoint[ZFS_MAX_DATASET_NAME_LEN]; 1067168404Spjd 1068168404Spjd VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 1069168404Spjd 1070286575Smav if (ds->ds_is_snapshot) 1071219089Spjd flags |= DSL_PROP_GET_SNAPSHOT; 1072168404Spjd 1073248571Smm ASSERT(dsl_pool_config_held(dp)); 1074168404Spjd 1075275782Sdelphij if (dsl_dataset_phys(ds)->ds_props_obj != 0) { 1076219089Spjd ASSERT(flags & DSL_PROP_GET_SNAPSHOT); 1077219089Spjd dsl_dataset_name(ds, setpoint); 1078275782Sdelphij err = dsl_prop_get_all_impl(mos, 1079275782Sdelphij dsl_dataset_phys(ds)->ds_props_obj, setpoint, flags, *nvp); 1080219089Spjd if (err) 1081219089Spjd goto out; 1082219089Spjd } 1083219089Spjd 1084219089Spjd for (; dd != NULL; dd = dd->dd_parent) { 1085219089Spjd if (dd != ds->ds_dir || (flags & DSL_PROP_GET_SNAPSHOT)) { 1086219089Spjd if (flags & (DSL_PROP_GET_LOCAL | 1087219089Spjd DSL_PROP_GET_RECEIVED)) 1088219089Spjd break; 1089219089Spjd flags |= DSL_PROP_GET_INHERITING; 1090185029Spjd } 1091219089Spjd dsl_dir_name(dd, setpoint); 1092275782Sdelphij err = dsl_prop_get_all_impl(mos, 1093275782Sdelphij dsl_dir_phys(dd)->dd_props_zapobj, setpoint, flags, *nvp); 1094219089Spjd if (err) 1095219089Spjd break; 1096219089Spjd } 1097219089Spjdout: 1098219089Spjd return (err); 1099219089Spjd} 1100168404Spjd 1101219089Spjdboolean_t 1102248571Smmdsl_prop_get_hasrecvd(const char *dsname) 1103219089Spjd{ 1104219089Spjd uint64_t dummy; 1105185029Spjd 1106248571Smm return (0 == 1107248571Smm dsl_prop_get_integer(dsname, ZPROP_HAS_RECVD, &dummy, NULL)); 1108219089Spjd} 1109168404Spjd 1110248571Smmstatic int 1111248571Smmdsl_prop_set_hasrecvd_impl(const char *dsname, zprop_source_t source) 1112219089Spjd{ 1113248571Smm uint64_t version; 1114248571Smm spa_t *spa; 1115248571Smm int error = 0; 1116185029Spjd 1117248571Smm VERIFY0(spa_open(dsname, &spa, FTAG)); 1118248571Smm version = spa_version(spa); 1119248571Smm spa_close(spa, FTAG); 1120168404Spjd 1121248571Smm if (version >= SPA_VERSION_RECVD_PROPS) 1122248571Smm error = dsl_prop_set_int(dsname, ZPROP_HAS_RECVD, source, 0); 1123248571Smm return (error); 1124219089Spjd} 1125168404Spjd 1126219089Spjd/* 1127219089Spjd * Call after successfully receiving properties to ensure that only the first 1128219089Spjd * receive on or after SPA_VERSION_RECVD_PROPS blows away local properties. 1129219089Spjd */ 1130248571Smmint 1131248571Smmdsl_prop_set_hasrecvd(const char *dsname) 1132219089Spjd{ 1133248571Smm int error = 0; 1134248571Smm if (!dsl_prop_get_hasrecvd(dsname)) 1135248571Smm error = dsl_prop_set_hasrecvd_impl(dsname, ZPROP_SRC_LOCAL); 1136248571Smm return (error); 1137219089Spjd} 1138168404Spjd 1139219089Spjdvoid 1140248571Smmdsl_prop_unset_hasrecvd(const char *dsname) 1141219089Spjd{ 1142248571Smm VERIFY0(dsl_prop_set_hasrecvd_impl(dsname, ZPROP_SRC_NONE)); 1143168404Spjd} 1144168404Spjd 1145219089Spjdint 1146219089Spjddsl_prop_get_all(objset_t *os, nvlist_t **nvp) 1147219089Spjd{ 1148219089Spjd return (dsl_prop_get_all_ds(os->os_dsl_dataset, nvp, 0)); 1149219089Spjd} 1150219089Spjd 1151219089Spjdint 1152248571Smmdsl_prop_get_received(const char *dsname, nvlist_t **nvp) 1153219089Spjd{ 1154248571Smm objset_t *os; 1155248571Smm int error; 1156248571Smm 1157219089Spjd /* 1158219089Spjd * Received properties are not distinguishable from local properties 1159219089Spjd * until the dataset has received properties on or after 1160219089Spjd * SPA_VERSION_RECVD_PROPS. 1161219089Spjd */ 1162248571Smm dsl_prop_getflags_t flags = (dsl_prop_get_hasrecvd(dsname) ? 1163219089Spjd DSL_PROP_GET_RECEIVED : DSL_PROP_GET_LOCAL); 1164248571Smm 1165248571Smm error = dmu_objset_hold(dsname, FTAG, &os); 1166248571Smm if (error != 0) 1167248571Smm return (error); 1168248571Smm error = dsl_prop_get_all_ds(os->os_dsl_dataset, nvp, flags); 1169248571Smm dmu_objset_rele(os, FTAG); 1170248571Smm return (error); 1171219089Spjd} 1172219089Spjd 1173168404Spjdvoid 1174168404Spjddsl_prop_nvlist_add_uint64(nvlist_t *nv, zfs_prop_t prop, uint64_t value) 1175168404Spjd{ 1176168404Spjd nvlist_t *propval; 1177219089Spjd const char *propname = zfs_prop_to_name(prop); 1178219089Spjd uint64_t default_value; 1179168404Spjd 1180219089Spjd if (nvlist_lookup_nvlist(nv, propname, &propval) == 0) { 1181219089Spjd VERIFY(nvlist_add_uint64(propval, ZPROP_VALUE, value) == 0); 1182219089Spjd return; 1183219089Spjd } 1184219089Spjd 1185168404Spjd VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0); 1186185029Spjd VERIFY(nvlist_add_uint64(propval, ZPROP_VALUE, value) == 0); 1187219089Spjd /* Indicate the default source if we can. */ 1188297507Smav if (dodefault(prop, 8, 1, &default_value) == 0 && 1189219089Spjd value == default_value) { 1190219089Spjd VERIFY(nvlist_add_string(propval, ZPROP_SOURCE, "") == 0); 1191219089Spjd } 1192219089Spjd VERIFY(nvlist_add_nvlist(nv, propname, propval) == 0); 1193168404Spjd nvlist_free(propval); 1194168404Spjd} 1195168404Spjd 1196168404Spjdvoid 1197168404Spjddsl_prop_nvlist_add_string(nvlist_t *nv, zfs_prop_t prop, const char *value) 1198168404Spjd{ 1199168404Spjd nvlist_t *propval; 1200219089Spjd const char *propname = zfs_prop_to_name(prop); 1201168404Spjd 1202219089Spjd if (nvlist_lookup_nvlist(nv, propname, &propval) == 0) { 1203219089Spjd VERIFY(nvlist_add_string(propval, ZPROP_VALUE, value) == 0); 1204219089Spjd return; 1205219089Spjd } 1206219089Spjd 1207168404Spjd VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0); 1208185029Spjd VERIFY(nvlist_add_string(propval, ZPROP_VALUE, value) == 0); 1209219089Spjd VERIFY(nvlist_add_nvlist(nv, propname, propval) == 0); 1210168404Spjd nvlist_free(propval); 1211168404Spjd} 1212