git: caed7b1c399d - main - zfs: merge OpenZFS master-bedbc13da

Martin Matuska mm at FreeBSD.org
Wed Mar 3 01:16:24 UTC 2021


The branch main has been updated by mm:

URL: https://cgit.FreeBSD.org/src/commit/?id=caed7b1c399de04279822028e15b36367e84232f

commit caed7b1c399de04279822028e15b36367e84232f
Merge: df3747c6607b 154ce66101db
Author:     Martin Matuska <mm at FreeBSD.org>
AuthorDate: 2021-03-03 01:15:33 +0000
Commit:     Martin Matuska <mm at FreeBSD.org>
CommitDate: 2021-03-03 01:15:33 +0000

    zfs: merge OpenZFS master-bedbc13da
    
    Notable upstream commits:
      8e43fa12c Fix vdev_rebuild_thread deadlock
      03ef8f09e Add missing checks for unsupported features
      2e160dee9 Fix assert in FreeBSD-specific dmu_read_pages
      bedbc13da Cancel TRIM / initialize on FAULTED non-writeable vdevs
    
    MFC after:      1 week
    Obtained from:  OpenZFS

 sys/contrib/openzfs/cmd/vdev_id/vdev_id            | 12 ++-
 sys/contrib/openzfs/cmd/zpool/Makefile.am          |  2 +-
 sys/contrib/openzfs/config/kernel-bio.m4           | 29 ++++++++
 .../openzfs/config/kernel-generic_io_acct.m4       | 69 +++++++++++------
 sys/contrib/openzfs/configure.ac                   |  1 +
 .../include/os/linux/kernel/linux/blkdev_compat.h  |  8 +-
 sys/contrib/openzfs/include/sys/dsl_synctask.h     |  9 ++-
 sys/contrib/openzfs/lib/libzfs/libzfs_sendrecv.c   |  9 +++
 .../openzfs/man/man5/zfs-module-parameters.5       |  2 +-
 sys/contrib/openzfs/module/os/freebsd/zfs/dmu_os.c |  2 +-
 .../openzfs/module/os/linux/zfs/vdev_disk.c        |  4 +
 .../openzfs/module/os/linux/zfs/zfs_ioctl_os.c     |  2 +-
 sys/contrib/openzfs/module/os/linux/zfs/zvol_os.c  |  4 +
 sys/contrib/openzfs/module/zfs/spa_misc.c          | 31 +++++---
 sys/contrib/openzfs/module/zfs/vdev_initialize.c   | 10 ++-
 sys/contrib/openzfs/module/zfs/vdev_rebuild.c      |  2 +-
 sys/contrib/openzfs/module/zfs/vdev_trim.c         | 15 +++-
 sys/contrib/openzfs/module/zfs/zfs_ioctl.c         |  4 +-
 sys/contrib/openzfs/tests/runfiles/common.run      |  4 +-
 .../openzfs/tests/zfs-tests/cmd/Makefile.am        |  1 +
 .../tests/zfs-tests/cmd/send_doall/.gitignore      |  1 +
 .../tests/zfs-tests/cmd/send_doall/Makefile.am     | 11 +++
 .../tests/zfs-tests/cmd/send_doall/send_doall.c    | 87 ++++++++++++++++++++++
 .../openzfs/tests/zfs-tests/include/commands.cfg   |  1 +
 .../cli_root/zpool_initialize/Makefile.am          |  1 +
 ...zpool_initialize_fault_export_import_online.ksh | 59 +++++++++++++++
 .../functional/cli_root/zpool_trim/Makefile.am     |  1 +
 .../zpool_trim_fault_export_import_online.ksh      | 62 +++++++++++++++
 .../zpool_trim/zpool_trim_start_and_cancel_pos.ksh | 22 +++---
 .../zfs-tests/tests/functional/rsend/Makefile.am   |  4 +-
 .../tests/functional/rsend/send_doall.ksh          | 67 +++++++++++++++++
 31 files changed, 467 insertions(+), 69 deletions(-)

diff --cc sys/contrib/openzfs/module/zfs/zfs_ioctl.c
index 922253469fba,000000000000..5f291d067bef
mode 100644,000000..100644
--- a/sys/contrib/openzfs/module/zfs/zfs_ioctl.c
+++ b/sys/contrib/openzfs/module/zfs/zfs_ioctl.c
@@@ -1,7688 -1,0 +1,7688 @@@
 +/*
 + * CDDL HEADER START
 + *
 + * The contents of this file are subject to the terms of the
 + * Common Development and Distribution License (the "License").
 + * You may not use this file except in compliance with the License.
 + *
 + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 + * or http://www.opensolaris.org/os/licensing.
 + * See the License for the specific language governing permissions
 + * and limitations under the License.
 + *
 + * When distributing Covered Code, include this CDDL HEADER in each
 + * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 + * If applicable, add the following below this CDDL HEADER, with the
 + * fields enclosed by brackets "[]" replaced with your own identifying
 + * information: Portions Copyright [yyyy] [name of copyright owner]
 + *
 + * CDDL HEADER END
 + */
 +
 +/*
 + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
 + * Portions Copyright 2011 Martin Matuska
 + * Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
 + * Portions Copyright 2012 Pawel Jakub Dawidek <pawel at dawidek.net>
 + * Copyright (c) 2014, 2016 Joyent, Inc. All rights reserved.
 + * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
 + * Copyright (c) 2014, Joyent, Inc. All rights reserved.
 + * Copyright (c) 2011, 2020 by Delphix. All rights reserved.
 + * Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
 + * Copyright (c) 2013 Steven Hartland. All rights reserved.
 + * Copyright (c) 2014 Integros [integros.com]
 + * Copyright 2016 Toomas Soome <tsoome at me.com>
 + * Copyright (c) 2016 Actifio, Inc. All rights reserved.
 + * Copyright (c) 2018, loli10K <ezomori.nozomu at gmail.com>. All rights reserved.
 + * Copyright 2017 RackTop Systems.
 + * Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
 + * Copyright (c) 2019 Datto Inc.
 + * Copyright (c) 2019, 2020 by Christian Schwarz. All rights reserved.
 + * Copyright (c) 2019, Klara Inc.
 + * Copyright (c) 2019, Allan Jude
 + */
 +
 +/*
 + * ZFS ioctls.
 + *
 + * This file handles the ioctls to /dev/zfs, used for configuring ZFS storage
 + * pools and filesystems, e.g. with /sbin/zfs and /sbin/zpool.
 + *
 + * There are two ways that we handle ioctls: the legacy way where almost
 + * all of the logic is in the ioctl callback, and the new way where most
 + * of the marshalling is handled in the common entry point, zfsdev_ioctl().
 + *
 + * Non-legacy ioctls should be registered by calling
 + * zfs_ioctl_register() from zfs_ioctl_init().  The ioctl is invoked
 + * from userland by lzc_ioctl().
 + *
 + * The registration arguments are as follows:
 + *
 + * const char *name
 + *   The name of the ioctl.  This is used for history logging.  If the
 + *   ioctl returns successfully (the callback returns 0), and allow_log
 + *   is true, then a history log entry will be recorded with the input &
 + *   output nvlists.  The log entry can be printed with "zpool history -i".
 + *
 + * zfs_ioc_t ioc
 + *   The ioctl request number, which userland will pass to ioctl(2).
 + *   We want newer versions of libzfs and libzfs_core to run against
 + *   existing zfs kernel modules (i.e. a deferred reboot after an update).
 + *   Therefore the ioctl numbers cannot change from release to release.
 + *
 + * zfs_secpolicy_func_t *secpolicy
 + *   This function will be called before the zfs_ioc_func_t, to
 + *   determine if this operation is permitted.  It should return EPERM
 + *   on failure, and 0 on success.  Checks include determining if the
 + *   dataset is visible in this zone, and if the user has either all
 + *   zfs privileges in the zone (SYS_MOUNT), or has been granted permission
 + *   to do this operation on this dataset with "zfs allow".
 + *
 + * zfs_ioc_namecheck_t namecheck
 + *   This specifies what to expect in the zfs_cmd_t:zc_name -- a pool
 + *   name, a dataset name, or nothing.  If the name is not well-formed,
 + *   the ioctl will fail and the callback will not be called.
 + *   Therefore, the callback can assume that the name is well-formed
 + *   (e.g. is null-terminated, doesn't have more than one '@' character,
 + *   doesn't have invalid characters).
 + *
 + * zfs_ioc_poolcheck_t pool_check
 + *   This specifies requirements on the pool state.  If the pool does
 + *   not meet them (is suspended or is readonly), the ioctl will fail
 + *   and the callback will not be called.  If any checks are specified
 + *   (i.e. it is not POOL_CHECK_NONE), namecheck must not be NO_NAME.
 + *   Multiple checks can be or-ed together (e.g. POOL_CHECK_SUSPENDED |
 + *   POOL_CHECK_READONLY).
 + *
 + * zfs_ioc_key_t *nvl_keys
 + *  The list of expected/allowable innvl input keys. This list is used
 + *  to validate the nvlist input to the ioctl.
 + *
 + * boolean_t smush_outnvlist
 + *   If smush_outnvlist is true, then the output is presumed to be a
 + *   list of errors, and it will be "smushed" down to fit into the
 + *   caller's buffer, by removing some entries and replacing them with a
 + *   single "N_MORE_ERRORS" entry indicating how many were removed.  See
 + *   nvlist_smush() for details.  If smush_outnvlist is false, and the
 + *   outnvlist does not fit into the userland-provided buffer, then the
 + *   ioctl will fail with ENOMEM.
 + *
 + * zfs_ioc_func_t *func
 + *   The callback function that will perform the operation.
 + *
 + *   The callback should return 0 on success, or an error number on
 + *   failure.  If the function fails, the userland ioctl will return -1,
 + *   and errno will be set to the callback's return value.  The callback
 + *   will be called with the following arguments:
 + *
 + *   const char *name
 + *     The name of the pool or dataset to operate on, from
 + *     zfs_cmd_t:zc_name.  The 'namecheck' argument specifies the
 + *     expected type (pool, dataset, or none).
 + *
 + *   nvlist_t *innvl
 + *     The input nvlist, deserialized from zfs_cmd_t:zc_nvlist_src.  Or
 + *     NULL if no input nvlist was provided.  Changes to this nvlist are
 + *     ignored.  If the input nvlist could not be deserialized, the
 + *     ioctl will fail and the callback will not be called.
 + *
 + *   nvlist_t *outnvl
 + *     The output nvlist, initially empty.  The callback can fill it in,
 + *     and it will be returned to userland by serializing it into
 + *     zfs_cmd_t:zc_nvlist_dst.  If it is non-empty, and serialization
 + *     fails (e.g. because the caller didn't supply a large enough
 + *     buffer), then the overall ioctl will fail.  See the
 + *     'smush_nvlist' argument above for additional behaviors.
 + *
 + *     There are two typical uses of the output nvlist:
 + *       - To return state, e.g. property values.  In this case,
 + *         smush_outnvlist should be false.  If the buffer was not large
 + *         enough, the caller will reallocate a larger buffer and try
 + *         the ioctl again.
 + *
 + *       - To return multiple errors from an ioctl which makes on-disk
 + *         changes.  In this case, smush_outnvlist should be true.
 + *         Ioctls which make on-disk modifications should generally not
 + *         use the outnvl if they succeed, because the caller can not
 + *         distinguish between the operation failing, and
 + *         deserialization failing.
 + *
 + * IOCTL Interface Errors
 + *
 + * The following ioctl input errors can be returned:
 + *   ZFS_ERR_IOC_CMD_UNAVAIL	the ioctl number is not supported by kernel
 + *   ZFS_ERR_IOC_ARG_UNAVAIL	an input argument is not supported by kernel
 + *   ZFS_ERR_IOC_ARG_REQUIRED	a required input argument is missing
 + *   ZFS_ERR_IOC_ARG_BADTYPE	an input argument has an invalid type
 + */
 +
 +#include <sys/types.h>
 +#include <sys/param.h>
 +#include <sys/errno.h>
 +#include <sys/uio_impl.h>
 +#include <sys/file.h>
 +#include <sys/kmem.h>
 +#include <sys/cmn_err.h>
 +#include <sys/stat.h>
 +#include <sys/zfs_ioctl.h>
 +#include <sys/zfs_quota.h>
 +#include <sys/zfs_vfsops.h>
 +#include <sys/zfs_znode.h>
 +#include <sys/zap.h>
 +#include <sys/spa.h>
 +#include <sys/spa_impl.h>
 +#include <sys/vdev.h>
 +#include <sys/vdev_impl.h>
 +#include <sys/dmu.h>
 +#include <sys/dsl_dir.h>
 +#include <sys/dsl_dataset.h>
 +#include <sys/dsl_prop.h>
 +#include <sys/dsl_deleg.h>
 +#include <sys/dmu_objset.h>
 +#include <sys/dmu_impl.h>
 +#include <sys/dmu_redact.h>
 +#include <sys/dmu_tx.h>
 +#include <sys/sunddi.h>
 +#include <sys/policy.h>
 +#include <sys/zone.h>
 +#include <sys/nvpair.h>
 +#include <sys/pathname.h>
 +#include <sys/fs/zfs.h>
 +#include <sys/zfs_ctldir.h>
 +#include <sys/zfs_dir.h>
 +#include <sys/zfs_onexit.h>
 +#include <sys/zvol.h>
 +#include <sys/dsl_scan.h>
 +#include <sys/fm/util.h>
 +#include <sys/dsl_crypt.h>
 +#include <sys/rrwlock.h>
 +#include <sys/zfs_file.h>
 +
 +#include <sys/dmu_recv.h>
 +#include <sys/dmu_send.h>
 +#include <sys/dmu_recv.h>
 +#include <sys/dsl_destroy.h>
 +#include <sys/dsl_bookmark.h>
 +#include <sys/dsl_userhold.h>
 +#include <sys/zfeature.h>
 +#include <sys/zcp.h>
 +#include <sys/zio_checksum.h>
 +#include <sys/vdev_removal.h>
 +#include <sys/vdev_impl.h>
 +#include <sys/vdev_initialize.h>
 +#include <sys/vdev_trim.h>
 +
 +#include "zfs_namecheck.h"
 +#include "zfs_prop.h"
 +#include "zfs_deleg.h"
 +#include "zfs_comutil.h"
 +
 +#include <sys/lua/lua.h>
 +#include <sys/lua/lauxlib.h>
 +#include <sys/zfs_ioctl_impl.h>
 +
 +kmutex_t zfsdev_state_lock;
 +zfsdev_state_t *zfsdev_state_list;
 +
 +/*
 + * Limit maximum nvlist size.  We don't want users passing in insane values
 + * for zc->zc_nvlist_src_size, since we will need to allocate that much memory.
 + * Defaults to 0=auto which is handled by platform code.
 + */
 +unsigned long zfs_max_nvlist_src_size = 0;
 +
 +/*
 + * When logging the output nvlist of an ioctl in the on-disk history, limit
 + * the logged size to this many bytes.  This must be less then DMU_MAX_ACCESS.
 + * This applies primarily to zfs_ioc_channel_program().
 + */
 +unsigned long zfs_history_output_max = 1024 * 1024;
 +
 +uint_t zfs_fsyncer_key;
 +uint_t zfs_allow_log_key;
 +
 +/* DATA_TYPE_ANY is used when zkey_type can vary. */
 +#define	DATA_TYPE_ANY	DATA_TYPE_UNKNOWN
 +
 +typedef struct zfs_ioc_vec {
 +	zfs_ioc_legacy_func_t	*zvec_legacy_func;
 +	zfs_ioc_func_t		*zvec_func;
 +	zfs_secpolicy_func_t	*zvec_secpolicy;
 +	zfs_ioc_namecheck_t	zvec_namecheck;
 +	boolean_t		zvec_allow_log;
 +	zfs_ioc_poolcheck_t	zvec_pool_check;
 +	boolean_t		zvec_smush_outnvlist;
 +	const char		*zvec_name;
 +	const zfs_ioc_key_t	*zvec_nvl_keys;
 +	size_t			zvec_nvl_key_count;
 +} zfs_ioc_vec_t;
 +
 +/* This array is indexed by zfs_userquota_prop_t */
 +static const char *userquota_perms[] = {
 +	ZFS_DELEG_PERM_USERUSED,
 +	ZFS_DELEG_PERM_USERQUOTA,
 +	ZFS_DELEG_PERM_GROUPUSED,
 +	ZFS_DELEG_PERM_GROUPQUOTA,
 +	ZFS_DELEG_PERM_USEROBJUSED,
 +	ZFS_DELEG_PERM_USEROBJQUOTA,
 +	ZFS_DELEG_PERM_GROUPOBJUSED,
 +	ZFS_DELEG_PERM_GROUPOBJQUOTA,
 +	ZFS_DELEG_PERM_PROJECTUSED,
 +	ZFS_DELEG_PERM_PROJECTQUOTA,
 +	ZFS_DELEG_PERM_PROJECTOBJUSED,
 +	ZFS_DELEG_PERM_PROJECTOBJQUOTA,
 +};
 +
 +static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc);
 +static int zfs_ioc_id_quota_upgrade(zfs_cmd_t *zc);
 +static int zfs_check_settable(const char *name, nvpair_t *property,
 +    cred_t *cr);
 +static int zfs_check_clearable(const char *dataset, nvlist_t *props,
 +    nvlist_t **errors);
 +static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *,
 +    boolean_t *);
 +int zfs_set_prop_nvlist(const char *, zprop_source_t, nvlist_t *, nvlist_t *);
 +static int get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp);
 +
 +static void
 +history_str_free(char *buf)
 +{
 +	kmem_free(buf, HIS_MAX_RECORD_LEN);
 +}
 +
 +static char *
 +history_str_get(zfs_cmd_t *zc)
 +{
 +	char *buf;
 +
 +	if (zc->zc_history == 0)
 +		return (NULL);
 +
 +	buf = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP);
 +	if (copyinstr((void *)(uintptr_t)zc->zc_history,
 +	    buf, HIS_MAX_RECORD_LEN, NULL) != 0) {
 +		history_str_free(buf);
 +		return (NULL);
 +	}
 +
 +	buf[HIS_MAX_RECORD_LEN -1] = '\0';
 +
 +	return (buf);
 +}
 +
 +/*
 + * Return non-zero if the spa version is less than requested version.
 + */
 +static int
 +zfs_earlier_version(const char *name, int version)
 +{
 +	spa_t *spa;
 +
 +	if (spa_open(name, &spa, FTAG) == 0) {
 +		if (spa_version(spa) < version) {
 +			spa_close(spa, FTAG);
 +			return (1);
 +		}
 +		spa_close(spa, FTAG);
 +	}
 +	return (0);
 +}
 +
 +/*
 + * Return TRUE if the ZPL version is less than requested version.
 + */
 +static boolean_t
 +zpl_earlier_version(const char *name, int version)
 +{
 +	objset_t *os;
 +	boolean_t rc = B_TRUE;
 +
 +	if (dmu_objset_hold(name, FTAG, &os) == 0) {
 +		uint64_t zplversion;
 +
 +		if (dmu_objset_type(os) != DMU_OST_ZFS) {
 +			dmu_objset_rele(os, FTAG);
 +			return (B_TRUE);
 +		}
 +		/* XXX reading from non-owned objset */
 +		if (zfs_get_zplprop(os, ZFS_PROP_VERSION, &zplversion) == 0)
 +			rc = zplversion < version;
 +		dmu_objset_rele(os, FTAG);
 +	}
 +	return (rc);
 +}
 +
 +static void
 +zfs_log_history(zfs_cmd_t *zc)
 +{
 +	spa_t *spa;
 +	char *buf;
 +
 +	if ((buf = history_str_get(zc)) == NULL)
 +		return;
 +
 +	if (spa_open(zc->zc_name, &spa, FTAG) == 0) {
 +		if (spa_version(spa) >= SPA_VERSION_ZPOOL_HISTORY)
 +			(void) spa_history_log(spa, buf);
 +		spa_close(spa, FTAG);
 +	}
 +	history_str_free(buf);
 +}
 +
 +/*
 + * Policy for top-level read operations (list pools).  Requires no privileges,
 + * and can be used in the local zone, as there is no associated dataset.
 + */
 +/* ARGSUSED */
 +static int
 +zfs_secpolicy_none(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
 +{
 +	return (0);
 +}
 +
 +/*
 + * Policy for dataset read operations (list children, get statistics).  Requires
 + * no privileges, but must be visible in the local zone.
 + */
 +/* ARGSUSED */
 +static int
 +zfs_secpolicy_read(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
 +{
 +	if (INGLOBALZONE(curproc) ||
 +	    zone_dataset_visible(zc->zc_name, NULL))
 +		return (0);
 +
 +	return (SET_ERROR(ENOENT));
 +}
 +
 +static int
 +zfs_dozonecheck_impl(const char *dataset, uint64_t zoned, cred_t *cr)
 +{
 +	int writable = 1;
 +
 +	/*
 +	 * The dataset must be visible by this zone -- check this first
 +	 * so they don't see EPERM on something they shouldn't know about.
 +	 */
 +	if (!INGLOBALZONE(curproc) &&
 +	    !zone_dataset_visible(dataset, &writable))
 +		return (SET_ERROR(ENOENT));
 +
 +	if (INGLOBALZONE(curproc)) {
 +		/*
 +		 * If the fs is zoned, only root can access it from the
 +		 * global zone.
 +		 */
 +		if (secpolicy_zfs(cr) && zoned)
 +			return (SET_ERROR(EPERM));
 +	} else {
 +		/*
 +		 * If we are in a local zone, the 'zoned' property must be set.
 +		 */
 +		if (!zoned)
 +			return (SET_ERROR(EPERM));
 +
 +		/* must be writable by this zone */
 +		if (!writable)
 +			return (SET_ERROR(EPERM));
 +	}
 +	return (0);
 +}
 +
 +static int
 +zfs_dozonecheck(const char *dataset, cred_t *cr)
 +{
 +	uint64_t zoned;
 +
 +	if (dsl_prop_get_integer(dataset, zfs_prop_to_name(ZFS_PROP_ZONED),
 +	    &zoned, NULL))
 +		return (SET_ERROR(ENOENT));
 +
 +	return (zfs_dozonecheck_impl(dataset, zoned, cr));
 +}
 +
 +static int
 +zfs_dozonecheck_ds(const char *dataset, dsl_dataset_t *ds, cred_t *cr)
 +{
 +	uint64_t zoned;
 +
 +	if (dsl_prop_get_int_ds(ds, zfs_prop_to_name(ZFS_PROP_ZONED), &zoned))
 +		return (SET_ERROR(ENOENT));
 +
 +	return (zfs_dozonecheck_impl(dataset, zoned, cr));
 +}
 +
 +static int
 +zfs_secpolicy_write_perms_ds(const char *name, dsl_dataset_t *ds,
 +    const char *perm, cred_t *cr)
 +{
 +	int error;
 +
 +	error = zfs_dozonecheck_ds(name, ds, cr);
 +	if (error == 0) {
 +		error = secpolicy_zfs(cr);
 +		if (error != 0)
 +			error = dsl_deleg_access_impl(ds, perm, cr);
 +	}
 +	return (error);
 +}
 +
 +static int
 +zfs_secpolicy_write_perms(const char *name, const char *perm, cred_t *cr)
 +{
 +	int error;
 +	dsl_dataset_t *ds;
 +	dsl_pool_t *dp;
 +
 +	/*
 +	 * First do a quick check for root in the global zone, which
 +	 * is allowed to do all write_perms.  This ensures that zfs_ioc_*
 +	 * will get to handle nonexistent datasets.
 +	 */
 +	if (INGLOBALZONE(curproc) && secpolicy_zfs(cr) == 0)
 +		return (0);
 +
 +	error = dsl_pool_hold(name, FTAG, &dp);
 +	if (error != 0)
 +		return (error);
 +
 +	error = dsl_dataset_hold(dp, name, FTAG, &ds);
 +	if (error != 0) {
 +		dsl_pool_rele(dp, FTAG);
 +		return (error);
 +	}
 +
 +	error = zfs_secpolicy_write_perms_ds(name, ds, perm, cr);
 +
 +	dsl_dataset_rele(ds, FTAG);
 +	dsl_pool_rele(dp, FTAG);
 +	return (error);
 +}
 +
 +/*
 + * Policy for setting the security label property.
 + *
 + * Returns 0 for success, non-zero for access and other errors.
 + */
 +static int
 +zfs_set_slabel_policy(const char *name, const char *strval, cred_t *cr)
 +{
 +#ifdef HAVE_MLSLABEL
 +	char		ds_hexsl[MAXNAMELEN];
 +	bslabel_t	ds_sl, new_sl;
 +	boolean_t	new_default = FALSE;
 +	uint64_t	zoned;
 +	int		needed_priv = -1;
 +	int		error;
 +
 +	/* First get the existing dataset label. */
 +	error = dsl_prop_get(name, zfs_prop_to_name(ZFS_PROP_MLSLABEL),
 +	    1, sizeof (ds_hexsl), &ds_hexsl, NULL);
 +	if (error != 0)
 +		return (SET_ERROR(EPERM));
 +
 +	if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0)
 +		new_default = TRUE;
 +
 +	/* The label must be translatable */
 +	if (!new_default && (hexstr_to_label(strval, &new_sl) != 0))
 +		return (SET_ERROR(EINVAL));
 +
 +	/*
 +	 * In a non-global zone, disallow attempts to set a label that
 +	 * doesn't match that of the zone; otherwise no other checks
 +	 * are needed.
 +	 */
 +	if (!INGLOBALZONE(curproc)) {
 +		if (new_default || !blequal(&new_sl, CR_SL(CRED())))
 +			return (SET_ERROR(EPERM));
 +		return (0);
 +	}
 +
 +	/*
 +	 * For global-zone datasets (i.e., those whose zoned property is
 +	 * "off", verify that the specified new label is valid for the
 +	 * global zone.
 +	 */
 +	if (dsl_prop_get_integer(name,
 +	    zfs_prop_to_name(ZFS_PROP_ZONED), &zoned, NULL))
 +		return (SET_ERROR(EPERM));
 +	if (!zoned) {
 +		if (zfs_check_global_label(name, strval) != 0)
 +			return (SET_ERROR(EPERM));
 +	}
 +
 +	/*
 +	 * If the existing dataset label is nondefault, check if the
 +	 * dataset is mounted (label cannot be changed while mounted).
 +	 * Get the zfsvfs_t; if there isn't one, then the dataset isn't
 +	 * mounted (or isn't a dataset, doesn't exist, ...).
 +	 */
 +	if (strcasecmp(ds_hexsl, ZFS_MLSLABEL_DEFAULT) != 0) {
 +		objset_t *os;
 +		static const char *setsl_tag = "setsl_tag";
 +
 +		/*
 +		 * Try to own the dataset; abort if there is any error,
 +		 * (e.g., already mounted, in use, or other error).
 +		 */
 +		error = dmu_objset_own(name, DMU_OST_ZFS, B_TRUE, B_TRUE,
 +		    setsl_tag, &os);
 +		if (error != 0)
 +			return (SET_ERROR(EPERM));
 +
 +		dmu_objset_disown(os, B_TRUE, setsl_tag);
 +
 +		if (new_default) {
 +			needed_priv = PRIV_FILE_DOWNGRADE_SL;
 +			goto out_check;
 +		}
 +
 +		if (hexstr_to_label(strval, &new_sl) != 0)
 +			return (SET_ERROR(EPERM));
 +
 +		if (blstrictdom(&ds_sl, &new_sl))
 +			needed_priv = PRIV_FILE_DOWNGRADE_SL;
 +		else if (blstrictdom(&new_sl, &ds_sl))
 +			needed_priv = PRIV_FILE_UPGRADE_SL;
 +	} else {
 +		/* dataset currently has a default label */
 +		if (!new_default)
 +			needed_priv = PRIV_FILE_UPGRADE_SL;
 +	}
 +
 +out_check:
 +	if (needed_priv != -1)
 +		return (PRIV_POLICY(cr, needed_priv, B_FALSE, EPERM, NULL));
 +	return (0);
 +#else
 +	return (SET_ERROR(ENOTSUP));
 +#endif /* HAVE_MLSLABEL */
 +}
 +
 +static int
 +zfs_secpolicy_setprop(const char *dsname, zfs_prop_t prop, nvpair_t *propval,
 +    cred_t *cr)
 +{
 +	char *strval;
 +
 +	/*
 +	 * Check permissions for special properties.
 +	 */
 +	switch (prop) {
 +	default:
 +		break;
 +	case ZFS_PROP_ZONED:
 +		/*
 +		 * Disallow setting of 'zoned' from within a local zone.
 +		 */
 +		if (!INGLOBALZONE(curproc))
 +			return (SET_ERROR(EPERM));
 +		break;
 +
 +	case ZFS_PROP_QUOTA:
 +	case ZFS_PROP_FILESYSTEM_LIMIT:
 +	case ZFS_PROP_SNAPSHOT_LIMIT:
 +		if (!INGLOBALZONE(curproc)) {
 +			uint64_t zoned;
 +			char setpoint[ZFS_MAX_DATASET_NAME_LEN];
 +			/*
 +			 * Unprivileged users are allowed to modify the
 +			 * limit on things *under* (ie. contained by)
 +			 * the thing they own.
 +			 */
 +			if (dsl_prop_get_integer(dsname,
 +			    zfs_prop_to_name(ZFS_PROP_ZONED), &zoned, setpoint))
 +				return (SET_ERROR(EPERM));
 +			if (!zoned || strlen(dsname) <= strlen(setpoint))
 +				return (SET_ERROR(EPERM));
 +		}
 +		break;
 +
 +	case ZFS_PROP_MLSLABEL:
 +		if (!is_system_labeled())
 +			return (SET_ERROR(EPERM));
 +
 +		if (nvpair_value_string(propval, &strval) == 0) {
 +			int err;
 +
 +			err = zfs_set_slabel_policy(dsname, strval, CRED());
 +			if (err != 0)
 +				return (err);
 +		}
 +		break;
 +	}
 +
 +	return (zfs_secpolicy_write_perms(dsname, zfs_prop_to_name(prop), cr));
 +}
 +
 +/* ARGSUSED */
 +static int
 +zfs_secpolicy_set_fsacl(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
 +{
 +	int error;
 +
 +	error = zfs_dozonecheck(zc->zc_name, cr);
 +	if (error != 0)
 +		return (error);
 +
 +	/*
 +	 * permission to set permissions will be evaluated later in
 +	 * dsl_deleg_can_allow()
 +	 */
 +	return (0);
 +}
 +
 +/* ARGSUSED */
 +static int
 +zfs_secpolicy_rollback(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
 +{
 +	return (zfs_secpolicy_write_perms(zc->zc_name,
 +	    ZFS_DELEG_PERM_ROLLBACK, cr));
 +}
 +
 +/* ARGSUSED */
 +static int
 +zfs_secpolicy_send(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
 +{
 +	dsl_pool_t *dp;
 +	dsl_dataset_t *ds;
 +	const char *cp;
 +	int error;
 +
 +	/*
 +	 * Generate the current snapshot name from the given objsetid, then
 +	 * use that name for the secpolicy/zone checks.
 +	 */
 +	cp = strchr(zc->zc_name, '@');
 +	if (cp == NULL)
 +		return (SET_ERROR(EINVAL));
 +	error = dsl_pool_hold(zc->zc_name, FTAG, &dp);
 +	if (error != 0)
 +		return (error);
 +
 +	error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &ds);
 +	if (error != 0) {
 +		dsl_pool_rele(dp, FTAG);
 +		return (error);
 +	}
 +
 +	dsl_dataset_name(ds, zc->zc_name);
 +
 +	error = zfs_secpolicy_write_perms_ds(zc->zc_name, ds,
 +	    ZFS_DELEG_PERM_SEND, cr);
 +	dsl_dataset_rele(ds, FTAG);
 +	dsl_pool_rele(dp, FTAG);
 +
 +	return (error);
 +}
 +
 +/* ARGSUSED */
 +static int
 +zfs_secpolicy_send_new(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
 +{
 +	return (zfs_secpolicy_write_perms(zc->zc_name,
 +	    ZFS_DELEG_PERM_SEND, cr));
 +}
 +
 +static int
 +zfs_secpolicy_share(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
 +{
 +	return (SET_ERROR(ENOTSUP));
 +}
 +
 +static int
 +zfs_secpolicy_smb_acl(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
 +{
 +	return (SET_ERROR(ENOTSUP));
 +}
 +
 +static int
 +zfs_get_parent(const char *datasetname, char *parent, int parentsize)
 +{
 +	char *cp;
 +
 +	/*
 +	 * Remove the @bla or /bla from the end of the name to get the parent.
 +	 */
 +	(void) strncpy(parent, datasetname, parentsize);
 +	cp = strrchr(parent, '@');
 +	if (cp != NULL) {
 +		cp[0] = '\0';
 +	} else {
 +		cp = strrchr(parent, '/');
 +		if (cp == NULL)
 +			return (SET_ERROR(ENOENT));
 +		cp[0] = '\0';
 +	}
 +
 +	return (0);
 +}
 +
 +int
 +zfs_secpolicy_destroy_perms(const char *name, cred_t *cr)
 +{
 +	int error;
 +
 +	if ((error = zfs_secpolicy_write_perms(name,
 +	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
 +		return (error);
 +
 +	return (zfs_secpolicy_write_perms(name, ZFS_DELEG_PERM_DESTROY, cr));
 +}
 +
 +/* ARGSUSED */
 +static int
 +zfs_secpolicy_destroy(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
 +{
 +	return (zfs_secpolicy_destroy_perms(zc->zc_name, cr));
 +}
 +
 +/*
 + * Destroying snapshots with delegated permissions requires
 + * descendant mount and destroy permissions.
 + */
 +/* ARGSUSED */
 +static int
 +zfs_secpolicy_destroy_snaps(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
 +{
 +	nvlist_t *snaps;
 +	nvpair_t *pair, *nextpair;
 +	int error = 0;
 +
 +	snaps = fnvlist_lookup_nvlist(innvl, "snaps");
 +
 +	for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL;
 +	    pair = nextpair) {
 +		nextpair = nvlist_next_nvpair(snaps, pair);
 +		error = zfs_secpolicy_destroy_perms(nvpair_name(pair), cr);
 +		if (error == ENOENT) {
 +			/*
 +			 * Ignore any snapshots that don't exist (we consider
 +			 * them "already destroyed").  Remove the name from the
 +			 * nvl here in case the snapshot is created between
 +			 * now and when we try to destroy it (in which case
 +			 * we don't want to destroy it since we haven't
 +			 * checked for permission).
 +			 */
 +			fnvlist_remove_nvpair(snaps, pair);
 +			error = 0;
 +		}
 +		if (error != 0)
 +			break;
 +	}
 +
 +	return (error);
 +}
 +
 +int
 +zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr)
 +{
 +	char	parentname[ZFS_MAX_DATASET_NAME_LEN];
 +	int	error;
 +
 +	if ((error = zfs_secpolicy_write_perms(from,
 +	    ZFS_DELEG_PERM_RENAME, cr)) != 0)
 +		return (error);
 +
 +	if ((error = zfs_secpolicy_write_perms(from,
 +	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
 +		return (error);
 +
 +	if ((error = zfs_get_parent(to, parentname,
 +	    sizeof (parentname))) != 0)
 +		return (error);
 +
 +	if ((error = zfs_secpolicy_write_perms(parentname,
 +	    ZFS_DELEG_PERM_CREATE, cr)) != 0)
 +		return (error);
 +
 +	if ((error = zfs_secpolicy_write_perms(parentname,
 +	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
 +		return (error);
 +
 +	return (error);
 +}
 +
 +/* ARGSUSED */
 +static int
 +zfs_secpolicy_rename(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
 +{
 +	return (zfs_secpolicy_rename_perms(zc->zc_name, zc->zc_value, cr));
 +}
 +
 +/* ARGSUSED */
 +static int
 +zfs_secpolicy_promote(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
 +{
 +	dsl_pool_t *dp;
 +	dsl_dataset_t *clone;
 +	int error;
 +
 +	error = zfs_secpolicy_write_perms(zc->zc_name,
 +	    ZFS_DELEG_PERM_PROMOTE, cr);
 +	if (error != 0)
 +		return (error);
 +
 +	error = dsl_pool_hold(zc->zc_name, FTAG, &dp);
 +	if (error != 0)
 +		return (error);
 +
 +	error = dsl_dataset_hold(dp, zc->zc_name, FTAG, &clone);
 +
 +	if (error == 0) {
 +		char parentname[ZFS_MAX_DATASET_NAME_LEN];
 +		dsl_dataset_t *origin = NULL;
 +		dsl_dir_t *dd;
 +		dd = clone->ds_dir;
 +
 +		error = dsl_dataset_hold_obj(dd->dd_pool,
 +		    dsl_dir_phys(dd)->dd_origin_obj, FTAG, &origin);
 +		if (error != 0) {
 +			dsl_dataset_rele(clone, FTAG);
 +			dsl_pool_rele(dp, FTAG);
 +			return (error);
 +		}
 +
 +		error = zfs_secpolicy_write_perms_ds(zc->zc_name, clone,
 +		    ZFS_DELEG_PERM_MOUNT, cr);
 +
 +		dsl_dataset_name(origin, parentname);
 +		if (error == 0) {
 +			error = zfs_secpolicy_write_perms_ds(parentname, origin,
 +			    ZFS_DELEG_PERM_PROMOTE, cr);
 +		}
 +		dsl_dataset_rele(clone, FTAG);
 +		dsl_dataset_rele(origin, FTAG);
 +	}
 +	dsl_pool_rele(dp, FTAG);
 +	return (error);
 +}
 +
 +/* ARGSUSED */
 +static int
 +zfs_secpolicy_recv(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
 +{
 +	int error;
 +
 +	if ((error = zfs_secpolicy_write_perms(zc->zc_name,
 +	    ZFS_DELEG_PERM_RECEIVE, cr)) != 0)
 +		return (error);
 +
 +	if ((error = zfs_secpolicy_write_perms(zc->zc_name,
 +	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
 +		return (error);
 +
 +	return (zfs_secpolicy_write_perms(zc->zc_name,
 +	    ZFS_DELEG_PERM_CREATE, cr));
 +}
 +
 +/* ARGSUSED */
 +static int
 +zfs_secpolicy_recv_new(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
 +{
 +	return (zfs_secpolicy_recv(zc, innvl, cr));
 +}
 +
*** 6794 LINES SKIPPED ***


More information about the dev-commits-src-main mailing list