/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright 2013, 2016 Joyent, Inc. All rights reserved.
* Copyright (c) 2014 by Delphix. All rights reserved.
*/
#include <sys/sysmacros.h>
#include <sys/zfs_ioctl.h>
#include <sys/vfs_opreg.h>
/* Below are protected by devzvol_mtx */
/*
* ready to go
*/
int (*szcm)(char *);
/*
* they are enabled, preserving the historic behavior.
*/
int
{
return (-1);
}
int
{
return (-1);
}
int
{
int rc;
&devzvol_lh, devzvol_li))
return (-1);
return (rc);
}
if ((szcm = (int (*)(char *))
return (rc);
}
return (rc);
}
return (-1);
return (0);
}
void
{
(void) ddi_modclose(zfs_mod);
}
}
int
{
int unused;
int rc;
if (cmd != ZFS_IOC_POOL_CONFIGS)
if (!devzvol_isopen) {
if ((rc = devzvol_open_zfs()) == 0) {
} else {
if (cmd != ZFS_IOC_POOL_CONFIGS)
return (ENXIO);
}
}
KM_SLEEP);
&unused);
int newsize;
goto again;
}
if (alloc_size == NULL)
else
*alloc_size = size;
if (cmd != ZFS_IOC_POOL_CONFIGS)
return (rc);
}
/* figures out if the objset exists and returns its type */
int
{
int rc;
if (is_snapshot && !devzvol_snaps_allowed)
return (ENOTSUP);
nvl = fnvlist_alloc();
return (rc);
}
/*
* path and an optional name (can be NULL).
*
* Note that if the name param is NULL, then path must be an
* actual dataset's directory and not one of the top-level
* specific dataset.
*/
char *
{
char *dsname;
const char *ptr;
int dslen;
return (NULL);
return (NULL);
else
return (NULL);
if (*ptr == '/')
ptr++;
return (NULL);
if (dslen)
dslen++; /* plus null */
if (name)
if (*ptr) {
if (name)
}
if (name)
return (dsname);
}
/*
* check if the zvol's sdev_node is still valid, which means make
* sure the zvol is still valid. zvol minors aren't proactively
* destroyed when the zvol is destroyed, so we use a validator to clean
* these up (in other words, when such nodes are encountered during
* subsequent lookup() and readdir() operations) so that only valid
* nodes are returned. The ordering between devname_lookup_func and
* devzvol_validate is a little inefficient in the case of invalid
* or stale nodes because devname_lookup_func calls
* devzvol_create_{dir, link}, then the validator says it's invalid,
* and then the node gets cleaned up.
*/
int
{
char *dsname;
int rc;
/*
* validate only READY nodes; if someone is sitting on the
* directory of a dataset that just got destroyed we could
* get a zombie node which we just skip.
*/
return (SDEV_VTOR_SKIP);
}
return (SDEV_VTOR_VALID);
return (SDEV_VTOR_INVALID);
/*
* Leave any nodes alone that have been explicitly created by
* sdev profiles.
*/
return (SDEV_VTOR_VALID);
}
if (rc != 0) {
/*
* Explicitly passed-through zvols in our sdev profile can't
* be created as prof_* shadow nodes, because in the GZ they
* are symlinks, but in the NGZ they are actual device files.
*
* The objset_check will fail on these as they are outside
* any delegated dataset (zfs will not allow ioctl access to
* them from this zone). We still want them to work, though.
*/
} else {
return (SDEV_VTOR_INVALID);
}
}
sdcmn_err13((" v_type %d do_type %d",
do_type != DMU_OST_ZVOL) ||
return (SDEV_VTOR_STALE);
}
long val = 0;
return (SDEV_VTOR_STALE);
}
}
return (SDEV_VTOR_VALID);
}
/*
* Taskq callback to update the devzvol_zclist.
*
* We need to defer this to the taskq to avoid it running with a user
* context that might be associated with some non-global zone, and thus
* not being able to list all of the pools on the entire system.
*/
/*ARGSUSED*/
static void
{
int rc;
switch (rc) {
case 0:
/* new generation */
if (devzvol_zclist)
/* Keep the alloc'd size, not the nvlist size. */
break;
default:
/*
* Either there was no change in pool configuration
* since we last asked (rc == EEXIST) or we got a
* catastrophic error.
*
* Give up memory and exit.
*/
size);
break;
}
}
static void
devzvol_update_zclist(void)
{
if (devzvol_zclist_task_running == B_TRUE) {
goto wait;
}
wait:
}
/*
* Creates sub-directories for each zpool as needed in response to a
*/
void
{
int pools = 0;
int rc;
sdcmn_err13(("devzvol_create_pool_dirs"));
devzvol_zclist_size, &nv, 0);
if (rc) {
devzvol_gen = 0;
devzvol_zclist_size = 0;
goto out;
}
/* should either work, or not be visible from a zone */
if (rc == 0)
pools++;
}
if (devzvol_isopen && pools == 0) {
/* clean up so zfs can be unloaded */
}
out:
}
/*ARGSUSED3*/
static int
{
gethrestime(&now);
return (0);
}
/*ARGSUSED3*/
static int
{
int rc;
char *dsname;
char *x;
return (-1);
}
/*
* This is a valid zvol; create a symlink that points to the
*/
*pathname = '\0';
strlen(ZVOL_FULL_RDEV_DIR)) == 0)
return (0);
}
/* Clean zvol sdev_nodes that are no longer valid. */
static void
{
}
while (dv) {
switch (devzvol_validate(dv)) {
case SDEV_VTOR_VALID:
case SDEV_VTOR_SKIP:
continue;
case SDEV_VTOR_INVALID:
sdcmn_err7(("prunedir: destroy invalid "
break;
}
continue;
}
/* remove the cache node */
}
}
/*
* This function is used to create a dir or dev inside a zone's /dev when the
* zone has a zvol that is dynamically created within the zone (i.e. inside
* of a delegated dataset. Since there is no /devices tree within a zone,
* making symlinks.
*/
static int
{
int res;
char *dsname;
gethrestime(&now);
return (ENOENT);
/*
* objset_check will succeed on any valid objset in the global
* zone, and any valid delegated dataset. It will fail, however,
* in non-global zones on explicitly whitelisted zvol devices
* that are outside any delegated dataset.
*
* The directories leading up to the zvol device itself will be
* created by prof for us in advance (and will always validate
* because of the matching check in devzvol_validate). The zvol
* device itself can't be created by prof though because in the
* GZ it's a symlink, and in the NGZ it is not. So, we create
* such zvol device files here.
*/
} else {
return (ENOENT);
}
}
if (do_type == DMU_OST_ZVOL)
if (expected_type == VDIR) {
} else {
int rc;
return (ENOENT);
}
else
}
if (res != 0)
return (ENOENT);
return (0);
}
/*ARGSUSED*/
static int
{
char *dsname;
int error;
/* execute access is required to search the directory */
return (error);
if (!SDEV_IS_GLOBAL(parent)) {
int res;
/*
* If we're in the global zone and reach down into a non-global
* of all of the zvol devices for every zone into the non-global
* zone's /dev tree. This could be a big security hole. To
* prevent this, disallow the global zone from looking inside
* delegated datasets, which cannot be used by the global zone.
*/
if (getzoneid() == GLOBAL_ZONEID)
return (EPERM);
/*
* We won't find a zvol that was dynamically created inside
* a NGZ, within a delegated dataset, in the zone's dev profile
* but prof_lookup will also find it via sdev_cache_lookup.
*/
/*
* We have to create the sdev node for the dymamically
* created zvol.
*/
return (ENOENT);
}
return (res);
}
/*
* Don't let the global-zone style lookup succeed here when we're not
* running in the global zone. This can happen because prof calls into
* us (in prof_filldir) trying to create an explicitly passed-through
* zvol device outside any delegated dataset.
*
* We have to stop this here or else we will create prof shadows of
* the global zone symlink, which will make no sense at all in the
* non-global zone (it has no /devices for the symlink to point at).
*
* These zvols will be created later (at access time) by mk_ngz_node
* instead. The dirs leading up to them will be created by prof
* internally.
*
* We have to return EPERM here, because ENOENT is given special
* meaning by prof in this context.
*/
if (getzoneid() != GLOBAL_ZONEID) {
return (EPERM);
}
if (dsname) {
if (error != 0) {
goto out;
}
if (do_type == DMU_OST_ZVOL)
}
/*
* the callbacks expect:
*
* parent->sdev_path nm
*
* sdev_name is always last path component of sdev_path
*/
if (expected_type == VDIR) {
} else {
}
out:
if (dsname)
return (error);
}
/*
* We allow create to find existing nodes
* - if the node doesn't exist - EROFS
* - creating an existing dir read-only succeeds, otherwise EISDIR
* - exclusive creates fail - EEXIST
*/
/*ARGSUSED2*/
static int
{
int error;
NULL);
if (error == 0) {
else
if (error) {
} else
}
return (error);
}
void
{
int rc;
char *ptr;
goto skip;
if (rc == 0) {
goto skip;
} else {
/*
* EBUSY == problem with zvols's dmu holds?
* EPERM when in a NGZ and traversing up and out.
*/
goto skip;
}
if (arg == ZFS_IOC_DATASET_LIST_NEXT &&
skip:
}
}
void
{
}
/*ARGSUSED4*/
static int
{
char *ptr;
}
if (uiop->uio_offset == 0)
}
return (ENOENT);
ptr++;
}
};