zfs_ioctl.c revision a7f53a5629374ca27c5696ace9a1946c2ca050f4
/*
* 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
*/
/*
*/
#include <sys/zfs_ioctl.h>
#include <sys/zfs_vfsops.h>
#include <sys/zfs_znode.h>
#include <sys/spa_impl.h>
#include <sys/priv_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_onexit.h>
#include <sys/dsl_scan.h>
#include <sys/dmu_objset.h>
#include "zfs_namecheck.h"
#include "zfs_prop.h"
#include "zfs_deleg.h"
#include "zfs_comutil.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 enum {
typedef struct zfs_ioc_vec {
/* This array is indexed by zfs_userquota_prop_t */
static const char *userquota_perms[] = {
};
boolean_t *);
/* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */
void
{
const char *newfile;
char buf[512];
/*
* 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
history_str_free(char *buf)
{
}
static char *
{
char *buf;
return (NULL);
return (NULL);
}
return (buf);
}
/*
* Check to see if the named dataset is currently defined as bootable
*/
static boolean_t
zfs_is_bootfs(const char *name)
{
return (ret);
}
return (B_FALSE);
}
/*
* zfs_earlier_version
*
* Return non-zero if the spa version is less than requested version.
*/
static int
{
return (1);
}
}
return (0);
}
/*
* zpl_earlier_version
*
* Return TRUE if the ZPL version is less than requested version.
*/
static boolean_t
{
return (B_TRUE);
}
/* XXX reading from non-owned objset */
}
return (rc);
}
static void
{
char *buf;
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);
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);
}
static int
{
return (ENOENT);
}
static int
{
return (ENOENT);
}
}
int
{
int error;
if (error == 0) {
if (error)
}
return (error);
}
int
{
int error;
if (error == 0) {
if (error)
}
return (error);
}
/*
* Policy for setting the security label property.
*
* Returns 0 for success, non-zero for access and other errors.
*/
static int
{
char ds_hexsl[MAXNAMELEN];
int needed_priv = -1;
int error;
/* First get the existing dataset label. */
if (error)
return (EPERM);
new_default = TRUE;
/* The label must be translatable */
return (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)) {
return (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,
return (EPERM);
if (!zoned) {
return (EPERM);
}
/*
* If the existing dataset label is nondefault, check if the
* dataset is mounted (label cannot be changed while mounted).
* Get the zfsvfs; if there isn't one, then the dataset isn't
* mounted (or isn't a dataset, doesn't exist, ...).
*/
static 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).
*/
if (error)
return (EPERM);
if (new_default) {
goto out_check;
}
return (EPERM);
} else {
/* dataset currently has a default label */
if (!new_default)
}
if (needed_priv != -1)
return (0);
}
static int
{
char *strval;
/*
* 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 (!INGLOBALZONE(curproc)) {
char setpoint[MAXNAMELEN];
/*
* Unprivileged users are allowed to modify the
* quota on things *under* (ie. contained by)
* the thing they own.
*/
setpoint))
return (EPERM);
return (EPERM);
}
break;
case ZFS_PROP_MLSLABEL:
if (!is_system_labeled())
return (EPERM);
int err;
if (err != 0)
return (err);
}
break;
}
}
int
{
int error;
if (error)
return (error);
/*
* permission to set permissions will be evaluated later in
* dsl_deleg_can_allow()
*/
return (0);
}
int
{
}
int
{
dsl_pool_t *dp;
char *cp;
int error;
/*
* Generate the current snapshot name from the given objsetid, then
*/
return (EINVAL);
if (error)
return (error);
if (error)
return (error);
return (error);
}
static int
{
int error;
return (error);
/* Now make sure mntpnt and dataset are ZFS */
return (EPERM);
}
}
int
{
if (!INGLOBALZONE(curproc))
return (EPERM);
if (secpolicy_nfs(cr) == 0) {
return (0);
} else {
}
}
int
{
if (!INGLOBALZONE(curproc))
return (EPERM);
if (secpolicy_smb(cr) == 0) {
return (0);
} else {
}
}
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
{
}
/*
* Destroying snapshots with delegated permissions requires
* descendent mount and destroy permissions.
* Reassemble the full filesystem@snap name so dsl_deleg_access()
* can do the correct permission check.
*
* Since this routine is used when doing a recursive destroy of snapshots
* and destroying snapshots requires descendent permissions, a successfull
* check of the top level snapshot applies to snapshots of all descendent
* datasets as well.
*/
static int
{
int error;
char *dsname;
return (error);
}
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
{
return (zfs_secpolicy_write_perms(name,
}
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);
}
/*
* Policy for fault injection. Requires all privileges.
*/
/* ARGSUSED */
static int
{
return (secpolicy_zinject(cr));
}
static int
{
if (prop == ZPROP_INVAL) {
return (EINVAL);
} else {
}
}
static int
{
if (err)
return (err);
return (EINVAL);
/*
* themself, allow it.
*/
return (0);
} else {
return (0);
}
}
}
static int
{
if (err)
return (err);
return (EINVAL);
}
static int
{
}
static int
{
}
static int
{
}
/*
* 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.
*/
if (size == 0)
return (EINVAL);
iflag)) != 0) {
return (error);
}
return (error);
}
return (0);
}
static int
{
int n = 0;
return (ENOMEM);
do {
n++;
NV_ENCODE_NATIVE) == 0);
}
return (0);
}
static int
{
int error = 0;
} else {
KM_SLEEP) == 0);
}
return (error);
}
static int
{
int error;
if (error)
return (error);
return (EINVAL);
}
if (*zfvp) {
} else {
}
return (error);
}
/*
* Find a zfsvfs_t for a mounted filesystem, or create our own, in which
* case its z_vfs will be NULL, and it will be opened as the owner.
*/
static int
{
int error = 0;
if (error == 0) {
if ((*zfvp)->z_unmounted) {
/*
* XXX we could probably try again, since the unmounting
* thread should be just about to disassociate the
* objset from the zfsvfs.
*/
return (EBUSY);
}
}
return (error);
}
static void
{
} else {
}
}
static int
{
int error;
char *buf;
return (error);
return (error);
}
if (props) {
(void) nvlist_lookup_uint64(props,
goto pool_props_bad;
}
if (nvl) {
if (error != 0) {
return (error);
}
}
if (error)
goto pool_props_bad;
}
/*
* Set the remaining root properties
*/
return (error);
}
static int
{
int error;
if (error == 0)
return (error);
}
static int
{
int error;
return (error);
return (error);
}
else
if (zc->zc_nvlist_dst != 0)
if (props)
return (error);
}
static int
{
int error;
if (error == 0)
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);
}
/*
* inputs:
* zc_name name of the pool
* zc_cookie scan func (pool_scan_func_t)
*/
static int
{
int error;
return (error);
else
return (error);
}
static int
{
int error;
if (error == 0) {
}
return (error);
}
static int
{
int error;
return (error);
return (EINVAL);
}
return (error);
}
static int
{
char *hist_buf;
int error;
return (EINVAL);
return (error);
return (ENOTSUP);
}
}
return (error);
}
static int
{
int error;
return (error);
return (0);
}
/*
* inputs:
* zc_name name of filesystem
* zc_obj object to find
*
* outputs:
* zc_value name of object
*/
static int
{
int error;
/* XXX reading from objset not owned */
return (error);
return (EINVAL);
}
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.
*
* Intent log device can not be added to a rootpool because
* during mountroot, zil is replayed, a seperated log device
* can not be accessed during the mountroot time.
*
* l2cache and spare devices are ok to be added to a rootpool.
*/
return (EDOM);
}
if (error == 0) {
}
return (error);
}
/*
* inputs:
* zc_name name of the pool
* zc_nvlist_conf nvlist of devices to remove
* zc_cookie to stop the remove?
*/
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;
return (error);
return (error);
}
return (error);
}
return (error);
}
static int
{
int error;
if (error != 0)
return (error);
return (error);
}
static int
{
int error;
if (error != 0)
return (error);
return (error);
}
static int
{
int error = 0;
if (zc->zc_nvlist_dst != 0 &&
/*
* NB: zvol_get_stats() will read the objset contents,
* which we aren't supposed to do with a
* DS_MODE_USER hold, because it could be
* inconsistent. So this is a bit of a workaround...
* XXX reading with out owning
*/
}
}
return (error);
}
/*
* inputs:
* zc_name name of filesystem
* zc_nvlist_dst_size size of buffer for property nvlist
*
* outputs:
* zc_objset_stats stats
* zc_nvlist_dst property nvlist
* zc_nvlist_dst_size size of property nvlist
*/
static int
{
int error;
return (error);
return (error);
}
/*
* inputs:
* zc_name name of filesystem
* zc_nvlist_dst_size size of buffer for property nvlist
*
* outputs:
* zc_nvlist_dst received property nvlist
* zc_nvlist_dst_size size of received property nvlist
*
* Gets received properties (distinct from local properties on or after
* SPA_VERSION_RECVD_PROPS) for callers who want to differentiate received from
* local property values.
*/
static int
{
int error;
return (error);
/*
* Without this check, we would return local property values if the
* caller has not already received properties on or after
* SPA_VERSION_RECVD_PROPS.
*/
if (!dsl_prop_get_hasrecvd(os)) {
return (ENOTSUP);
}
if (zc->zc_nvlist_dst != 0 &&
}
return (error);
}
static int
{
int error;
/*
* zfs_get_zplprop() will either find a value or give us
* the default value (if there is one).
*/
return (error);
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
{
int err;
/* XXX reading without owning */
return (err);
/*
* NB: nvl_add_zplprop() will read the objset contents,
* which we aren't supposed to do with a DS_MODE_USER
* hold, because it could be inconsistent.
*/
} else {
}
return (err);
}
static boolean_t
dataset_name_hidden(const char *name)
{
/*
* Skip over datasets that are not visible in this zone,
* internal datasets (which have a $ in their name), and
* temporary datasets (which have a % in their name).
*/
return (B_TRUE);
return (B_TRUE);
return (B_TRUE);
return (B_FALSE);
}
/*
* inputs:
* zc_name name of filesystem
* zc_cookie zap cursor
* zc_nvlist_dst_size size of buffer for property nvlist
*
* outputs:
* zc_name name of next filesystem
* zc_cookie zap cursor
* zc_objset_stats stats
* zc_nvlist_dst property nvlist
* zc_nvlist_dst_size size of property nvlist
*/
static int
{
int error;
char *p;
top:
return (error);
}
/*
* Pre-fetch the datasets. dmu_objset_prefetch() always returns 0
* but is not declared void because its called by dmu_objset_find().
*/
(void) dmu_objset_prefetch(p, NULL);
}
do {
/*
* If it's an internal dataset (ie. with a '$' in its name),
* don't try to get stats for it, otherwise we'll return ENOENT.
*/
/* We lost a race with destroy, get the next one. */
goto top;
}
}
return (error);
}
/*
* inputs:
* zc_name name of filesystem
* zc_cookie zap cursor
* zc_nvlist_dst_size size of buffer for property nvlist
*
* outputs:
* zc_name name of next snapshot
* zc_objset_stats stats
* zc_nvlist_dst property nvlist
* zc_nvlist_dst_size size of property nvlist
*/
static int
{
int error;
top:
if (error)
/*
* A dataset name of maximum length cannot have any snapshots,
* so exit immediately.
*/
return (ESRCH);
}
NULL);
if (error == 0) {
/*
* Since we probably don't have a hold on this snapshot,
* it's possible that the objsetid could have been destroyed
* and reused for a new objset. It's OK if this happens during
* a zfs send operation, since the new createtxg will be
* beyond the range we're interested in.
*/
if (error) {
/* Racing with destroy, get the next one. */
goto top;
}
} else {
if (error == 0)
}
}
/* if we failed, undo the @ that we tacked on to zc_name */
if (error)
return (error);
}
static int
{
unsigned int vallen;
const char *domain;
char *dash;
int err;
&pair) != 0)
return (EINVAL);
}
/*
* A correctly constructed propname is encoded as
* userquota@<rid>-<domain>.
*/
vallen != 3)
return (EINVAL);
if (err == 0) {
}
return (err);
}
/*
* If the named property is one that has a special function to set its value,
* return 0 on success and a positive error code on failure; otherwise if it is
* not one of the special properties handled by this function, return -1.
*
* XXX: It would be better for callers of the property interface if we handled
* these special cases in dsl_prop.c (in the dsl layer).
*/
static int
{
int err;
if (prop == ZPROP_INVAL) {
if (zfs_prop_userquota(propname))
return (-1);
}
&pair) == 0);
}
return (-1);
switch (prop) {
case ZFS_PROP_QUOTA:
break;
case ZFS_PROP_REFQUOTA:
break;
case ZFS_PROP_RESERVATION:
break;
case ZFS_PROP_REFRESERVATION:
break;
case ZFS_PROP_VOLSIZE:
intval);
break;
case ZFS_PROP_VERSION:
{
break;
(void) zfs_ioc_userspace_upgrade(zc);
}
break;
}
default:
err = -1;
}
return (err);
}
/*
* This function is best effort. If it fails to set any of the given properties,
* it continues to set as many as it can and returns the first error
* encountered. If the caller provides a non-NULL errlist, it also gives the
* complete list of names of all the properties it failed to set along with the
* corresponding error numbers. The caller is responsible for freeing the
* returned errlist.
*
* If every property is set successfully, zero is returned and the list pointed
* at by errlist is NULL.
*/
int
{
int rv = 0;
char *strval;
int err = 0;
/* decode the property value */
&propval) != 0)
}
/* Validate value type */
if (zfs_prop_user(propname)) {
} else if (zfs_prop_userquota(propname)) {
if (nvpair_type(propval) !=
}
} else if (err == 0) {
const char *unused;
&intval) == 0);
switch (zfs_prop_get_type(prop)) {
case PROP_TYPE_NUMBER:
break;
case PROP_TYPE_STRING:
break;
case PROP_TYPE_INDEX:
break;
default:
"unknown property type");
}
} else {
}
}
/* Validate permissions */
if (err == 0)
if (err == 0) {
if (err == -1) {
/*
* For better performance we build up a list of
* properties to set in a single transaction.
*/
/*
* This may be a spurious error caused by
* receiving quota and reservation out of order.
* Try again in a second pass.
*/
}
}
if (err != 0)
}
goto retry;
}
if (!nvlist_empty(genericnvl) &&
/*
* If this fails, we still want to set as many properties as we
* can, so try setting them individually.
*/
int err = 0;
&propval) == 0);
}
&strval) == 0);
} else {
&intval) == 0);
1, &intval);
}
if (err != 0) {
err) == 0);
}
}
}
} else {
}
else
return (rv);
}
/*
* Check that all the properties are valid user properties.
*/
static int
{
int error = 0;
char *valstr;
if (!zfs_prop_user(propname) ||
return (EINVAL);
return (error);
return (ENAMETOOLONG);
return (E2BIG);
}
return (0);
}
static void
{
continue;
}
}
static int
{
int err = 0;
if (!nvlist_empty(cleared_props)) {
/*
* Acts on local properties until the dataset has received
* properties at least once on or after SPA_VERSION_RECVD_PROPS.
*/
}
return (err);
}
/*
* inputs:
* zc_name name of filesystem
* zc_value name of property to set
* zc_nvlist_src{_size} nvlist of properties to apply
* zc_cookie received properties flag
*
* outputs:
* zc_nvlist_dst{_size} error for each unapplied received property
*/
static int
{
int error;
return (error);
if (received) {
(void) clear_received_props(os,
}
}
}
}
return (error);
}
/*
* inputs:
* zc_name name of filesystem
* zc_value name of property to inherit
* zc_cookie revert to received value if TRUE
*
* outputs: none
*/
static int
{
? ZPROP_SRC_NONE /* revert to received value, if any */
: ZPROP_SRC_INHERITED); /* explicitly inherit */
if (received) {
int err;
/*
* zfs_prop_set_special() expects properties in the form of an
* nvpair with type info.
*/
if (prop == ZPROP_INVAL) {
if (!zfs_prop_user(propname))
return (EINVAL);
} else if (prop == ZFS_PROP_VOLSIZE ||
prop == ZFS_PROP_VERSION) {
return (EINVAL);
} else {
}
switch (type) {
case PROP_TYPE_STRING:
break;
case PROP_TYPE_NUMBER:
case PROP_TYPE_INDEX:
break;
default:
return (EINVAL);
}
if (err != -1)
return (err); /* special property already handled */
} else {
/*
* Only check this in the non-received case. We want to allow
* 'inherit -S' to revert non-inheritable properties like quota
* and reservation to the received or default values even though
* they are not considered inheritable.
*/
return (EINVAL);
}
/* the property name has been validated by zfs_secpolicy_inherit() */
}
static int
{
int error;
return (error);
/*
* If the only property is the configfile, then just do a spa_lookup()
* to handle the faulted case.
*/
zpool_prop_to_name(ZPOOL_PROP_CACHEFILE)) == 0 &&
}
return (0);
}
}
return (error);
}
return (error);
}
static int
{
int error;
/*
* If the pool is faulted, there may be properties we can still
* get (such as altroot and cachefile), so attempt to get them
* anyway.
*/
} else {
}
else
return (error);
}
/*
* inputs:
* zc_name name of filesystem
* zc_nvlist_src{_size} nvlist of delegated permissions
*
* outputs: none
*/
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);
}
/*
* inputs:
* zc_name name of filesystem
*
* outputs:
* zc_nvlist_src{_size} nvlist of delegated permissions
*/
static int
{
int error;
}
return (error);
}
/*
* 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
{
}
/*
* inputs:
* createprops list of properties requested by creator
* default_zplver zpl version to use if unspecified in createprops
* fuids_ok fuids allowed in this version of the spa?
* os parent objset pointer (NULL if root fs)
*
* outputs:
* zplprops values for the zplprops we attach to the master node object
* is_ci true if requested file system will be purely case-insensitive
*
* Determine the settings for utf8only, normalization and
* casesensitivity. Specific values may have been requested by the
* 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
{
/*
* Pull out creator prop choices, if any.
*/
if (createprops) {
(void) nvlist_lookup_uint64(createprops,
(void) nvlist_lookup_uint64(createprops,
(void) nvlist_remove_all(createprops,
(void) nvlist_lookup_uint64(createprops,
(void) nvlist_remove_all(createprops,
(void) nvlist_lookup_uint64(createprops,
(void) nvlist_remove_all(createprops,
}
/*
* If the zpl version requested is whacky or 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.
*/
sense != ZFS_PROP_UNDEFINED)))
return (ENOTSUP);
/*
* Put the version in the zplprops
*/
if (norm == ZFS_PROP_UNDEFINED)
/*
* If we're normalizing, names must always be valid UTF-8 strings.
*/
if (norm)
u8 = 1;
if (u8 == ZFS_PROP_UNDEFINED)
if (sense == ZFS_PROP_UNDEFINED)
if (is_ci)
return (0);
}
static int
{
char parentname[MAXNAMELEN];
char *cp;
int error;
cp[0] = '\0';
return (error);
/*
* Open parent object set so we can inherit zplprop values.
*/
return (error);
return (error);
}
static int
{
int error;
return (error);
}
/*
* inputs:
* zc_objset_type type of objset to create (fs vs zvol)
* zc_name name of new objset
* zc_value name of snapshot to clone from (may be empty)
* zc_nvlist_src{_size} nvlist of properties to apply
*
* outputs: none
*/
static int
{
int error = 0;
switch (type) {
case DMU_OST_ZFS:
break;
case DMU_OST_ZVOL:
break;
default:
break;
}
return (EINVAL);
return (error);
/*
* We're creating a clone of an existing snapshot.
*/
return (EINVAL);
}
if (error) {
return (error);
}
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) {
int error;
/*
* We have to have normalization and
* case-folding flags correct when we do the
* file system creation, so go figure them out
* now.
*/
NV_UNIQUE_NAME, KM_SLEEP) == 0);
if (error != 0) {
return (error);
}
}
}
/*
* It would be nice to do this atomically.
*/
if (error == 0) {
if (error != 0)
}
return (error);
}
/*
* inputs:
* zc_name name of filesystem
* zc_value short name of snapshot
* zc_cookie recursive flag
* zc_nvlist_src[_size] property list
*
* outputs:
* zc_value short snapname (i.e. part after the '@')
*/
static int
{
int error;
return (EINVAL);
return (error);
if (error)
goto out;
if (!nvlist_empty(nvprops) &&
goto out;
}
out:
return (error);
}
int
{
if (arg) {
}
if (vfsp) {
/*
* Always force the unmount for snapshots.
*/
int err;
return (err);
}
return (err);
}
return (0);
}
/*
* inputs:
* zc_name name of filesystem
* zc_value short name of snapshot
* zc_defer_destroy mark for deferred destroy
*
* outputs: none
*/
static int
{
int err;
return (EINVAL);
if (err)
return (err);
zc->zc_defer_destroy));
}
/*
* inputs:
* zc_name name of dataset to destroy
* zc_objset_type type of objset
* zc_defer_destroy mark for deferred destroy
*
* outputs: none
*/
static int
{
int err;
if (err)
return (err);
}
return (err);
}
/*
* inputs:
* zc_name name of dataset to rollback (to most recent snapshot)
*
* outputs: none
*/
static int
{
int error;
char *clone_name;
if (error)
return (error);
/* must not be a snapshot */
if (dsl_dataset_is_snapshot(ds)) {
return (EINVAL);
}
/* must have a most recent snapshot */
return (EINVAL);
}
/*
* Create clone of most recent snapshot.
*/
if (error)
goto out;
if (error)
goto out;
/*
* Do clone swap.
*/
if (error == 0) {
int resume_err;
B_TRUE);
} else {
}
}
} else {
} else {
}
}
/*
* Destroy clone (which also closes it).
*/
out:
if (ds)
return (error);
}
/*
* inputs:
* zc_name old name of dataset
* zc_value new name of dataset
* zc_cookie recursive flag (only valid for snapshots)
*
* outputs: none
*/
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
{
int err;
if (prop == ZPROP_INVAL) {
if (zfs_prop_user(propname)) {
return (err);
return (0);
}
const char *uq_prefix =
const char *gq_prefix =
} else {
/* USERUSED and GROUPUSED are read-only */
return (EINVAL);
}
return (err);
return (0);
}
return (EINVAL);
}
if (issnap)
return (EINVAL);
/*
* dsl_prop_get_all_impl() returns properties in this
* format.
*/
&pair) == 0);
}
/*
* Check that this value is valid for this pool version
*/
switch (prop) {
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.
*/
if (intval >= ZIO_COMPRESS_GZIP_1 &&
intval <= ZIO_COMPRESS_GZIP_9 &&
return (ENOTSUP);
}
if (intval == ZIO_COMPRESS_ZLE &&
return (ENOTSUP);
/*
* If this is a bootable dataset then
* verify that the compression algorithm
* is supported for booting. We must return
* something other than ENOTSUP since it
* implies a downrev pool version.
*/
if (zfs_is_bootfs(dsname) &&
return (ERANGE);
}
}
break;
case ZFS_PROP_COPIES:
return (ENOTSUP);
break;
case ZFS_PROP_DEDUP:
return (ENOTSUP);
break;
case ZFS_PROP_SHARESMB:
return (ENOTSUP);
break;
case ZFS_PROP_ACLINHERIT:
if (intval == ZFS_ACL_PASSTHROUGH_X &&
return (ENOTSUP);
}
break;
}
}
/*
* Removes properties from the given props list that fail permission checks
* needed to clear them and to restore them in case of a receive error. For each
* property, make sure we have both set and inherit permissions.
*
* Returns the first error encountered if any permission checks fail. If the
* caller provides a non-NULL errlist, it also gives the complete list of names
* of all the properties that failed a permission check along with the
* corresponding error numbers. The caller is responsible for freeing the
* returned errlist.
*
* If every property checks out successfully, zero is returned and the list
* pointed at by errlist is NULL.
*/
static int
{
return (0);
}
}
} else {
}
else
return (rv);
}
static boolean_t
{
/* dsl_prop_get_all_impl() format */
&p1) == 0);
}
&p2) == 0);
}
return (B_FALSE);
} else {
}
}
/*
* Remove properties from props if they are not going to change (as determined
* by comparison with origprops). Remove them from origprops as well, since we
* do not need to clear or restore properties that won't change.
*/
static void
{
return; /* all props need to be received */
goto next; /* need to set received value */
/* don't clear the existing received value */
/* don't bother receiving the property */
next:
}
}
#ifdef DEBUG
static boolean_t zfs_ioc_recv_inject_err;
#endif
/*
* inputs:
* zc_name name of containing filesystem
* zc_nvlist_src{_size} nvlist of properties to apply
* zc_value name of snapshot to create
* zc_string name of clone origin (if DRR_FLAG_CLONE)
* zc_cookie file descriptor to recv from
* zc_begin_record the BEGIN record of the stream (not byteswapped)
* zc_guid force flag
* zc_cleanup_fd cleanup-on-exit file descriptor
*
* outputs:
* zc_cookie number of bytes read
* zc_nvlist_dst{_size} error for each unapplied received property
* zc_obj zprop_errflags_t
*/
static int
{
int fd;
int error = 0;
int props_error = 0;
char *tosnap;
char tofs[ZFS_MAXNAMELEN];
return (EINVAL);
*tosnap++ = '\0';
return (error);
return (EBADF);
}
!dsl_prop_get_hasrecvd(os)) {
}
/*
* If new received properties are supplied, they are to
* completely replace the existing received properties, so stash
* away the existing ones.
*/
/*
* Don't bother writing a property if its value won't
* change (and avoid the unnecessary security checks).
*
* The first receive after SPA_VERSION_RECVD_PROPS is a
* special case where we blow away all local properties
* regardless.
*/
if (!first_recvd_props)
&errlist) != 0)
}
}
if (error)
goto out;
}
if (origin)
if (error)
goto out;
/*
* Set properties before we receive the stream so that they are applied
* to the new data. Note that we must call dmu_recv_stream() if
* dmu_recv_begin() succeeds.
*/
if (props) {
} else {
}
}
}
/*
* Caller made zc->zc_nvlist_dst less than the minimum expected
* size or supplied an invalid address.
*/
}
&zc->zc_action_handle);
if (error == 0) {
/* online recv */
int end_err;
/*
* If the suspend fails, then the recv_end will
* likely also fail, and clean up after itself.
*/
if (error == 0)
} else {
}
}
#ifdef DEBUG
if (zfs_ioc_recv_inject_err) {
error = 1;
}
#endif
/*
* On error, restore the original props.
*/
/*
* We failed to clear the received properties.
* Since we may have left a $recvd value on the
* system, we can't clear the $hasrecvd flag.
*/
} else if (first_recvd_props) {
}
/* We failed to clear the received properties. */
}
/* We failed to stash the original properties. */
}
/*
* dsl_props_set() will not convert RECEIVED to LOCAL on or
* after SPA_VERSION_RECVD_PROPS, so we need to specify LOCAL
* explictly if we're restoring local properties cleared in the
* first new-style receive.
*/
/*
* We stashed the original properties but failed to
* restore them.
*/
}
}
out:
if (error == 0)
error = props_error;
return (error);
}
/*
* inputs:
* zc_name name of snapshot to send
* zc_cookie file descriptor to send stream to
* zc_obj fromorigin flag (mutually exclusive with zc_fromobj)
* zc_sendobj objsetid of snapshot to send
* zc_fromobj objsetid of incremental fromsnap (may be zero)
*
* outputs: none
*/
static int
{
int error;
dsl_pool_t *dp;
if (error)
return (error);
if (error) {
return (error);
}
if (error) {
return (error);
}
if (zc->zc_fromobj != 0) {
if (error) {
return (error);
}
if (error) {
return (error);
}
} else {
}
if (dsfrom)
return (EBADF);
}
if (dsfrom)
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;
/*
* On zpool clear we also fix up missing slogs
*/
return (EIO);
}
}
spa->spa_last_open_failed = 0;
} else {
return (EINVAL);
}
}
}
if (error)
return (error);
} else {
return (ENODEV);
}
}
/*
*/
if (zio_resume(spa) != 0)
return (error);
}
/*
* inputs:
* zc_name name of filesystem
* zc_value name of origin snapshot
*
* outputs:
* zc_string name of conflicting snapshot, if there is one
*/
static int
{
char *cp;
/*
* We don't need to unmount *all* the origin fs's snapshots, but
* it's easier.
*/
if (cp)
*cp = '\0';
}
/*
* Retrieve a single {user|group}{used|quota}@... property.
*
* inputs:
* zc_name name of filesystem
* zc_objset_type zfs_userquota_prop_t
* zc_value domain name (eg. "S-1-234-567-89")
*
* outputs:
* zc_cookie property value
*/
static int
{
int error;
return (EINVAL);
if (error)
return (error);
return (error);
}
/*
* inputs:
* zc_name name of filesystem
* zc_cookie zap cursor
* zc_objset_type zfs_userquota_prop_t
* zc_nvlist_dst[_size] buffer to fill (not really an nvlist)
*
* outputs:
* zc_nvlist_dst[_size] data buffer (array of zfs_useracct_t)
* zc_cookie zap cursor
*/
static int
{
if (bufsize <= 0)
return (ENOMEM);
if (error)
return (error);
if (error == 0) {
}
return (error);
}
/*
* inputs:
* zc_name name of filesystem
*
* outputs:
* none
*/
static int
{
int error = 0;
/*
* If userused is not enabled, it may be because the
* objset needs to be closed & reopened (to grow the
*/
if (error == 0)
}
if (error == 0)
} else {
/* XXX kind of reading contents without owning */
if (error)
return (error);
}
return (error);
}
/*
* We don't want to have a hard dependency
* against some special symbols in sharefs
* nfs, and smbsrv. Determine them if needed when
* the first file system is shared.
* Neither sharefs, nfs or smbsrv are unloadable modules.
*/
int (*znfsexport_fs)(void *arg);
int zfs_nfsshare_inited;
int zfs_smbshare_inited;
static int
{
int error;
/* Both NFS and SMB shares also require sharetab support. */
ddi_modopen("fs/sharefs",
return (ENOSYS);
}
return (ENOSYS);
}
return (0);
}
static int
{
int error;
int opcode;
case ZFS_SHARE_NFS:
case ZFS_UNSHARE_NFS:
if (zfs_nfsshare_inited == 0) {
return (ENOSYS);
}
if (znfsexport_fs == NULL &&
((znfsexport_fs = (int (*)(void *))
return (ENOSYS);
}
error = zfs_init_sharefs();
if (error) {
return (ENOSYS);
}
zfs_nfsshare_inited = 1;
}
break;
case ZFS_SHARE_SMB:
case ZFS_UNSHARE_SMB:
if (zfs_smbshare_inited == 0) {
ddi_modopen("drv/smbsrv",
return (ENOSYS);
}
return (ENOSYS);
}
error = zfs_init_sharefs();
if (error) {
return (ENOSYS);
}
zfs_smbshare_inited = 1;
}
break;
default:
return (EINVAL);
}
case ZFS_SHARE_NFS:
case ZFS_UNSHARE_NFS:
if (error =
znfsexport_fs((void *)
return (error);
break;
case ZFS_SHARE_SMB:
case ZFS_UNSHARE_SMB:
if (error = zsmbexport_fs((void *)
return (error);
}
break;
}
/*
* Add or remove share from sharetab
*/
return (error);
}
ace_t full_access[] = {
};
/*
* Remove all ACL files in shares dir
*/
static int
{
int error;
zap_cursor_advance(&zc)) {
NULL, 0)) != 0)
break;
}
return (error);
}
static int
{
int error = 0;
return (error);
/* Now make sure mntpnt and dataset are ZFS */
return (EINVAL);
}
/*
* Create share dir if its missing.
*/
if (zfsvfs->z_shares_dir == 0) {
if (error) {
} else {
}
if (error) {
return (error);
}
}
return (error);
}
case ZFS_SMB_ACL_ADD:
if (resourcevp)
break;
case ZFS_SMB_ACL_REMOVE:
NULL, 0);
break;
case ZFS_SMB_ACL_RENAME:
return (error);
}
&target)) {
return (error);
}
break;
case ZFS_SMB_ACL_PURGE:
break;
default:
break;
}
return (error);
}
/*
* inputs:
* zc_name name of filesystem
* zc_value short name of snap
* zc_string user-supplied tag for this hold
* zc_cookie recursive flag
* zc_temphold set if hold is temporary
* zc_cleanup_fd cleanup-on-exit file descriptor for calling process
* zc_sendobj if non-zero, the objid for zc_name@zc_value
* zc_createtxg if zc_sendobj is non-zero, snap must have zc_createtxg
*
* outputs: none
*/
static int
{
dsl_pool_t *dp;
int error;
return (EINVAL);
if (zc->zc_sendobj == 0) {
zc->zc_cleanup_fd));
}
if (recursive)
return (EINVAL);
if (error)
return (error);
if (error)
return (error);
/*
* Until we have a hold on this snapshot, it's possible that
* zc_sendobj could've been destroyed and reused as part
* of a later txg. Make sure we're looking at the right object.
*/
return (ENOENT);
}
if (error) {
return (error);
}
}
zc->zc_temphold);
if (minor != 0) {
if (error == 0) {
minor);
}
}
return (error);
}
/*
* inputs:
* zc_name name of dataset from which we're releasing a user hold
* zc_value short name of snap
* zc_string user-supplied tag for this hold
* zc_cookie recursive flag
*
* outputs: none
*/
static int
{
return (EINVAL);
}
/*
* inputs:
* zc_name name of filesystem
*
* outputs:
* zc_nvlist_src{_size} nvlist of snapshot holds
*/
static int
{
int error;
}
return (error);
}
/*
* pool create, destroy, and export don't log the history as part of
* zfsdev_ioctl, but rather zfs_ioc_pool_create, and zfs_ioc_pool_export
* do the logging of those commands.
*/
static zfs_ioc_vec_t zfs_ioc_vec[] = {
B_FALSE },
B_FALSE },
B_FALSE },
B_FALSE },
B_FALSE },
B_FALSE },
B_FALSE },
B_TRUE },
B_FALSE },
B_TRUE },
B_FALSE },
B_TRUE },
B_TRUE },
B_FALSE },
B_TRUE },
B_TRUE },
B_TRUE },
B_TRUE },
B_TRUE },
B_FALSE },
B_TRUE },
B_TRUE },
B_TRUE},
B_TRUE },
B_FALSE },
B_FALSE },
B_FALSE },
B_FALSE },
B_TRUE },
B_TRUE },
B_FALSE },
B_TRUE },
B_TRUE },
B_FALSE },
B_TRUE },
B_FALSE },
B_TRUE },
B_FALSE },
B_TRUE },
B_TRUE },
B_FALSE },
B_TRUE }
};
int
{
int error;
if (error == 0) {
if (spa_suspended(spa))
}
return (error);
}
/*
* Find a free minor number.
*/
zfsdev_minor_alloc(void)
{
static minor_t last_minor;
minor_t m;
if (m > ZFSDEV_MAX_MINOR)
m = 1;
last_minor = m;
return (m);
}
}
return (0);
}
static int
{
minor = zfsdev_minor_alloc();
if (minor == 0)
return (ENXIO);
return (EAGAIN);
return (0);
}
static void
{
}
void *
{
return (NULL);
}
static int
{
int error = 0;
/* This is the control device. Allocate a new minor if requested. */
}
return (error);
}
static int
{
if (minor == 0)
return (0);
}
return (0);
}
static int
{
if (minor != 0 &&
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) {
if (rc != 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 = {
zfsdev_open, /* open */
zfsdev_close, /* close */
zvol_strategy, /* strategy */
nodev, /* print */
zvol_dump, /* 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 */
NULL, /* power */
ddi_quiesce_not_needed, /* quiesce */
};
static struct modldrv zfs_modldrv = {
"ZFS storage pool",
};
static struct modlinkage modlinkage = {
(void *)&zfs_modlfs,
(void *)&zfs_modldrv,
};
extern uint_t rrw_tsd_key;
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_nfsshare_inited)
(void) ddi_modclose(nfs_mod);
if (zfs_smbshare_inited)
(void) ddi_modclose(smbsrv_mod);
(void) ddi_modclose(sharefs_mod);
return (error);
}
int
{
}