Mercurial > illumos > illumos-gate
diff usr/src/uts/common/fs/zfs/zfs_ioctl.c @ 5498:334b476844ca
6622831 normalization properties are not preserved by "zfs send"
6625194 delegate 'clone' privilege broken
6626152 zfs should initialize the fuid lock it uses
Contributed by Pawel Dawidek
author | timh |
---|---|
date | Tue, 20 Nov 2007 05:42:50 -0800 |
parents | b25030891c44 |
children | 029cc4273b57 |
line wrap: on
line diff
--- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c Mon Nov 19 21:07:12 2007 -0800 +++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c Tue Nov 20 05:42:50 2007 -0800 @@ -38,7 +38,6 @@ #include <sys/cmn_err.h> #include <sys/stat.h> #include <sys/zfs_ioctl.h> -#include <sys/zfs_i18n.h> #include <sys/zfs_znode.h> #include <sys/zap.h> #include <sys/spa.h> @@ -65,8 +64,6 @@ #include <sys/zfs_dir.h> #include <sys/zvol.h> #include <sharefs/share.h> -#include <sys/zfs_znode.h> -#include <sys/zfs_vfsops.h> #include <sys/dmu_objset.h> #include "zfs_namecheck.h" @@ -1095,6 +1092,30 @@ return (error); } +static int +zfs_os_open_retry(char *name, objset_t **os) +{ + int error; + +retry: + error = dmu_objset_open(name, DMU_OST_ANY, + DS_MODE_STANDARD | DS_MODE_READONLY, os); + if (error != 0) { + /* + * This is ugly: dmu_objset_open() can return EBUSY if + * the objset is held exclusively. Fortunately this hold is + * only for a short while, so we retry here. + * This avoids user code having to handle EBUSY, + * for example for a "zfs list". + */ + if (error == EBUSY) { + delay(1); + goto retry; + } + } + return (error); +} + /* * inputs: * zc_name name of filesystem @@ -1113,23 +1134,8 @@ int error; nvlist_t *nv; -retry: - error = dmu_objset_open(zc->zc_name, DMU_OST_ANY, - DS_MODE_STANDARD | DS_MODE_READONLY, &os); - if (error != 0) { - /* - * This is ugly: dmu_objset_open() can return EBUSY if - * the objset is held exclusively. Fortunately this hold is - * only for a short while, so we retry here. - * This avoids user code having to handle EBUSY, - * for example for a "zfs list". - */ - if (error == EBUSY) { - delay(1); - goto retry; - } + if ((error = zfs_os_open_retry(zc->zc_name, &os)) != 0) return (error); - } dmu_objset_fast_stat(os, &zc->zc_objset_stats); @@ -1156,6 +1162,66 @@ return (error); } +static int +nvl_add_zplprop(objset_t *os, nvlist_t *props, zfs_prop_t prop) +{ + uint64_t value; + int error; + + /* + * zfs_get_zplprop() will either find a value or give us + * the default value (if there is one). + */ + if ((error = zfs_get_zplprop(os, prop, &value)) != 0) + return (error); + VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(prop), value) == 0); + return (0); +} + +/* + * inputs: + * zc_name name of filesystem + * zc_nvlist_dst_size size of buffer for zpl property nvlist + * + * outputs: + * zc_nvlist_dst zpl property nvlist + * zc_nvlist_dst_size size of zpl property nvlist + */ +static int +zfs_ioc_objset_zplprops(zfs_cmd_t *zc) +{ + objset_t *os; + int err; + + if ((err = zfs_os_open_retry(zc->zc_name, &os)) != 0) + return (err); + + dmu_objset_fast_stat(os, &zc->zc_objset_stats); + + /* + * NB: nvl_add_zplprop() will read the objset contents, + * which we aren't supposed to do with a DS_MODE_STANDARD + * open, because it could be inconsistent. + */ + if (zc->zc_nvlist_dst != NULL && + !zc->zc_objset_stats.dds_inconsistent && + dmu_objset_type(os) == DMU_OST_ZFS) { + nvlist_t *nv; + + VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0); + if ((err = nvl_add_zplprop(os, nv, ZFS_PROP_VERSION)) == 0 && + (err = nvl_add_zplprop(os, nv, ZFS_PROP_NORMALIZE)) == 0 && + (err = nvl_add_zplprop(os, nv, ZFS_PROP_UTF8ONLY)) == 0 && + (err = nvl_add_zplprop(os, nv, ZFS_PROP_CASE)) == 0) + err = put_nvlist(zc, nv); + nvlist_free(nv); + } else { + err = ENOENT; + } + dmu_objset_close(os); + return (err); +} + /* * inputs: * zc_name name of filesystem @@ -1170,68 +1236,13 @@ * zc_value alternate root */ static int -zfs_ioc_objset_version(zfs_cmd_t *zc) -{ - objset_t *os = NULL; - int error; - -retry: - error = dmu_objset_open(zc->zc_name, DMU_OST_ANY, - DS_MODE_STANDARD | DS_MODE_READONLY, &os); - if (error != 0) { - /* - * This is ugly: dmu_objset_open() can return EBUSY if - * the objset is held exclusively. Fortunately this hold is - * only for a short while, so we retry here. - * This avoids user code having to handle EBUSY, - * for example for a "zfs list". - */ - if (error == EBUSY) { - delay(1); - goto retry; - } - return (error); - } - - dmu_objset_fast_stat(os, &zc->zc_objset_stats); - - /* - * NB: zfs_get_version() will read the objset contents, - * which we aren't supposed to do with a - * DS_MODE_STANDARD open, because it could be - * inconsistent. So this is a bit of a workaround... - */ - zc->zc_cookie = 0; - if (!zc->zc_objset_stats.dds_inconsistent) - if (dmu_objset_type(os) == DMU_OST_ZFS) - (void) zfs_get_version(os, &zc->zc_cookie); - - dmu_objset_close(os); - return (0); -} - -static int zfs_ioc_dataset_list_next(zfs_cmd_t *zc) { objset_t *os; int error; char *p; -retry: - error = dmu_objset_open(zc->zc_name, DMU_OST_ANY, - DS_MODE_STANDARD | DS_MODE_READONLY, &os); - if (error != 0) { - /* - * This is ugly: dmu_objset_open() can return EBUSY if - * the objset is held exclusively. Fortunately this hold is - * only for a short while, so we retry here. - * This avoids user code having to handle EBUSY, - * for example for a "zfs list". - */ - if (error == EBUSY) { - delay(1); - goto retry; - } + if ((error = zfs_os_open_retry(zc->zc_name, &os)) != 0) { if (error == ENOENT) error = ESRCH; return (error); @@ -1281,21 +1292,7 @@ objset_t *os; int error; -retry: - error = dmu_objset_open(zc->zc_name, DMU_OST_ANY, - DS_MODE_STANDARD | DS_MODE_READONLY, &os); - if (error != 0) { - /* - * This is ugly: dmu_objset_open() can return EBUSY if - * the objset is held exclusively. Fortunately this hold is - * only for a short while, so we retry here. - * This avoids user code having to handle EBUSY, - * for example for a "zfs list". - */ - if (error == EBUSY) { - delay(1); - goto retry; - } + if ((error = zfs_os_open_retry(zc->zc_name, &os)) != 0) { if (error == ENOENT) error = ESRCH; return (error); @@ -1385,12 +1382,6 @@ if (zfs_check_version(name, SPA_VERSION_DITTO_BLOCKS)) return (ENOTSUP); break; - case ZFS_PROP_NORMALIZE: - case ZFS_PROP_UTF8ONLY: - case ZFS_PROP_CASE: - if (zfs_check_version(name, SPA_VERSION_NORMALIZATION)) - return (ENOTSUP); - } if ((error = zfs_secpolicy_setprop(name, prop, CRED())) != 0) return (error); @@ -1759,172 +1750,112 @@ zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx) { zfs_creat_t *zct = arg; - uint64_t version; - - if (spa_version(dmu_objset_spa(os)) >= SPA_VERSION_FUID) - version = ZPL_VERSION; - else - version = ZPL_VERSION_FUID - 1; - - (void) nvlist_lookup_uint64(zct->zct_props, - zfs_prop_to_name(ZFS_PROP_VERSION), &version); - - zfs_create_fs(os, cr, version, zct->zct_norm, tx); + + zfs_create_fs(os, cr, zct->zct_zplprops, tx); } +#define ZFS_PROP_UNDEFINED ((uint64_t)-1) + /* - * zfs_prop_lookup() + * inputs: + * createprops list of properties requested by creator + * dataset name of dataset we are creating * - * Look for the property first in the existing property nvlist. If - * it's already present, you're done. If it's not there, attempt to - * find the property value from a parent dataset. If that fails, fall - * back to the property's default value. In either of these two - * cases, if update is TRUE, add a value for the property to the - * property nvlist. - * - * If the rval pointer is non-NULL, copy the discovered value to rval. - * - * If we get any unexpected errors, bail and return the error number - * to the caller. + * outputs: + * zplprops values for the zplprops we attach to the master node object * - * If we succeed, return 0. - */ -static int -zfs_prop_lookup(const char *parentname, zfs_prop_t propnum, - nvlist_t *proplist, uint64_t *rval, boolean_t update) -{ - const char *propname; - uint64_t value; - int error = ENOENT; - - propname = zfs_prop_to_name(propnum); - if (proplist != NULL) - error = nvlist_lookup_uint64(proplist, propname, &value); - if (error == ENOENT) { - error = dsl_prop_get_integer(parentname, propname, - &value, NULL); - if (error == ENOENT) - value = zfs_prop_default_numeric(propnum); - else if (error != 0) - return (error); - if (update) { - ASSERT(proplist != NULL); - error = nvlist_add_uint64(proplist, propname, value); - } - } - if (error == 0 && rval) - *rval = value; - return (error); -} - -/* - * zfs_normalization_get - * - * Get the normalization flag value. If the properties have - * non-default values, make sure the pool version is recent enough to - * support these choices. + * Determine the settings for utf8only, normalization and + * casesensitivity. Specific values may have been requested by the + * creator and/or we can inherit values from the parent dataset. If + * the file system is of too early a vintage, a creator can not + * request settings for these properties, even if the requested + * setting is the default value. We don't actually want to create dsl + * properties for these, so remove them from the source nvlist after + * processing. */ static int -zfs_normalization_get(const char *dataset, nvlist_t *proplist, int *norm, - boolean_t update) +zfs_fill_zplprops(const char *dataset, nvlist_t *createprops, + nvlist_t *zplprops, uint64_t zplver) { + objset_t *os; char parentname[MAXNAMELEN]; - char poolname[MAXNAMELEN]; char *cp; - uint64_t value; - int check = 0; - int error; - - ASSERT(norm != NULL); - *norm = 0; - - (void) strncpy(parentname, dataset, sizeof (parentname)); - cp = strrchr(parentname, '@'); - if (cp != NULL) { - cp[0] = '\0'; - } else { - cp = strrchr(parentname, '/'); - if (cp == NULL) - return (ENOENT); - cp[0] = '\0'; - } - - (void) strncpy(poolname, dataset, sizeof (poolname)); - cp = strchr(poolname, '/'); - if (cp != NULL) - cp[0] = '\0'; + uint64_t sense = ZFS_PROP_UNDEFINED; + uint64_t norm = ZFS_PROP_UNDEFINED; + uint64_t u8 = ZFS_PROP_UNDEFINED; + int error = 0; + + ASSERT(zplprops != NULL); + + (void) strlcpy(parentname, dataset, sizeof (parentname)); + cp = strrchr(parentname, '/'); + ASSERT(cp != NULL); + cp[0] = '\0'; /* - * Make sure pool is of new enough vintage to support normalization. + * Pull out creator prop choices, if any. */ - if (zfs_check_version(poolname, SPA_VERSION_NORMALIZATION)) - return (0); - - error = zfs_prop_lookup(parentname, ZFS_PROP_UTF8ONLY, - proplist, &value, update); - if (error != 0) - return (error); - if (value != zfs_prop_default_numeric(ZFS_PROP_UTF8ONLY)) - check = 1; - - error = zfs_prop_lookup(parentname, ZFS_PROP_NORMALIZE, - proplist, &value, update); - if (error != 0) - return (error); - if (value != zfs_prop_default_numeric(ZFS_PROP_NORMALIZE)) { - check = 1; - switch ((int)value) { - case ZFS_NORMALIZE_NONE: - break; - case ZFS_NORMALIZE_C: - *norm |= U8_TEXTPREP_NFC; - break; - case ZFS_NORMALIZE_D: - *norm |= U8_TEXTPREP_NFD; - break; - case ZFS_NORMALIZE_KC: - *norm |= U8_TEXTPREP_NFKC; - break; - case ZFS_NORMALIZE_KD: - *norm |= U8_TEXTPREP_NFKD; - break; - default: - ASSERT((int)value >= ZFS_NORMALIZE_NONE); - ASSERT((int)value <= ZFS_NORMALIZE_KD); - break; - } - } - - error = zfs_prop_lookup(parentname, ZFS_PROP_CASE, - proplist, &value, update); - if (error != 0) - return (error); - if (value != zfs_prop_default_numeric(ZFS_PROP_CASE)) { - check = 1; - switch ((int)value) { - case ZFS_CASE_SENSITIVE: - break; - case ZFS_CASE_INSENSITIVE: - *norm |= U8_TEXTPREP_TOUPPER; - break; - case ZFS_CASE_MIXED: - *norm |= U8_TEXTPREP_TOUPPER; - break; - default: - ASSERT((int)value >= ZFS_CASE_SENSITIVE); - ASSERT((int)value <= ZFS_CASE_MIXED); - break; - } + if (createprops) { + (void) nvlist_lookup_uint64(createprops, + zfs_prop_to_name(ZFS_PROP_NORMALIZE), &norm); + (void) nvlist_remove_all(createprops, + zfs_prop_to_name(ZFS_PROP_NORMALIZE)); + (void) nvlist_lookup_uint64(createprops, + zfs_prop_to_name(ZFS_PROP_UTF8ONLY), &u8); + (void) nvlist_remove_all(createprops, + zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); + (void) nvlist_lookup_uint64(createprops, + zfs_prop_to_name(ZFS_PROP_CASE), &sense); + (void) nvlist_remove_all(createprops, + zfs_prop_to_name(ZFS_PROP_CASE)); } /* - * At the moment we are disabling non-default values for these - * properties because they cannot be preserved properly with a - * zfs send. + * If the file system or pool is version is too "young" to + * support normalization and the creator tried to set a value + * for one of the props, error out. We only need check the + * ZPL version because we've already checked by now that the + * SPA version is compatible with the selected ZPL version. + */ + if (zplver < ZPL_VERSION_NORMALIZATION && + (norm != ZFS_PROP_UNDEFINED || u8 != ZFS_PROP_UNDEFINED || + sense != ZFS_PROP_UNDEFINED)) + return (ENOTSUP); + + /* + * Put the version in the zplprops + */ + VERIFY(nvlist_add_uint64(zplprops, + zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0); + + /* + * Open parent object set so we can inherit zplprop values if + * necessary. */ - if (check == 1) - return (ENOTSUP); - + if ((error = zfs_os_open_retry(parentname, &os)) != 0) + return (error); + + if (norm == ZFS_PROP_UNDEFINED) + VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0); + VERIFY(nvlist_add_uint64(zplprops, + zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0); + + /* + * If we're normalizing, names must always be valid UTF-8 strings. + */ + if (norm) + u8 = 1; + if (u8 == ZFS_PROP_UNDEFINED) + VERIFY(zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8) == 0); + VERIFY(nvlist_add_uint64(zplprops, + zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0); + + if (sense == ZFS_PROP_UNDEFINED) + VERIFY(zfs_get_zplprop(os, ZFS_PROP_CASE, &sense) == 0); + VERIFY(nvlist_add_uint64(zplprops, + zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0); + + dmu_objset_close(os); return (0); } @@ -1935,7 +1866,7 @@ * zc_value name of snapshot to clone from (may be empty) * zc_nvlist_src{_size} nvlist of properties to apply * - * outputs: none + * outputs: none */ static int zfs_ioc_create(zfs_cmd_t *zc) @@ -1969,7 +1900,7 @@ &nvprops)) != 0) return (error); - zct.zct_norm = 0; + zct.zct_zplprops = NULL; zct.zct_props = nvprops; if (zc->zc_value[0] != '\0') { @@ -1994,29 +1925,6 @@ nvlist_free(nvprops); return (error); } - /* - * If caller did not provide any properties, allocate - * an nvlist for properties, as we will be adding our set-once - * properties to it. This carries the choices made on the - * original file system into the clone. - */ - if (nvprops == NULL) - VERIFY(nvlist_alloc(&nvprops, - NV_UNIQUE_NAME, KM_SLEEP) == 0); - - /* - * We have to have normalization and case-folding - * flags correct when we do the file system creation, - * so go figure them out now. All we really care about - * here is getting these values into the property list. - */ - error = zfs_normalization_get(zc->zc_value, nvprops, - &zct.zct_norm, B_TRUE); - if (error != 0) { - dmu_objset_close(clone); - nvlist_free(nvprops); - return (error); - } dmu_objset_close(clone); } else { if (cbfunc == NULL) { @@ -2057,15 +1965,29 @@ uint64_t version; int error; - error = nvlist_lookup_uint64(nvprops, + /* + * Default ZPL version to non-FUID capable if the + * pool is not upgraded to support FUIDs. + */ + if (zfs_check_version(zc->zc_name, SPA_VERSION_FUID)) + version = ZPL_VERSION_FUID - 1; + else + version = ZPL_VERSION; + + /* + * Potentially override default ZPL version based + * on creator's request. + */ + (void) nvlist_lookup_uint64(nvprops, zfs_prop_to_name(ZFS_PROP_VERSION), &version); - if (error == 0 && (version < ZPL_VERSION_INITIAL || - version > ZPL_VERSION)) { - nvlist_free(nvprops); - return (ENOTSUP); - } else if (error == 0 && version >= ZPL_VERSION_FUID && - zfs_check_version(zc->zc_name, SPA_VERSION_FUID)) { + /* + * Make sure version we ended up with is kosher + */ + if ((version < ZPL_VERSION_INITIAL || + version > ZPL_VERSION) || + (version >= ZPL_VERSION_FUID && + zfs_check_version(zc->zc_name, SPA_VERSION_FUID))) { nvlist_free(nvprops); return (ENOTSUP); } @@ -2074,19 +1996,21 @@ * We have to have normalization and * case-folding flags correct when we do the * file system creation, so go figure them out - * now. The final argument to zfs_normalization_get() - * tells that routine not to update the nvprops - * list. + * now. */ - error = zfs_normalization_get(zc->zc_name, nvprops, - &zct.zct_norm, B_FALSE); + VERIFY(nvlist_alloc(&zct.zct_zplprops, + NV_UNIQUE_NAME, KM_SLEEP) == 0); + error = zfs_fill_zplprops(zc->zc_name, nvprops, + zct.zct_zplprops, version); if (error != 0) { nvlist_free(nvprops); + nvlist_free(zct.zct_zplprops); return (error); } } error = dmu_objset_create(zc->zc_name, type, NULL, cbfunc, &zct); + nvlist_free(zct.zct_zplprops); } /* @@ -2793,7 +2717,7 @@ { zfs_ioc_vdev_detach, zfs_secpolicy_config, POOL_NAME, B_TRUE }, { zfs_ioc_vdev_setpath, zfs_secpolicy_config, POOL_NAME, B_FALSE }, { zfs_ioc_objset_stats, zfs_secpolicy_read, DATASET_NAME, B_FALSE }, - { zfs_ioc_objset_version, zfs_secpolicy_read, DATASET_NAME, B_FALSE }, + { zfs_ioc_objset_zplprops, zfs_secpolicy_read, DATASET_NAME, B_FALSE }, { zfs_ioc_dataset_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE }, { zfs_ioc_snapshot_list_next, zfs_secpolicy_read,