zfs_ioctl.c revision da165920f9fa0e493db9dba43b0c3771051409dc
/*
* 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
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/zfs_ioctl.h>
#include <sys/spa_impl.h>
#include <sys/vdev_impl.h>
#include <sys/dsl_dataset.h>
#include <sys/dsl_prop.h>
#include <sys/dsl_deleg.h>
#include <sys/dmu_objset.h>
#include <sys/pathname.h>
#include <sys/zfs_ctldir.h>
#include <sys/zfs_znode.h>
#include "zfs_namecheck.h"
#include "zfs_prop.h"
#include "zfs_deleg.h"
extern struct modlfs zfs_modlfs;
extern void zfs_init(void);
extern void zfs_fini(void);
typedef int zfs_ioc_func_t(zfs_cmd_t *);
typedef struct zfs_ioc_vec {
enum {
/* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */
void
{
const char *newfile;
char buf[256];
/*
* Get rid of annoying "../common/" prefix to filename.
*/
} else {
}
/*
* To get this data, use the zfs-dprintf probe as so:
* dtrace -q -n 'zfs-dprintf \
* /stringof(arg0) == "dbuf.c"/ \
* {printf("%s: %s", stringof(arg1), stringof(arg3))}'
* arg0 = file name
* arg1 = function name
* arg2 = line number
* arg3 = message
*/
}
static void
{
char *buf;
return;
return;
return;
}
return;
}
}
/*
* 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
{
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
{
if (INGLOBALZONE(curproc) ||
return (0);
return (ENOENT);
}
static int
{
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) &&
return (ENOENT);
return (ENOENT);
if (INGLOBALZONE(curproc)) {
/*
* If the fs is zoned, only root can access it from the
* global zone.
*/
return (EPERM);
} else {
/*
* If we are in a local zone, the 'zoned' property must be set.
*/
if (!zoned)
return (EPERM);
/* must be writable by this zone */
if (!writable)
return (EPERM);
}
return (0);
}
int
{
int error;
if (error == 0) {
if (error) {
}
}
return (error);
}
static int
{
int error = 0;
/*
* Check permissions for special properties.
*/
switch (prop) {
case ZFS_PROP_ZONED:
/*
* Disallow setting of 'zoned' from within a local zone.
*/
if (!INGLOBALZONE(curproc))
return (EPERM);
break;
case ZFS_PROP_QUOTA:
if (error =
return (error);
if (!INGLOBALZONE(curproc)) {
char setpoint[MAXNAMELEN];
int dslen;
/*
* Unprivileged users are allowed to modify the
* quota on things *under* (ie. contained by)
* the thing they own.
*/
setpoint))
return (EPERM);
if (!zoned) /* this shouldn't happen */
return (EPERM);
return (EPERM);
}
default:
}
return (error);
}
int
{
int error;
if (error)
return (error);
/*
* permission to set permissions will be evaluated later in
* dsl_deleg_can_allow()
*/
return (0);
}
int
{
int error;
if (error == 0)
return (error);
}
int
{
}
int
{
if (!INGLOBALZONE(curproc))
return (EPERM);
if (secpolicy_nfs(CRED()) == 0) {
return (0);
} else {
int error;
return (error);
/* Now make sure mntpnt and dataset are ZFS */
return (EPERM);
}
}
}
static int
{
char *cp;
/*
* Remove the @bla or /bla from the end of the name to get the parent.
*/
cp[0] = '\0';
} else {
return (ENOENT);
cp[0] = '\0';
}
return (0);
}
int
{
int error;
ZFS_DELEG_PERM_MOUNT, cr)) != 0)
return (error);
}
static int
{
}
/*
* Must have sys_config privilege to check the iscsi permission
*/
/* ARGSUSED */
static int
{
return (secpolicy_zfs(cr));
}
int
{
char parentname[MAXNAMELEN];
int error;
ZFS_DELEG_PERM_RENAME, cr)) != 0)
return (error);
ZFS_DELEG_PERM_MOUNT, cr)) != 0)
return (error);
sizeof (parentname))) != 0)
return (error);
ZFS_DELEG_PERM_CREATE, cr)) != 0)
return (error);
ZFS_DELEG_PERM_MOUNT, cr)) != 0)
return (error);
return (error);
}
static int
{
}
static int
{
char parentname[MAXNAMELEN];
int error;
if (error)
return (error);
if (error == 0) {
if (error) {
return (error);
}
if (error == 0)
}
return (error);
}
static int
{
int error;
ZFS_DELEG_PERM_RECEIVE, cr)) != 0)
return (error);
ZFS_DELEG_PERM_MOUNT, cr)) != 0)
return (error);
}
int
{
int error;
ZFS_DELEG_PERM_SNAPSHOT, cr)) != 0)
return (error);
return (error);
}
static int
{
}
static int
{
char parentname[MAXNAMELEN];
int error;
sizeof (parentname))) != 0)
return (error);
ZFS_DELEG_PERM_CLONE, cr)) != 0)
return (error);
}
ZFS_DELEG_PERM_CREATE, cr)) != 0)
return (error);
return (error);
}
static int
{
int error;
if (error) {
}
return (error);
}
/*
* SYS_CONFIG privilege, which is not available in a local zone.
*/
/* ARGSUSED */
static int
{
return (EPERM);
return (0);
}
/*
* Just like zfs_secpolicy_config, except that we will check for
* the minor nodes.
*/
static int
{
}
return (0);
}
/*
* Policy for fault injection. Requires all privileges.
*/
/* ARGSUSED */
static int
{
return (secpolicy_zinject(cr));
}
/*
* Returns the nvlist as specified by the user in the zfs_cmd_t.
*/
static int
{
char *packed;
int error;
/*
* Read in and unpack the user-supplied nvlist.
*/
return (EINVAL);
size)) != 0) {
return (error);
}
return (error);
}
return (0);
}
static int
{
int error;
} else {
KM_SLEEP) == 0);
size);
}
return (error);
}
static int
{
int error;
return (error);
return (error);
}
static int
{
int error;
return (error);
}
static int
{
int error;
return (error);
else
return (error);
}
static int
{
int error;
return (error);
}
static int
{
int error;
return (EEXIST);
return (error);
}
static int
{
int error;
int ret = 0;
/*
* The config may be present even if 'error' is non-zero.
* In this case we return success, and preserve the real errno
* in 'zc_cookie'.
*/
} else {
}
return (ret);
}
/*
* Try to import the given pool, returning pool stats as appropriate so that
* user land knows which devices are available and overall pool health.
*/
static int
{
int error;
return (error);
return (EINVAL);
return (error);
}
static int
{
int error;
return (error);
return (error);
}
static int
{
int error;
if (error == 0) {
}
return (error);
}
static int
{
int error;
return (error);
return (error);
}
static int
{
char *hist_buf;
int error;
return (EINVAL);
return (error);
return (ENOTSUP);
}
zc->zc_history_len);
}
return (error);
}
static int
{
int error;
return (error);
return (0);
}
static int
{
int error;
return (error);
return (error);
}
static int
{
int error;
if (error != 0)
return (error);
/*
* A root pool with concatenated devices is not supported.
* Thus, can not add a device to a root pool with one device.
*/
return (EDOM);
}
}
return (error);
}
static int
{
int error;
if (error != 0)
return (error);
return (error);
}
static int
{
int error;
return (error);
case VDEV_STATE_ONLINE:
break;
case VDEV_STATE_OFFLINE:
break;
case VDEV_STATE_FAULTED:
break;
case VDEV_STATE_DEGRADED:
break;
default:
}
return (error);
}
static int
{
int error;
return (error);
}
return (error);
}
static int
{
int error;
return (error);
return (error);
}
static int
{
int error;
if (error != 0)
return (error);
return (error);
}
static int
{
int error;
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".
*/
delay(1);
goto retry;
}
return (error);
}
if (zc->zc_nvlist_dst != 0 &&
/*
* NB: {zpl,zvol}_get_stats() 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...
*/
}
}
return (error);
}
static int
{
int error;
char *p;
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".
*/
delay(1);
goto retry;
}
return (error);
}
do {
/*
* If it's a hidden dataset (ie. with a '$' in its name), don't
* try to get stats for it. Userland will skip over it.
*/
return (error);
}
static int
{
int error;
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".
*/
delay(1);
goto retry;
}
return (error);
}
/*
* A dataset name of maximum length cannot have any snapshots,
* so exit immediately.
*/
return (ESRCH);
}
if (error == 0)
return (error);
}
static int
{
int error;
const char *propname;
char *strval;
/*
* First validate permission to set all of the properties
*/
/*
* If this is a user-defined property, it must be a
* string, and there is no further validation to do.
*/
if (!zfs_prop_user(propname) ||
return (EINVAL);
if (error) {
return (EPERM);
}
continue;
}
/*
* Check permissions for special properties
*/
switch (prop) {
case ZFS_PROP_ZONED:
/*
* Disallow setting of 'zoned' from within a local zone.
*/
if (!INGLOBALZONE(curproc))
return (EPERM);
break;
case ZFS_PROP_QUOTA:
return (error);
if (!INGLOBALZONE(curproc)) {
char setpoint[MAXNAMELEN];
int dslen;
/*
* Unprivileged users are allowed to modify the
* quota on things *under* (ie. contained by)
* the thing they own.
*/
setpoint))
return (EPERM);
if (!zoned) /* this shouldn't happen */
return (EPERM);
return (EPERM);
}
break;
case ZFS_PROP_COMPRESSION:
/*
* If the user specified gzip compression, make sure
* the SPA supports it. We ignore any errors here since
* we'll catch them later.
*/
intval >= ZIO_COMPRESS_GZIP_1 &&
intval <= ZIO_COMPRESS_GZIP_9) {
if (spa_version(spa) <
return (ENOTSUP);
}
}
}
break;
case ZFS_PROP_COPIES:
{
if (spa_version(spa) <
return (ENOTSUP);
}
}
break;
}
}
return (error);
}
if (error == 0)
continue;
else
return (error);
}
switch (prop) {
case ZFS_PROP_QUOTA:
return (error);
break;
case ZFS_PROP_RESERVATION:
intval)) != 0)
return (error);
break;
case ZFS_PROP_VOLSIZE:
return (error);
break;
case ZFS_PROP_VOLBLOCKSIZE:
return (error);
break;
case ZFS_PROP_VERSION:
return (error);
break;
default:
if (zfs_prop_get_type(prop) !=
return (EINVAL);
strval)) != 0)
return (error);
const char *unused;
switch (zfs_prop_get_type(prop)) {
case prop_type_number:
break;
case prop_type_boolean:
if (intval > 1)
return (EINVAL);
break;
case prop_type_string:
return (EINVAL);
case prop_type_index:
return (EINVAL);
break;
default:
"unknown property type");
break;
}
return (error);
} else {
return (EINVAL);
}
break;
}
}
return (0);
}
static int
{
int error;
/*
* If zc_value is set, then this is an attempt to inherit a value.
* Otherwise, zc_nvlist refers to a list of properties to set.
*/
return (EINVAL);
}
return (error);
return (error);
}
static int
{
int error, reset_bootfs = 0;
char *vdev_type;
return (error);
return (error);
}
return (ENOTSUP);
}
return (EINVAL);
}
switch (prop) {
case ZPOOL_PROP_DELEGATION:
if (intval > 1)
break;
case ZPOOL_PROP_BOOTFS:
/*
* A bootable filesystem can not be on a RAIDZ pool
* nor a striped pool with more than 1 device.
*/
break;
}
reset_bootfs = 1;
break;
}
break;
break;
}
if (error)
break;
}
if (error == 0) {
if (reset_bootfs) {
DATA_TYPE_STRING) == 0);
objnum) == 0);
}
}
return (error);
}
static int
{
int error;
return (error);
else
if (nvp)
return (error);
}
static int
{
int error;
return (error);
}
ZFS_DELEG_PERM_UID, &uid)) != 0) {
return (EPERM);
}
ZFS_DELEG_PERM_GID, &gid)) != 0) {
return (EPERM);
}
return (EPERM);
}
return (EPERM);
}
return (error);
}
static int
{
int error;
return (error);
/*
* Verify nvlist is constructed correctly
*/
return (EINVAL);
}
/*
* If we don't have PRIV_SYS_MOUNT, then validate
* that user is allowed to hand out each permission in
* the nvlist(s)
*/
if (error) {
else
}
if (error == 0)
return (error);
}
static int
{
int error;
}
return (error);
}
static int
{
}
static int
{
}
/*
* Search the vfs list for a specified resource. Returns a pointer to it
* or NULL if no suitable entry is found. The caller of this routine
* is responsible for releasing the returned vfs pointer.
*/
static vfs_t *
zfs_get_vfs(const char *resource)
{
do {
break;
}
return (vfs_found);
}
/* ARGSUSED */
static void
{
(void) nvlist_lookup_uint64(nvprops,
}
static int
{
int error = 0;
switch (type) {
case DMU_OST_ZFS:
break;
case DMU_OST_ZVOL:
break;
default:
}
return (EINVAL);
return (error);
/*
* We're creating a clone of an existing snapshot.
*/
return (EINVAL);
}
if (error) {
return (error);
}
} else {
return (EINVAL);
}
if (type == DMU_OST_ZVOL) {
&volsize) != 0) {
return (EINVAL);
}
return (EINVAL);
}
if (error != 0)
if ((error = zvol_check_volblocksize(
volblocksize)) != 0 ||
volblocksize)) != 0) {
return (error);
}
} else if (type == DMU_OST_ZFS) {
if (0 == nvlist_lookup_uint64(nvprops,
(version < ZPL_VERSION_INITIAL ||
version > ZPL_VERSION)) {
return (EINVAL);
}
}
nvprops);
}
/*
* It would be nice to do this atomically.
*/
if (error == 0) {
nvprops)) != 0)
}
return (error);
}
static int
{
return (EINVAL);
}
int
{
char *cp;
/*
* Snapshots (which are under .zfs control) must be unmounted
* before they can be destroyed.
*/
if (snapname) {
*cp = '\0';
}
if (vfsp) {
/*
* Always force the unmount for snapshots.
*/
int err;
return (err);
}
return (err);
}
return (0);
}
static int
{
int err;
return (EINVAL);
if (err)
return (err);
}
static int
{
if (err)
return (err);
}
}
static int
{
}
static int
{
return (EINVAL);
/*
* Unmount snapshot unless we're doing a recursive rename,
* in which case the dataset code figures out which snapshots
* to unmount.
*/
if (err)
return (err);
}
}
static int
{
return (EINVAL);
return (EBADF);
return (error);
}
static int
{
int error;
if (error)
return (error);
char buf[MAXPATHLEN];
char *cp;
if (cp)
*(cp+1) = 0;
if (error) {
return (error);
}
}
if (fromsnap)
return (EBADF);
}
if (fromsnap)
return (error);
}
static int
{
&zc->zc_inject_record);
if (error == 0)
return (error);
}
static int
{
}
static int
{
int error;
&zc->zc_inject_record);
return (error);
}
static int
{
int error;
return (error);
&count);
if (error == 0)
else
return (error);
}
static int
{
int error;
return (error);
return (ENODEV);
}
return (0);
}
static int
{
char *cp;
/*
* We don't need to unmount *all* the origin fs's snapshots, but
* it's easier.
*/
if (cp)
*cp = '\0';
}
/*
* We don't want to have a hard dependency
* against some special symbols in sharefs
* and nfs. Determine them if needed when
* the first file system is shared.
* Neither sharefs or nfs are unloadable modules.
*/
int (*zexport_fs)(void *arg);
int zfs_share_inited;
static int
{
int error;
int opcode;
if (zfs_share_inited == 0) {
return (ENOSYS);
}
return (ENOSYS);
}
return (ENOSYS);
}
zfs_share_inited = 1;
}
return (error);
return (error);
}
/*
* pool destroy and pool export don't log the history as part of zfsdev_ioctl,
* but rather zfs_ioc_pool_create, and zfs_ioc_pool_export do the loggin
* of those commands.
*/
static zfs_ioc_vec_t zfs_ioc_vec[] = {
DATASET_NAME, B_FALSE },
DATASET_NAME, B_FALSE },
DATASET_NAME, B_FALSE },
};
static int
{
return (EINVAL);
if (error == 0) {
}
/*
* the lower layers.
*/
if (error == 0) {
case POOL_NAME:
break;
case DATASET_NAME:
break;
case NO_NAME:
break;
}
}
if (error == 0)
if (error == 0) {
}
return (error);
}
static int
{
if (cmd != DDI_ATTACH)
return (DDI_FAILURE);
DDI_PSEUDO, 0) == DDI_FAILURE)
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
static int
{
return (DDI_FAILURE);
if (cmd != DDI_DETACH)
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
{
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
return (DDI_SUCCESS);
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)0;
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
/*
* OK, so this is a little weird.
*
*
* so most of the standard driver entry points are in zvol.c.
*/
static struct cb_ops zfs_cb_ops = {
zvol_open, /* open */
zvol_close, /* close */
zvol_strategy, /* strategy */
nodev, /* print */
nodev, /* dump */
zvol_read, /* read */
zvol_write, /* write */
zfsdev_ioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
ddi_prop_op, /* prop_op */
NULL, /* streamtab */
CB_REV, /* version */
nodev, /* async read */
nodev, /* async write */
};
static struct dev_ops zfs_dev_ops = {
DEVO_REV, /* version */
0, /* refcnt */
zfs_info, /* info */
nulldev, /* identify */
nulldev, /* probe */
zfs_attach, /* attach */
zfs_detach, /* detach */
nodev, /* reset */
&zfs_cb_ops, /* driver operations */
NULL /* no bus operations */
};
static struct modldrv zfs_modldrv = {
};
static struct modlinkage modlinkage = {
(void *)&zfs_modlfs,
(void *)&zfs_modldrv,
};
int
_init(void)
{
int error;
zfs_init();
zvol_init();
zvol_fini();
zfs_fini();
spa_fini();
return (error);
}
return (0);
}
int
_fini(void)
{
int error;
return (EBUSY);
return (error);
zvol_fini();
zfs_fini();
spa_fini();
if (zfs_share_inited) {
(void) ddi_modclose(nfs_mod);
(void) ddi_modclose(sharefs_mod);
}
return (error);
}
int
{
}