zfs_ioctl.c revision c9030f6c93613fe30ee0c16f92b96da7816ac052
/*
* 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
*/
/*
* Portions Copyright 2011 Martin Matuska
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2014, Joyent, Inc. All rights reserved.
* Copyright (c) 2011, 2014 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, Nexenta Systems, Inc. All rights reserved.
*/
/*
* ZFS ioctls.
*
*
* 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).
* The ioctl numbers can change from release to release, because
* the caller (libzfs) must be matched to the kernel.
*
* 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).
*
* 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.
*/
#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/dmu_impl.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 <sys/dmu_send.h>
#include <sys/dsl_destroy.h>
#include <sys/dsl_bookmark.h>
#include <sys/dsl_userhold.h>
#include <sys/zfeature.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);
extern uint_t rrw_tsd_key;
static uint_t zfs_allow_log_key;
typedef int zfs_ioc_legacy_func_t(zfs_cmd_t *);
typedef enum {
typedef enum {
POOL_CHECK_NONE = 1 << 0,
typedef struct zfs_ioc_vec {
const char *zvec_name;
/* 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);
}
/*
* Return non-zero if the spa version is less than requested version.
*/
static int
{
return (1);
}
}
return (0);
}
/*
* 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);
}
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) &&
if (INGLOBALZONE(curproc)) {
/*
* If the fs is zoned, only root can access it from the
* global zone.
*/
} else {
/*
* If we are in a local zone, the 'zoned' property must be set.
*/
if (!zoned)
/* must be writable by this zone */
if (!writable)
}
return (0);
}
static int
{
}
static int
{
}
static int
{
int error;
if (error == 0) {
if (error != 0)
}
return (error);
}
static int
{
int error;
dsl_pool_t *dp;
if (error != 0)
return (error);
if (error != 0) {
return (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 != 0)
new_default = TRUE;
/* The label must be translatable */
/*
* 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 (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,
if (!zoned) {
}
/*
* 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 != 0)
if (new_default) {
goto out_check;
}
} 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))
break;
case ZFS_PROP_QUOTA:
case ZFS_PROP_SNAPSHOT_LIMIT:
if (!INGLOBALZONE(curproc)) {
char setpoint[MAXNAMELEN];
/*
* Unprivileged users are allowed to modify the
* limit on things *under* (ie. contained by)
* the thing they own.
*/
setpoint))
}
break;
case ZFS_PROP_MLSLABEL:
if (!is_system_labeled())
int err;
if (err != 0)
return (err);
}
break;
}
}
/* ARGSUSED */
static int
{
int error;
if (error != 0)
return (error);
/*
* permission to set permissions will be evaluated later in
* dsl_deleg_can_allow()
*/
return (0);
}
/* ARGSUSED */
static int
{
}
/* ARGSUSED */
static int
{
dsl_pool_t *dp;
char *cp;
int error;
/*
* Generate the current snapshot name from the given objsetid, then
*/
if (error != 0)
return (error);
if (error != 0) {
return (error);
}
return (error);
}
/* ARGSUSED */
static int
{
}
/* ARGSUSED */
static int
{
int error;
return (error);
/* Now make sure mntpnt and dataset are ZFS */
}
}
int
{
if (!INGLOBALZONE(curproc))
if (secpolicy_nfs(cr) == 0) {
return (0);
} else {
}
}
int
{
if (!INGLOBALZONE(curproc))
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 {
cp[0] = '\0';
}
return (0);
}
int
{
int error;
ZFS_DELEG_PERM_MOUNT, cr)) != 0)
return (error);
}
/* ARGSUSED */
static int
{
}
/*
* Destroying snapshots with delegated permissions requires
* descendant mount and destroy permissions.
*/
/* ARGSUSED */
static int
{
int error = 0;
/*
* 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).
*/
error = 0;
}
if (error != 0)
break;
}
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);
}
/* ARGSUSED */
static int
{
}
/* ARGSUSED */
static int
{
dsl_pool_t *dp;
int error;
if (error != 0)
return (error);
if (error != 0)
return (error);
if (error == 0) {
char parentname[MAXNAMELEN];
if (error != 0) {
return (error);
}
if (error == 0) {
}
}
return (error);
}
/* ARGSUSED */
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,
}
/*
* Check for permission to create each snapshot in the nvlist.
*/
/* ARGSUSED */
static int
{
int error = 0;
break;
}
*atp = '\0';
*atp = '@';
if (error != 0)
break;
}
return (error);
}
/*
* Check for permission to create each snapshot in the nvlist.
*/
/* ARGSUSED */
static int
{
int error = 0;
break;
}
*hashp = '\0';
*hashp = '#';
if (error != 0)
break;
}
return (error);
}
/* ARGSUSED */
static int
{
int error = 0;
break;
}
*hashp = '\0';
*hashp = '#';
/*
* Ignore any filesystems that don't exist (we consider
* their bookmarks "already destroyed"). Remove
* the name from the nvl here in case the filesystem
* is created between now and when we try to destroy
* the bookmark (in which case we don't want to
* destroy it since we haven't checked for permission).
*/
error = 0;
}
if (error != 0)
break;
}
return (error);
}
/* ARGSUSED */
static int
{
/*
* Even root must have a proper TSD so that we know what pool
* to log to.
*/
return (0);
}
static int
{
char parentname[MAXNAMELEN];
int error;
char *origin;
sizeof (parentname))) != 0)
return (error);
ZFS_DELEG_PERM_CLONE, cr)) != 0)
return (error);
ZFS_DELEG_PERM_CREATE, cr)) != 0)
return (error);
return (zfs_secpolicy_write_perms(parentname,
}
/*
* SYS_CONFIG privilege, which is not available in a local zone.
*/
/* ARGSUSED */
static int
{
return (0);
}
/*
* Policy for object to name lookups.
*/
/* ARGSUSED */
static int
{
int error;
return (0);
return (error);
}
/*
* Policy for fault injection. Requires all privileges.
*/
/* ARGSUSED */
static int
{
return (secpolicy_zinject(cr));
}
/* ARGSUSED */
static int
{
if (prop == ZPROP_INVAL) {
} else {
}
}
static int
{
if (err)
return (err);
/*
* themself, allow it.
*/
return (0);
} else {
return (0);
}
}
}
static int
{
if (err)
return (err);
}
/* ARGSUSED */
static int
{
}
/* ARGSUSED */
static int
{
int error;
if (error != 0)
char fsname[MAXNAMELEN];
if (error != 0)
return (error);
if (error != 0)
return (error);
}
return (0);
}
/* ARGSUSED */
static int
{
int error;
char fsname[MAXNAMELEN];
if (error != 0)
return (error);
if (error != 0)
return (error);
}
return (0);
}
/*
* Policy for allowing temporary snapshots to be taken or released
*/
static int
{
/*
* A temporary snapshot is the same as a snapshot,
* hold, destroy and release all rolled into one.
* Delegated diff alone is sufficient that we allow this.
*/
int error;
ZFS_DELEG_PERM_DIFF, cr)) == 0)
return (0);
if (error == 0)
if (error == 0)
if (error == 0)
return (error);
}
/*
* 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)
iflag)) != 0) {
return (error);
}
return (error);
}
return (0);
}
/*
* Reduce the size of this nvlist until it can be serialized in 'max' bytes.
* Entries will be removed from the end of the nvlist, and one int32 entry
* named "N_MORE_ERRORS" will be added indicating how many entries were
* removed.
*/
static int
{
int n = 0;
if (max < 1024)
do {
n++;
}
return (0);
}
static int
{
int error = 0;
} else {
}
return (error);
}
static int
{
int error;
if (error != 0)
return (error);
}
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.
* If 'writer' is set, the z_teardown_lock will be held for RW_WRITER,
* which prevents all vnode ops from running.
*/
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 (error);
}
static void
{
} else {
}
}
static int
{
int error;
return (error);
return (error);
}
if (props) {
(void) nvlist_lookup_uint64(props,
if (!SPA_VERSION_IS_SUPPORTED(version)) {
goto pool_props_bad;
}
if (nvl) {
if (error != 0) {
return (error);
}
}
if (error != 0)
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) {
int err;
}
if (props)
return (error);
}
static int
{
int error;
if (error == 0)
return (error);
}
static int
{
int error;
return (error);
}
/*
* inputs:
* zc_name name of the pool
*
* outputs:
* zc_cookie real errno
* zc_nvlist_dst config nvlist
* zc_nvlist_dst_size size of config nvlist
*/
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 (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 (error);
}
static int
{
char *hist_buf;
int error;
return (error);
}
}
return (error);
}
static int
{
int error;
if (error == 0) {
}
return (error);
}
static int
{
}
/*
* 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 (error);
}
/*
* inputs:
* zc_name name of filesystem
* zc_obj object to find
*
* outputs:
* zc_stat stats on object
* zc_value path to object
*/
static int
{
int error;
/* XXX reading from objset not owned */
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.
*
* 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.
*/
}
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);
}
}
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;
if (error == 0) {
}
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 = 0;
/*
* 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 (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);
}
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;
if (error != 0) {
}
/*
* A dataset name of maximum length cannot have any snapshots,
* so exit immediately.
*/
}
NULL);
if (error == 0) {
if (error == 0) {
if (error == 0)
}
}
/* if we failed, undo the @ that we tacked on to zc_name */
if (error != 0)
return (error);
}
static int
{
unsigned int vallen;
const char *domain;
char *dash;
int err;
&pair) != 0)
}
/*
* A correctly constructed propname is encoded as
* userquota@<rid>-<domain>.
*/
vallen != 3)
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_SNAPSHOT_LIMIT:
if (intval == UINT64_MAX) {
/* clearing the limit, just do it */
err = 0;
} else {
}
/*
* Set err to -1 to force the zfs_set_prop_nvlist code down the
* default path to set the value in the nvlist.
*/
if (err == 0)
err = -1;
break;
case ZFS_PROP_RESERVATION:
break;
case ZFS_PROP_REFRESERVATION:
break;
case ZFS_PROP_VOLSIZE:
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 last error
* encountered. If the caller provides a non-NULL errlist, it will be filled in
* with the list of names of all the properties that failed along with the
* corresponding error numbers.
*
* If every property is set successfully, zero is returned and errlist is not
* modified.
*/
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 {
}
} else if (err == 0) {
const char *unused;
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;
}
} else {
intval);
}
if (err != 0) {
err);
}
}
}
}
return (rv);
}
/*
* Check that all the properties are valid user properties.
*/
static int
{
int error = 0;
if (!zfs_prop_user(propname) ||
return (error);
return (SET_ERROR(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) {
}
}
errors = fnvlist_alloc();
if (error == 0)
}
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))
} else if (prop == ZFS_PROP_VOLSIZE ||
prop == ZFS_PROP_VERSION) {
} else {
}
switch (type) {
case PROP_TYPE_STRING:
break;
case PROP_TYPE_NUMBER:
case PROP_TYPE_INDEX:
break;
default:
}
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.
*/
}
/* property name has been validated by zfs_secpolicy_inherit_prop() */
}
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
*/
}
/*
* 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 != 0) {
} 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:
* os parent objset pointer (NULL if root fs)
* fuids_ok fuids allowed in this version of the spa?
* sa_ok SAs allowed in this version of the spa?
* createprops list of properties requested by creator
*
* 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)))
/*
* 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);
}
/*
* innvl: {
* "type" -> dmu_objset_type_t (int32)
* (optional) "props" -> { prop -> value }
* }
*
* outnvl: propname -> error code (int32)
*/
static int
{
int error = 0;
zfs_creat_t zct = { 0 };
switch (type) {
case DMU_OST_ZFS:
break;
case DMU_OST_ZVOL:
break;
default:
break;
}
if (type == DMU_OST_ZVOL) {
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)
(void) dsl_destroy_head(fsname);
}
return (error);
}
/*
* innvl: {
* "origin" -> name of origin snapshot
* (optional) "props" -> { prop -> value }
* }
*
* outnvl: propname -> error code (int32)
*/
static int
{
int error = 0;
char *origin_name;
if (error != 0)
return (error);
/*
* It would be nice to do this atomically.
*/
if (error == 0) {
if (error != 0)
(void) dsl_destroy_head(fsname);
}
return (error);
}
/*
* innvl: {
* "snaps" -> { snapshot1, snapshot2 }
* (optional) "props" -> { prop -> value (string) }
* }
*
* outnvl: snapshot -> error code (int32)
*/
static int
{
return (error);
if (!nvlist_empty(props) &&
/*
* The snap name must contain an @, and the part after it must
* contain only valid characters.
*/
/*
* The snap must be in the specified pool.
*/
/* This must be the only snap of this fs. */
== 0) {
}
}
}
return (error);
}
/*
* innvl: "message" -> string
*/
/* ARGSUSED */
static int
{
char *message;
int error;
char *poolname;
/*
* The poolname in the ioctl is not set, we get it from the TSD,
* which was set at the end of the last successful ioctl that allows
* logging. The secpolicy func already checked that it is set.
* Only one log ioctl is allowed after each successful ioctl, so
* we clear the TSD here.
*/
if (error != 0)
return (error);
}
}
return (error);
}
/*
* The dp_config_rwlock must not be held when calling this, because the
* unmount may need to write out data.
*
* This function is best-effort. Callers must deal gracefully if it
* remains mounted (or is remounted after this call).
*
* Returns 0 if the argument is not a snapshot, or it is not currently a
* filesystem, or we were able to unmount it. Returns error code otherwise.
*/
int
zfs_unmount_snap(const char *snapname)
{
int err;
return (0);
return (0);
if (err != 0)
/*
* Always force the unmount for snapshots.
*/
return (0);
}
/* ARGSUSED */
static int
{
return (zfs_unmount_snap(snapname));
}
/*
* When a clone is destroyed, its origin may also need to be destroyed,
* in which case it must be unmounted. This routine will do that unmount
* if necessary.
*/
void
zfs_destroy_unmount_origin(const char *fsname)
{
int error;
if (error != 0)
return;
char originname[MAXNAMELEN];
(void) zfs_unmount_snap(originname);
} else {
}
}
/*
* innvl: {
* "snaps" -> { snapshot1, snapshot2 }
* (optional boolean) "defer"
* }
*
* outnvl: snapshot -> error code (int32)
*
*/
/* ARGSUSED */
static int
{
}
}
/*
* Create bookmarks. Bookmark names are of the form <fs>#<bmark>.
* All bookmarks must be in the same pool.
*
* innvl: {
* bookmark1 -> snapshot1, bookmark2 -> snapshot2
* }
*
* outnvl: bookmark -> error code (int32)
*
*/
/* ARGSUSED */
static int
{
char *snap_name;
/*
* Verify the snapshot argument.
*/
/* Verify that the keys (bookmarks) are unique */
}
}
}
/*
* innvl: {
* property 1, property 2, ...
* }
*
* outnvl: {
* bookmark name 1 -> { property 1, property 2, ... },
* bookmark name 2 -> { property 1, property 2, ... }
* }
*
*/
static int
{
}
/*
* innvl: {
* bookmark name 1, bookmark name 2
* }
*
* outnvl: bookmark -> error code (int32)
*
*/
static int
{
/*
* The bookmark name must contain an #, and the part after it
* must contain only valid characters.
*/
/*
* The bookmark must be in the specified pool.
*/
}
return (error);
}
/*
* 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 != 0)
return (err);
}
else
return (err);
}
/*
* fsname is name of dataset to rollback (to most recent snapshot)
*
* innvl is not used.
*
* outnvl: "target" -> name of most recent snapshot
* }
*/
/* ARGSUSED */
static int
{
int error;
if (error == 0) {
int resume_err;
}
} else {
}
return (error);
}
static int
{
char fullname[MAXNAMELEN];
return (zfs_unmount_snap(fullname));
}
/*
* 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
{
char *at;
/* snaps must be in same fs */
int error;
*at = '\0';
recursive ? DS_FIND_CHILDREN : 0);
if (error != 0) {
*at = '@';
return (error);
}
}
*at = '@';
return (error);
} else {
}
}
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 (err);
return (0);
}
}
if (issnap)
/*
* 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 &&
}
if (intval == ZIO_COMPRESS_ZLE &&
if (intval == ZIO_COMPRESS_LZ4) {
return (err);
if (!spa_feature_is_enabled(spa,
}
}
/*
* 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) &&
}
}
break;
case ZFS_PROP_COPIES:
break;
case ZFS_PROP_DEDUP:
break;
case ZFS_PROP_SHARESMB:
break;
case ZFS_PROP_ACLINHERIT:
if (intval == ZFS_ACL_PASSTHROUGH_X &&
}
break;
}
}
/*
* Checks for a race condition to make sure we don't increment a feature flag
* multiple times.
*/
static int
{
return (0);
else
}
/*
* The callback invoked on feature activation in the sync task caused by
* zfs_prop_activate_feature.
*/
static void
{
}
/*
* Activates a feature on a pool in response to a property setting. This
* creates a new sync task which modifies the pool to reflect the feature
* as being active.
*/
static int
{
int err;
/* EBUSY here indicates that the feature is already active */
return (err);
else
return (0);
}
/*
* 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];
*tosnap++ = '\0';
return (error);
}
if (error != 0)
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 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)
} else {
}
}
if (props_error == 0) {
}
}
if (zc->zc_nvlist_dst_size != 0 &&
/*
* 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 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)
* zc_guid if set, estimate size of stream only. zc_cookie is ignored.
* output size in zc_objset_type.
* zc_flags if =1, WRITE_EMBEDDED records are permitted
*
* outputs:
* zc_objset_type estimated size, if zc_guid is set
*/
static int
{
int error;
dsl_pool_t *dp;
if (error != 0)
return (error);
if (error != 0) {
return (error);
}
}
if (estimate) {
dsl_pool_t *dp;
if (error != 0)
return (error);
if (error != 0) {
return (error);
}
if (zc->zc_fromobj != 0) {
if (error != 0) {
return (error);
}
}
&zc->zc_objset_type);
} else {
}
return (error);
}
/*
* inputs:
* zc_name name of snapshot on which to report progress
* zc_cookie file descriptor of send stream
*
* outputs:
* zc_cookie number of bytes written in send stream thus far
*/
static int
{
dsl_pool_t *dp;
int error;
if (error != 0)
return (error);
if (error != 0) {
return (error);
}
/*
* Iterate over all the send streams currently active on this dataset.
* If there's one which matches the specified file descriptor _and_ the
* stream was started by the current process, return the progress of
* that stream.
*/
break;
}
else
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
*/
}
}
spa->spa_last_open_failed = 0;
} else {
int err;
}
}
}
if (error != 0)
return (error);
} else {
}
}
/*
*/
if (zio_resume(spa) != 0)
return (error);
}
static int
{
int error;
if (error != 0)
return (error);
/*
* If a resilver is already in progress then set the
* spa_scrub_reopen flag to B_TRUE so that we don't restart
* the scan as a side effect of the reopen. Otherwise, let
* vdev_open() decided if a resilver is required.
*/
return (0);
}
/*
* 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;
if (error != 0)
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)
if (error != 0)
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) {
zfsvfs);
}
}
if (error == 0)
} else {
/* XXX kind of reading contents without owning */
if (error != 0)
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 (0);
}
static int
{
int error;
int opcode;
case ZFS_SHARE_NFS:
case ZFS_UNSHARE_NFS:
if (zfs_nfsshare_inited == 0) {
}
if (znfsexport_fs == NULL &&
((znfsexport_fs = (int (*)(void *))
}
error = zfs_init_sharefs();
if (error != 0) {
}
zfs_nfsshare_inited = 1;
}
break;
case ZFS_SHARE_SMB:
case ZFS_UNSHARE_SMB:
if (zfs_smbshare_inited == 0) {
ddi_modopen("drv/smbsrv",
}
}
error = zfs_init_sharefs();
if (error != 0) {
}
zfs_smbshare_inited = 1;
}
break;
default:
}
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[] = {
};
/*
* inputs:
* zc_name name of containing filesystem
* zc_obj object # beyond which we want next in-use object #
*
* outputs:
* zc_obj next in-use object #
*/
static int
{
int error;
if (error != 0)
return (error);
return (error);
}
/*
* inputs:
* zc_name name of filesystem
* zc_value prefix name for snapshot
* zc_cleanup_fd cleanup-on-exit file descriptor for calling process
*
* outputs:
* zc_value short name of new snapshot
*/
static int
{
char *snap_name;
char *hold_name;
int error;
if (error != 0)
return (error);
if (error == 0)
return (error);
}
/*
* inputs:
* zc_name name of "to" snapshot
* zc_value name of "from" snapshot
* zc_cookie file descriptor to write diff data on
*
* outputs:
* dmu_diff_record_t's to the file descriptor
*/
static int
{
int error;
return (error);
}
/*
* 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 */
}
/*
* Create share dir if its missing.
*/
if (zfsvfs->z_shares_dir == 0) {
if (error != 0) {
} else {
}
if (error != 0) {
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);
}
/*
* innvl: {
* "holds" -> { snapname -> holdname (string), ... }
* (optional) "cleanup_fd" -> fd (int32)
* }
*
* outnvl: {
* snapname -> error value (int32)
* ...
* }
*/
/* ARGSUSED */
static int
{
int cleanup_fd = -1;
int error;
if (error != 0)
if (error != 0)
return (error);
}
if (minor != 0)
return (error);
}
/*
* innvl is not used.
*
* outnvl: {
* holdname -> time added (uint64 seconds since epoch)
* ...
* }
*/
/* ARGSUSED */
static int
{
}
/*
* innvl: {
* snapname -> { holdname, ... }
* ...
* }
*
* outnvl: {
* snapname -> error value (int32)
* ...
* }
*/
/* ARGSUSED */
static int
{
}
/*
* inputs:
* zc_name name of new filesystem or snapshot
* zc_value full name of old snapshot
*
* outputs:
* zc_cookie space in bytes
* zc_objset_type compressed space in bytes
* zc_perm_action uncompressed space in bytes
*/
static int
{
int error;
dsl_pool_t *dp;
if (error != 0)
return (error);
if (error != 0) {
return (error);
}
if (error != 0) {
return (error);
}
return (error);
}
/*
* innvl: {
* "firstsnap" -> snapshot name
* }
*
* outnvl: {
* "used" -> space in bytes
* "compressed" -> compressed space in bytes
* "uncompressed" -> uncompressed space in bytes
* }
*/
static int
{
int error;
dsl_pool_t *dp;
char *firstsnap;
if (error != 0)
return (error);
if (error != 0) {
return (error);
}
if (error != 0) {
return (error);
}
return (error);
}
/*
* innvl: {
* "fd" -> file descriptor to write stream to (int32)
* (optional) "fromsnap" -> full snap name to send an incremental from
* (optional) "embedok" -> (value ignored)
* presence indicates DRR_WRITE_EMBEDDED records are permitted
* }
*
* outnvl is unused
*/
/* ARGSUSED */
static int
{
int error;
int fd;
if (error != 0)
return (error);
}
/*
* Determine approximately how large a zfs send stream will be -- the number
* of bytes that will be written to the fd supplied to zfs_ioc_send_new().
*
* innvl: {
* (optional) "fromsnap" -> full snap name to send an incremental from
* }
*
* outnvl: {
* "space" -> bytes of space (uint64)
* }
*/
static int
{
dsl_pool_t *dp;
int error;
char *fromname;
if (error != 0)
return (error);
if (error != 0) {
return (error);
}
if (error == 0) {
if (error != 0) {
return (error);
}
}
return (error);
}
static void
{
}
/*
* See the block comment at the beginning of this file for details on
* each argument to this function.
*/
static void
{
/* if we are logging, the name must be valid */
}
static void
{
}
static void
{
}
static void
{
}
static void
{
}
static void
{
}
static void
{
}
static void
{
}
static void
zfs_ioctl_init(void)
{
/* IOCTLS that use the legacy function signature */
/*
* pool destroy, and export don't log the history as part of
* zfsdev_ioctl, but rather zfs_ioc_pool_export
* does the logging of those commands.
*/
}
int
{
int error;
if (check & POOL_CHECK_NONE)
return (0);
if (error == 0) {
}
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 (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
{
const zfs_ioc_vec_t *vec;
char *saved_poolname = NULL;
if (minor != 0 &&
if (error != 0) {
goto out;
}
if (zc->zc_nvlist_src_size != 0) {
if (error != 0)
goto out;
}
/*
* the lower layers.
*/
switch (vec->zvec_namecheck) {
case POOL_NAME:
else
break;
case DATASET_NAME:
else
break;
case NO_NAME:
break;
}
if (error != 0)
goto out;
/* legacy ioctls can modify zc_name */
int puterror = 0;
/*
* Add the innvl to the lognv before calling the func,
* in case the func changes the innvl.
*/
if (vec->zvec_allow_log) {
lognv = fnvlist_alloc();
if (!nvlist_empty(innvl)) {
innvl);
}
}
outnvl = fnvlist_alloc();
if (!nvlist_empty(outnvl)) {
outnvl);
}
}
int smusherror = 0;
if (vec->zvec_smush_outnvlist) {
}
if (smusherror == 0)
}
if (puterror != 0)
} else {
}
out:
char *s = tsd_get(zfs_allow_log_key);
if (s != NULL)
strfree(s);
} else {
if (saved_poolname != NULL)
}
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,
};
static void
zfs_allow_log_destroy(void *arg)
{
}
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 (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
{
}