vol.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* vol: the volume management driver
*/
#include <sys/sysmacros.h>
/*
* NOTE:
*
* there was originally code in this module that would attempt to
* enqueue IO requests in a local queue if the reason for an IO
* failure was because the requested media was not present. there
* was a kernel thread that would run automatically when the requested
* media was inserted and would attempt to restart the queued IO's once
* the media was present. the code that enqueued the IO's had been
* ifdef'ed out for some time (since around version 1.27) due to some
* problems experienced with the sparc floppy driver. finally, the
* rest of the code, including the kernel thread itself was removed,
* because it wasn't really being used and was wasting resources. the
* code was removed as of version 1.67. if you're interested in seeing
* it, please retrieve that version.
*/
/* names */
#define VOLLONGNAME "Volume Management Driver, %I%"
static size_t vold_root_len = 0;
/*
* debug stuff
*/
#ifndef VOLDEBUG
#define VOLDEBUG 0
#endif
/*
* keep kvioc_queue and kvioc_event in sync. It is important that
* kve_next and kve_prev are in the same order and relative position
* in the respective structures.
*/
struct kvioc_queue {
struct kvioc_event *kve_next;
struct kvioc_event *kve_prev;
};
struct kvioc_event {
struct kvioc_event *kve_next;
struct kvioc_event *kve_prev;
struct vioc_event kve_event;
};
/*
* Data required to interface ioctls between vold.
*/
struct vol_ioctl {
int rval; /* return value from vold */
int nwait; /* # of threads waiting */
char active; /* set while waiting for a resp */
char closing; /* set while closing unit 0 */
char closewait; /* set while waiting for shutdown */
};
/*
* private device info -- controlling device "volctl"
*/
static struct volctl {
int ctl_daemon_pid; /* pid of daemon at work */
struct vol_ioctl ctl_insert;
struct vol_ioctl ctl_symname;
struct vol_ioctl ctl_symdev;
} volctl;
static struct pollhead vol_pollhead;
/*
* private device info, per active minor node.
*/
struct vol_tab {
int vol_cancel; /* cancel flag */
int vol_unit; /* minor number of this struct */
int vol_mtype; /* type of media (for checking) */
char *vol_path; /* path of mapped device */
char vol_relewait; /* release waiting flag */
char vol_locked; /* lock flag 1:READ 2:WRITE locked */
};
#define VOL_TAB_UNLOCKED 0
#define VOL_TAB_RD_LOCKED 1
#define VOL_TAB_WR_LOCKED 2
static void *voltab; /* dynamic voltab */
/* vol_flags */
/* vol_mtype */
/* flags to the vol_gettab function */
#define VGT_NOWAIT 0x01
#define VGT_WAITSIG 0x02
#define VGT_NEW 0x04
#define VGT_OPEN 0x08
#define VGT_CLOSE 0x10
#define VGT_NDELAY 0x20
/* local functions */
static void vol_cleanup(void);
#ifdef _SYSCALL32_IMPL
struct vioc_event *e);
#endif
static int vol_daemon_check(void);
int *rvalp, void *);
/* defaults */
/* devsw ops */
static struct cb_ops vol_cb_ops = {
volopen, /* open */
volclose, /* close */
volstrategy, /* strategy */
nodev, /* print */
nodev, /* dump */
volread, /* read */
volwrite, /* write */
volioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
volpoll, /* poll */
volprop_op, /* prop_op */
(struct streamtab *)0, /* streamtab */
};
void **result);
DEVO_REV, /* rev */
0, /* refcnt */
volinfo, /* info */
nulldev, /* identify */
nulldev, /* probe */
volattach, /* attach */
voldetach, /* detach */
nulldev, /* reset */
&vol_cb_ops, /* cb_ops */
(struct bus_ops *)0, /* bus_ops */
};
extern struct mod_ops mod_pseudodrvops;
extern struct mod_ops mod_driverops;
static struct modldrv vol_driver_info = {
&mod_driverops, /* modops */
VOLLONGNAME, /* name */
&vol_ops, /* dev_ops */
};
static struct modlinkage vol_linkage = {
MODREV_1, /* rev */
{ /* linkage */
NULL,
NULL,
NULL,
},
};
static kmutex_t floppy_chk_mutex;
/*
* Virtual driver loader entry points
*/
int
_init(void)
{
int ret;
DPRINTF("vol: _init\n");
/*
* The ddi_soft_state code automatically grows the array
* when more is asked for. DEFAULT_MAXUNIT is
* just a reasonable lower bound.
*/
if (ret != 0) {
return (-1);
}
if (ret != 0)
return (ret);
}
int
_fini(void)
{
int ret;
DPRINTF("vol: _fini\n");
if (ret != 0)
return (ret);
return (0);
}
int
{
}
/*
* Driver administration entry points
*/
static int
{
int unit;
int length;
int nbuf;
int i;
int err = DDI_SUCCESS;
DPRINTF("vol: attach: %d: dip %p cmd 0x%x\n",
/* check unit */
if (unit != 0)
return (ENXIO);
/* check command */
if (cmd != DDI_ATTACH) {
return (DDI_FAILURE);
}
"vol: attach: %d: already attached\n", unit);
return (DDI_FAILURE);
}
/* clear device entry, initialize locks, and save dev info */
/* get number of buffers, must use DDI_DEV_T_ANY */
DPRINTF("vol: couldn't get nbuf prop, using default %d\n",
nbuf = DEFAULT_NBUF;
err = 0; /* no biggie */
}
/* allocate buffers to stack with */
for (i = 0; (i < nbuf); ++i) {
"vol: attach: %d: could not allocate buf\n", unit);
goto out;
}
}
0, DDI_PSEUDO, 0)) != DDI_SUCCESS) {
"vol: attach: %d: ddi_create_minor_node '%s' failed\n",
unit, VOLCTLNAME);
goto out;
}
/*
* build our 'tp' for unit 0. makes things look better below
*/
(void) ddi_soft_state_zalloc(voltab, 0);
err = DDI_FAILURE;
goto out;
}
/* build the mapping */
/* initialize my linked list */
out:
/* cleanup or return success */
if (err != DDI_SUCCESS) {
}
return (err);
}
return (DDI_SUCCESS);
}
static int
{
int unit;
int stat;
/* get and check unit */
return (ENXIO);
switch (cmd) {
/* cleanup and detach */
case DDI_DETACH:
/*
* if the daemon has us open, say no without looking
* any further.
*/
return (DDI_FAILURE);
/*
* Free various data structures that have been allocated
* behind our back.
*/
/*
* Return our bufs to the world.
*/
}
/*
* Get rid of our various locks.
*/
/*
* A nice fresh volctl, for the next attach.
*/
stat = DDI_SUCCESS;
break;
default:
stat = DDI_FAILURE;
break;
}
return (stat);
}
/* ARGSUSED */
static int
{
int err = DDI_FAILURE;
DPRINTF("vol: info: dip %p cmd %d arg %p (%u.%u) result %p\n",
/* process command */
switch (cmd) {
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)0; /* only instance zero */
err = DDI_SUCCESS;
break;
case DDI_INFO_DEVT2DEVINFO:
err = DDI_SUCCESS;
}
break;
default:
break;
}
return (err);
}
/*
* Common entry points
*/
/* ARGSUSED3 */
static int
{
int unit;
int err = 0;
DPRINTF("vol: open: devp %p (%u.%u) flag %x otyp %x credp %p\n",
/* implement non-blocking open */
gflags |= VGT_NDELAY;
if (unit == 0) {
return (EINVAL);
}
/* get our vol structure for this unit */
return (err);
}
/*
* wrlock upgrade creates a race where other threads can
* modify flags while unlocking the rlock.
* We need write lock before testing all the flags.
*/
if (vol_tab_rwlock_upgrade_sig(tp)) {
/* we've lost the lock */
return (EINTR);
}
/* check for opening read-only with write flag set */
return (EROFS);
}
/* implement exclusive use */
return (EBUSY);
}
/* count and flag open */
} else {
}
/* release lock, and return */
return (0);
}
/* ARGSUSED3 */
static int
{
int unit;
int err;
DPRINTF("vol: close: dev %u.%u flag %x otyp %x credp %p\n",
/* unit 0 has already been closed */
return (0);
}
} else {
}
/*
* ST_ENXIO should be cleared on the last close.
* Otherwise, there is no way to reset the flags
* other than killing the vold.
*/
}
/*
* If we've closed the device clean up after ourselves
*/
#ifdef NOT_NEEDED
/*
* unmapping here means that every close unmaps so every open
* will have to remap
*/
/* "tp" is invalid after vol_unmap!!! */
#endif
}
if (unit == 0) {
/* vold is no longer responding */
volctl.ctl_daemon_pid = 0;
/* closing unit 0 by vold. clean up all vol_tabs */
vol_cleanup();
/*
* we've grabbed write lock for vol_tab unit#0, so no race
* between open to drop the ctl_open.
*/
}
/* release lock */
/* done */
return (0);
}
static int
{
int unit;
int err = 0;
DPRINTF2("vol: strategy: bp %p dev %u.%u off %lu len %ld\n",
if (unit == 0) {
return (0);
}
return (0);
}
/* it's not even open */
DPRINTF("vol: strategy: device not even open (ENXIO)\n");
return (0);
}
/* allocate new buffer */
panic("vol: strategy: bhead == NULL");
/*NOTREACHED*/
}
/* setup buffer */
if (err) {
else
/* free buffer */
/* release semaphore */
DPRINTF("vol: strategy: precheck failed, error %d\n",
err);
return (0);
}
}
/* release lock, pass request on to stacked driver */
/* pass request on to stacked driver */
return (bdev_strategy(mybp));
}
static int
{
DPRINTF2("vol: done: mybp %p bp %p dev %u.%u off %lu len %ld\n",
/*
* See NOTE comment at beginning of this module about code that
* used to queue failed IO requests.
*/
DPRINTF("vol: error %d from device (should retry)\n",
}
/* copy status */
/* free buffer */
/* release semaphore */
/* continue on with biodone() */
return (0);
}
/* ARGSUSED */
static int
{
int unit;
int err = 0;
int err1;
int found_media;
DPRINTF2("vol: read: dev %u.%u uiop %p credp %p\n",
(void *)credp);
if (unit == 0) {
return (ENXIO);
}
}
return (err);
}
goto out;
}
if (err) {
return (EINTR);
}
goto out;
}
}
for (;;) {
/* read data */
DPRINTF("vol: read: no device\n");
goto out;
}
return (EINTR);
}
/*
* if we got an error and media was actually
* in the drive, just return the error.
*/
if (found_media) {
break;
}
/*
* probably a cancel on the i/o.
*/
if (err1) {
break;
}
} else {
break;
}
}
/* release lock, return success */
out:
return (err);
}
/* ARGSUSED */
static int
{
int unit;
int err = 0;
int err1;
int found_media;
DPRINTF2("vol: write: dev %u.%u uiop %p credp %p\n",
(void *)credp);
if (unit == 0) {
return (ENXIO);
}
}
return (err);
}
goto out;
}
if (err) {
return (EINTR);
}
goto out;
}
}
for (;;) {
/* write data */
DPRINTF("vol: write: no device");
goto out;
}
return (EINTR);
}
/*
* if we got an error and media was actually
* in the drive, just return the error.
*/
if (found_media) {
break;
}
/*
* probably a cancel on the i/o.
*/
if (err1) {
break;
}
} else {
break;
}
}
/* release lock, return err */
out:
return (err);
}
/*
* Check the write to see if we are writing over the label
* on this unit. If we are, let the daemon know.
*/
static void
{
/*
* XXX: this is VERY incomplete.
* This only works with a full label write of the Sun label.
*/
if (uiop->uio_loffset == 0) {
/*
* We now need to invalidate the blocks that
* are cached for both the device we point at.
* Odds are good that the label was written
* through the raw device, and we don't want to
* read stale stuff.
*/
}
}
static int
{
int unit;
int err = 0;
DPRINTF2("vol: prop_op: dev %u.%u dip %p prop_op %d flags %x\n",
flags);
DPRINTF2(" name '%s' valuep %p lengthp %p\n",
/* send our props on to ddi_prop_op */
return (err);
}
if (unit == 0) {
return (DDI_PROP_NOT_FOUND);
}
err);
return (DDI_PROP_NOT_FOUND);
}
if (err) {
goto out;
}
/* get stacked dev info */
!= DDI_SUCCESS) {
"vol: prop_op: %d: could not get child dev info err %d\n",
goto out;
}
/* pass request on to stacked driver */
if (err) {
}
/* release lock, return err */
out:
return (err);
}
/* ARGSUSED */
static int
{
int err = 0;
DPRINTF("volioctl: dev=%u.%u cmd=%x arg=%lx mode=%x\n",
if (unit == 0) {
/* commands from vold */
switch (cmd) {
/*
* The daemon will call this if it's using the driver.
*/
case VOLIOCDAEMON: {
break;
}
if (volctl.ctl_daemon_pid == 0) {
} else {
if (vol_daemon_check() == 0) {
/* i'm vold who can change pid */
} else {
/* already a daemon running! */
}
}
#ifdef VOL_CLEANUP_BY_DAEMON
if (volctl.ctl_daemon_pid == 0) {
/* no daemon. clean up */
vol_cleanup();
}
#endif
break;
}
/*
* Establish a mapping between a unit (minor number)
* and a lower level driver (dev_t).
*/
case VOLIOCMAP: {
char *path;
if ((err = vol_daemon_check()) != 0)
break;
DPRINTF("vol: map:copyin broke\n");
break;
}
/* don't allow mapping for unit 0 */
break;
}
/* pathname cannot be longer than MAXPATHLEN */
if (pathlen >= MAXPATHLEN) {
err = ENAMETOOLONG;
break;
}
if (pathlen != 0) {
break;
}
}
/* copyins complete, get vol_tab */
DPRINTF("vol: map:null on vol %u, err %d\n",
break;
}
err = 0;
else
/* ready to grab the driver */
DPRINTF("vol: map:hold_inst broke\n");
break;
}
DPRINTF3("vol: ioctl: holding driver %u\n",
}
/* changing vol_tab, needs wlock(no interrupt) */
/* release old driver if it's been held */
} else {
/* clear data (in case of previous use) */
}
/* is this a read-only volume? */
}
/*
* if vim.vim_dev == NODEV, it means that the daemon
* is unblocking a "nodelay" request.
*/
/* build the mapping */
/* clear any pending cancel */
}
break;
}
/*
* Break a mapping established with the above
* map ioctl.
*/
case VOLIOCUNMAP: {
if ((err = vol_daemon_check()) != 0)
break;
DPRINTF("vol: unmap:copyin broke\n");
break;
}
if (tunit == 0) {
break;
}
break;
break;
}
/*
* Get an event. This is used by calling this
* ioctl until it returns EWOULDBLOCK. poll(2)
* is the mechanism for waiting around for an
* event to happen.
*/
case VOLIOCEVENT: {
if ((err = vol_daemon_check()) != 0)
break;
}
err = EWOULDBLOCK;
break;
}
}
}
#ifdef _SYSCALL32_IMPL
else {
struct vioc_event32 e32;
if (err == 0) {
}
}
}
#endif /* _SYSCALL32_IMPL */
/* add it back on err */
break;
}
break;
}
/*
* Deliver status (eject or don't eject) to a pending
* eject ioctl. That ioctl will then send down the
* eject to the device (or not).
*/
case VOLIOCEJECT: {
enum eject_state status;
if ((err = vol_daemon_check()) != 0)
break;
DPRINTF("VOLIOCEJECT: copyin error\n");
break;
}
break;
}
DPRINTF("VOLIOCEJECT: gettab error\n");
break;
}
status != VEJ_YESSTOP) {
break;
}
NULL)) != 0) {
break;
}
break;
}
/* Daemon response to setattr from user */
case VOLIOCDSATTR: {
int attr_err;
if ((err = vol_daemon_check()) != 0)
break;
break;
}
break;
}
break;
}
break;
&attr_ptr)) != 0) {
break;
}
/* shouldn't happen, but just in case */
} else {
}
break;
}
/* Daemon response to getattr from user */
case VOLIOCDGATTR: {
int attr_err;
if ((err = vol_daemon_check()) != 0)
break;
break;
}
break;
}
break;
}
break;
&attr_ptr)) != 0) {
break;
}
char *vstr;
int len;
/* put end mark so that strlen never overrun */
}
break;
}
/* Daemon response to insert from user */
case VOLIOCDCHECK:
if ((err = vol_daemon_check()) != 0)
break;
if ((int)arg < 0) {
break;
}
NULL)) != 0)
break;
break;
/* Daemon response to inuse from user */
case VOLIOCDINUSE:
if ((err = vol_daemon_check()) != 0)
break;
if ((int)arg < 0) {
break;
}
NULL)) != 0)
break;
break;
/* Daemon response to inuse from user */
case VOLIOCFLAGS: {
if ((err = vol_daemon_check()) != 0)
break;
break;
}
break;
}
break;
/* if we had an ENXIO error, then that's okay */
"volioctl: clearing gettab ENXIO or ENODEV\n");
err = 0;
}
/*
* vol_gettab() returns the vol_tab struct pointed
* to by tp locked in reader mode -- but, to
* change something in that struct, we need to
* lock it in writer mode
*/
/* set or clear the ST_ENXIO flag */
#ifdef DEBUG_ENXIO
(void) printf(
"volioctl: VOLIOCFLAGS(ST_ENXIO), unit %d (flags=0x%x, tp=%p)\n",
#endif
} else {
#ifdef DEBUG_ENXIO
(void) printf(
"volioctl: VOLIOCFLAGS(0), unit %d (flags=0x%x, tp=%p)\n",
#endif
}
"vol: volioctl: %s ST_ENXIO flag for unit %u\n",
break;
}
/* daemon response to symname request from user */
case VOLIOCDSYMNAME: {
char *symname;
if ((err = vol_daemon_check()) != 0)
break;
/* get the string struct */
break;
}
if (symname_len > VOL_SYMNAME_LEN) {
break;
}
/* if any string to get then get it */
if (symname_len != 0) {
/* allocate some memory for string */
/* grab the string */
break;
}
} else {
}
NULL)) != 0) {
break;
}
/* signal waiter that we have the info */
break;
}
/* daemon response to symdev request from user */
case VOLIOCDSYMDEV: {
char *symdev;
if ((err = vol_daemon_check()) != 0)
break;
/* get the string */
break;
}
if (symdev_len >= VOL_SYMDEV_LEN) {
break;
}
if (symdev_len != 0) {
/* get memory for string */
/* now get the dev string */
break;
}
} else {
}
/* lock data struct */
NULL)) != 0) {
break;
}
/* signal waiter that we have the info */
symdev_len, symdev);
break;
}
/*
* Begin: ioctls that joe random program can issue to
* the volctl device.
*/
/*
* Tell volume daemon to check for new media and wait
* for it to tell us if anything was there.
*/
case VOLIOCCHECK: {
int rval;
/*
* If there's no daemon, we already know the answer.
*/
if (volctl.ctl_daemon_pid == 0) {
break;
}
break;
else
if (err == 0)
break;
}
/*
* ask the volume daemon if it is running (dev_t ==
* this dev_t), or if it's controlling a particular
* device (any dev_t).
*/
case VOLIOCINUSE: {
int rval;
/*
* If there's no daemon, we already know the answer.
*/
if (volctl.ctl_daemon_pid == 0) {
break;
}
break;
else
if (err == 0)
break;
}
/* Cancel initiated from the daemon */
case VOLIOCCANCEL: {
if ((err = vol_daemon_check()) != 0)
break;
DPRINTF("vol: cancel:copyin broke\n");
break;
}
if (tunit == 0) {
break;
}
break;
err = 0;
/*
* need wrlock as we are changing vol_cancel.
*/
break;
}
/* set the volmgt root dir (defaults to "/vol") */
case VOLIOCDROOT: {
char *rptr;
if ((err = vol_daemon_check()) != 0)
break;
/* can't set if already set */
/* error */
break;
}
break;
}
break;
}
break;
}
/* someone has set the root */
} else {
}
break;
}
/* return where the vol root is */
case VOLIOCROOT: {
/* if no root set then punt */
/* allocate a default vol root */
KM_SLEEP);
}
if (vold_root_len >= sizeof (path))
else
/*
* copy in struct to know buf size at target
* for vold_root
*/
goto rootout;
}
/* check if our str len is out of range */
goto rootout;
}
/* all is ok, send back the vold_root */
}
break;
}
/* find a symname given a dev */
case VOLIOCSYMNAME: {
int symname_len = 0;
/* if there's no daemon then we can't check */
if (volctl.ctl_daemon_pid == 0) {
break;
}
/* get the struct */
break;
}
/* lock out others */
break;
/* tell the daemon that we want a symname */
else
&symname_len, &symname);
/* return result (if not interrupted) */
if (err == 0) {
/* is there enough room for the result ? */
if (symname_len >=
"volctl: no room for symname result\n");
} else if (symname_len == 0 ||
"volctl: no symname to copy out\n");
} else {
if (ddi_copyout(symname,
}
}
}
/*
* vol_ioctl_wait() may have failed, but vold
* may have allocated symname.
*/
/* release lock */
break;
}
/* find a dev path given a symname */
case VOLIOCSYMDEV: {
int symdev_len = 0;
/* if there's no daemon then we can't check */
if (volctl.ctl_daemon_pid == 0) {
break;
}
/* get the struct */
break;
}
/* see if user is providing a length too long */
break;
}
/* don't copyout garbage */
/* get the symname */
break;
}
/* lock out others */
break;
/* tell the daemon that we want a symdev */
/* wait for daemon to reply */
&symdev_len, &symdev);
/* return result (if not interrupted) */
if (err == 0) {
/* is there enough room for the result ? */
"volctl: no room for symdev result\n");
} else if (symdev_len == 0 ||
"volctl: no symdev to copy out\n");
} else {
if (ddi_copyout(symdev,
}
}
}
/* free room */
/* release lock */
break;
}
/*
* Create minor name for unit
*/
case VOLIOCCMINOR: {
char mname_chr[16];
char mname_blk[16];
if ((err = vol_daemon_check()) != 0)
break;
break;
}
VOLUNITNAME_BLK, (int)tunit);
}
break;
}
/*
* Remove minor name for unit
*/
case VOLIOCRMINOR: {
char mname[16];
if ((err = vol_daemon_check()) != 0)
break;
break;
}
VOLUNITNAME_BLK, (int)tunit);
VOLUNITNAME_CHR, (int)tunit);
break;
}
default:
break;
}
}
return (err);
/*NOTREACHED*/
}
/*
* This set of ioctls are available to be executed without
* having the unit available. vol_gettab() can be interrupted
* by signal.
*/
return (err);
err = 0;
switch (cmd) {
case VOLIOCINFO: {
/*
* Gather information about the unit. This is specific to
* volume management.
*
* XXX: we should just return an error if the amount of space
* the user has allocated for our return value is too small,
* but instead we just truncate and return ... ??
*/
return (EFAULT);
}
}
if (err == 0 &&
}
}
return (err);
}
/*
* Cancel i/o pending (i.e. waiting in vol_gettab) on
* a device. Cancel will persist until the last close.
*/
case VOLIOCCANCEL:
if (vol_tab_rwlock_upgrade_sig(tp)) {
break;
}
return (0);
case VOLIOCSATTR: {
int attr_err;
if (volctl.ctl_daemon_pid == 0) {
goto sattr_err;
}
mode) != 0) {
goto sattr_err;
}
/* zero out, otherwise, copyout kernel stack */
if (len > MAX_ATTR_LEN) {
goto sattr_err;
}
goto sattr_err;
}
if (len > MAX_ATTR_LEN) {
goto sattr_err;
}
goto sattr_err;
}
/*
* We need to release lock. Otherwise we fall into
* We are safe here; still tp is hold by refcnt, and
* also vol_attr is not used until we call vol_ioctl_exit().
*/
return (err);
}
if (err == 0) {
/* check response */
}
return (err);
return (err);
}
case VOLIOCGATTR: {
int attr_err;
if (volctl.ctl_daemon_pid == 0) {
goto gattr_dun;
}
mode) != 0) {
goto gattr_dun;
}
if (len > MAX_ATTR_LEN) {
goto gattr_dun;
}
goto gattr_dun;
}
/*
* do unlock, othewise deadlock.
*/
return (err);
}
if (err == 0) {
}
if (err == 0 &&
}
if (err == 0) {
}
}
return (err);
return (err);
}
case VOLIOCREMOUNT: /* the medium has a new partition structure */
/* may return ENODEV, even though event was queued ?? */
return (err);
case CDROMEJECT:
case FDEJECT:
case DKIOCEJECT:
return (EAGAIN);
}
break;
default:
break;
}
/*
* This is the part that passes ioctls on to the lower
* level devices. Some of these may have to be trapped
* and remapped.
*/
DPRINTF("vol: ioctl (to pass on): gettab on unit %d, err %d\n",
return (err);
}
/*
* this is almost certainly the ENXIO case for that special
* flag we set.
*/
if (err)
goto out;
goto out;
}
switch (cmd) {
/*
* Here's where we watch for the eject ioctls. Here, we enqueue
* a message for the daemon and wait around to hear the results.
*/
case CDROMEJECT:
case FDEJECT:
case DKIOCEJECT: {
int status;
if (volctl.ctl_daemon_pid == 0) {
return (ENXIO);
}
vej.viej_force = 0;
return (err);
}
/* ask daemon for permission to eject */
if (err == 0) {
}
if (err != 0) {
/* ioctl is either signalled or rejected by vold */
return (err);
}
/*
* clean out the block device.
*/
return (err);
}
/*
* The following ioctls cause volume management to
* reread the label after last close. The assumption is
* that these are only used during "format" operations
* and labels and stuff get written with these.
*/
case DKIOCSVTOC: /* set vtoc */
case DKIOCSGEOM: /* set geometry */
case DKIOCSAPART: /* set partitions */
case FDRAW: /* "raw" command to floppy */
/* FALL THROUGH */
default:
/*
* Pass the ioctl on down.
*/
DPRINTF("vol: tp->vol_dev = NODEV\n");
DPRINTF("vol: ioctl: dev %u.%u cmd %x arg %lx "
"mode %x credp %p rvalp %p\n",
break;
}
break;
}
/* release lock, return err */
out:
return (err);
}
static int
{
int unit;
int err = 0;
"vol: poll: dev %u.%u events 0x%x anyyet 0x%x revents 0x%x\n",
(int)*reventsp);
if (unit == 0) {
return (EINVAL);
}
if (events & POLLRDNORM) {
DPRINTF4("vol: poll: got a POLLRDNORM\n");
DPRINTF3("vol: poll: we have data\n");
*reventsp |= POLLRDNORM;
return (0);
}
}
if (!anyyet) {
*phpp = &vol_pollhead;
*reventsp = 0;
}
return (0);
}
return (err);
}
goto out;
}
out:
return (err);
}
static void
{
struct kvioc_event *kvie;
cred_t *c;
proc_t *p;
/* build our user friendly slop. Probably not DDI compliant */
if (drv_getparm(UCRED, &c) != 0) {
DPRINTF("vol: vol_enqueue: couldn't get ucred\n");
uid = -1;
gid = -1;
} else {
}
if (drv_getparm(UPROCP, &p) != 0) {
DPRINTF("vol: vol_enqueue: couldn't get uprocp\n");
} else {
}
switch (type) {
case VIE_MISSING:
break;
case VIE_INSERT:
break;
case VIE_CHECK:
break;
case VIE_INUSE:
break;
case VIE_EJECT:
break;
case VIE_DEVERR:
break;
case VIE_CLOSE:
break;
case VIE_REMOVED:
break;
case VIE_CANCEL:
break;
case VIE_NEWLABEL:
break;
case VIE_GETATTR:
case VIE_SETATTR:
break;
case VIE_SYMNAME:
break;
case VIE_SYMDEV:
break;
case VIE_REMOUNT:
break;
default:
return;
}
} else {
}
}
#ifdef _SYSCALL32_IMPL
static int
{
int err = 0;
switch (e->vie_type) {
case VIE_MISSING:
e->vie_missing.viem_tty))
break;
case VIE_EJECT:
break;
case VIE_DEVERR:
break;
case VIE_CLOSE:
break;
case VIE_CANCEL:
break;
case VIE_NEWLABEL:
break;
case VIE_INSERT:
e->vie_insert.viei_dev))
break;
case VIE_SETATTR:
case VIE_GETATTR:
/* copy all the data including the null terminate */
MAX_ATTR_LEN + 1);
MAX_ATTR_LEN + 1);
break;
case VIE_INUSE:
break;
case VIE_CHECK:
break;
case VIE_REMOVED:
break;
case VIE_SYMNAME:
e->vie_symname.vies_dev))
break;
case VIE_SYMDEV:
VOL_SYMNAME_LEN + 1);
break;
case VIE_REMOUNT:
break;
}
return (err);
}
#endif /* _SYSCALL32_IMPL */
/*
* vol_gettab() returns a vol_tab_t * for the unit. If the unit
* isn't mapped, we tell vold about it and wait around until
* a mapping occurs
*
* upon successful completion, the vol_tab for which the pointer is
*/
static struct vol_tab *
{
int rv;
struct ve_missing ve;
*err = 0;
/*
* If unit#0 has not opened, VGT_OPEN can be used to forcibly
* grab the vol_tab. Similarly, if unit#0 was closing, VGT_CLOSE
* can be used.
*/
DPRINTF("vol: gettab: ctl unit not open (ENXIO)!\n");
return (NULL);
}
if (unit < 0) {
DPRINTF("vol: vol_gettab: negative unit number!\n");
return (NULL);
}
/* this unit not yet created */
/* the "create" flag is set, so create it */
if (*err) {
goto out;
}
DPRINTF("vol: soft state was null!\n");
goto out;
}
} else {
/* didn't build a new one and we don't have one */
DPRINTF("vol: vol_gettab: ENODEV\n");
goto out;
}
}
tp->vol_refcnt++;
/* now we know the unit exists */
/* keep track of the largest unit number we've seen */
}
if (flags & VGT_WAITSIG) {
if (vol_tab_rlock_sig(tp)) {
return (NULL);
}
} else {
}
/* got it! */
goto out;
}
if (flags & VGT_NOWAIT) {
/* no ops, and they don't want to wait */
DPRINTF("vol: vol_gettab: no mapping for %d, no waiting\n",
unit);
goto out;
}
/* no vol_devops yet, but caller is willing to wait */
#ifdef DEBUG_ENXIO
} else {
DPRINTF("vol_gettab: no mapping for %d: it's MISSING "
}
#endif
/*
* It's been unmapped, but the requested behavior is to
* return ENXIO rather than waiting around. The enxio
* behavior is cleared on close.
*/
DPRINTF("vol: vol_gettab: no mapping for %d, doing ENXIO\n",
unit);
goto out;
}
/*
* there isn't a mapping -- enqueue a missing message to the
* daemon and wait around until it appears
*/
/*
* hang around until a unit appears or we're cancelled.
*/
if (tp->vol_cancel) {
break; /* a volcancel has been done */
}
/*
* Due to the lock ordering between rwlock and muxmutex, we
* need to release muxmuex prior to releasing rlock after
* cv_wait is complete. Between those two calls, corresponding
* node can be unmapped. As a result, we will go into
* cv_wait() again without posting VIE_MISSING, and thus
* cv_wait sleeps forever. Therefore, we need to post
* VIE_MISSING every time before we go into cv_wait().
*/
DPRINTF("vol: vol_gettab: enqueing missing event, unit %d "
/*
* We need to ensure that the thread is sleeping in the
* cond when it's signalled. If muxmutex was acquired after
* the vol_tab_unlock() below, it creates a race with
* VOLIOCMAP which would cause loss of signal. Therefore
* muxmutex should be held here before releasing rlock.
*/
/* release rlock so that VOLIOCMAP can update devops */
/* wait right here */
if (flags & VGT_WAITSIG) {
if (rv == 0) {
DPRINTF("vol: vol_gettab: eintr -> cnx\n");
/*
* found pending signal. We don't cancel
* the request here, otherwise next open
* would fail with ENXIO until vold creates
* a new mapping, also media can be ejected
* by a Ctrl-C.
*/
break;
}
} else {
/* can't be interrupted by a signal */
}
/* we may have signalled from close(). */
if (volctl.ctl_daemon_pid == 0) {
break;
}
/*
* muxmutex is no longer necessary. It should be released
* before acquiring rlock, so that thread running VOLIOCMAP
* etc won't deadlock.
*/
/* here, node can be unmapped again. see comments above */
/* rlock again as we will test vol_devops */
if (flags & VGT_WAITSIG) {
if (vol_tab_rlock_sig(tp)) {
break;
}
} else {
}
DPRINTF2("vol: vol_gettab: insert cv wakeup rcvd\n");
break;
}
out:
/*
* If the device is "cancelled", don't return the tp unless
* the caller really wants it (nowait and ndelay).
*/
!(flags & VGT_NDELAY)) {
}
if (*err != 0) {
DPRINTF("vol: vol_gettab: err=%d unit=%d, tp=%p\n",
}
return (tp);
}
/*
* Unmap *tp. Removes it from the ddi_soft_state list.
*/
static void
{
char mname[16];
/* wait until everyone is done with it */
/* release underlying driver */
/* get rid of the thing */
/* remove minor node */
/* done */
}
/*
* This is called when the volume daemon closes its connection.
* It cleans out our mux.
*/
static void
vol_cleanup(void)
{
int i;
int err;
struct kvioc_event *kve;
DPRINTF("vol_cleanup: entering (daemon dead?)\n");
/*
* We need to grab muxmutex to make sure that all threads
* opening unit>0 are aware of closing, and ctl_muxunit will
* never be changed.
*/
continue;
DPRINTF("vol_cleanup: unit %d\n", i);
/* cancel pending eject requests */
/*
* acquire muxmutex, to make sure that all the threads
* are aware of ctl_closing flag, and running threads have
* either failed or already acquired rlock in vol_gettab().
* write lock is required as we will touch vol_cancel.
*/
/* send a "cancel" for pending missing events */
/* tp is no longer valid after a vol_umnap() */
}
DPRINTF("vol: vol_cleanup: cleared from 0 to %d\n",
volctl.ctl_maxunit = 0;
/*
* handle threads waiting for replies from the daemon
*/
/*
* Free up anything lurking on the event queue.
*/
}
/* wake up threads if sleeping in poll() */
/*
* release memory only needed while the daemon is running
*/
vold_root_len = 0;
}
/* re-enable the ioctls */
volctl.ctl_closing = 0;
}
/*
* Check the floppy drive to see if there's a floppy still in the
* drive. If there isn't this function will block until the floppy
* is either back in the drive or the i/o is cancelled. If found_media
* is supplied the status will be returned through it.
*/
static int
{
int err = 0;
int badnews = 0;
DPRINTF2("vol: checkmedia\n");
/* do the grotty stuff to get the answer */
/* check to see if there's no media in the drive */
if (badnews) {
/* there's no media in the drive */
if (found_media) {
}
if (vol_tab_rwlock_upgrade_sig(tp))
return (EINTR);
/* unmap the device */
/* get the mapping for this device, waiting if needed */
DPRINTF("vol: checkmedia: calling gettab\n");
return (EINTR);
if (vol_tab_rlock_sig(tp))
return (EINTR);
DPRINTF("vol: checkmedia: gettab has returned\n");
} else {
/* there is media in the drive */
if (found_media) {
}
}
/* all done */
return (err);
}
/*
* return the bad news: media there (0) or not (1).
*/
static int
{
int err;
int fl_rval = 0; /* bitmap word: all bits clear initially */
case MT_FLOPPY:
/* check for a floppy disk in the drive */
/* ensure we have a dev to do the ioctl on */
/* it's been unmapped (so probably not there) */
DPRINTF("vol: checkmedia: volume unmapped\n");
return (1);
}
/*
* XXX this mutex make sure that we're only doing one of
* XXX these ioctl's at a time. this avoids a deadlock
* XXX in the floppy driver.
*/
if (err != 0) {
DPRINTF("vol: checkmedia: FDGETCHANGE failed %d\n",
err);
/* if we got an error, assume the worst */
return (1);
}
/* is media present ?? */
if (fl_rval & FDGC_CURRENT) {
DPRINTF("vol: checkmedia: no media! (fl_rval = 0x%x)\n",
fl_rval);
return (1); /* no media in the drive */
}
return (0); /* media in the drive */
default:
return (1);
}
/*NOTREACHED*/
}
/*
* Release the driver and cleanup unnecessary stuff in vol_tab.
*/
static void
{
}
/* drop media related flags */
tp->vol_pathlen = 0;
}
/*
* return 0 if you are vold.
*/
static int
vol_daemon_check(void)
{
if (volctl.ctl_daemon_pid == 0)
return (ENXIO);
return (EPERM);
else
return (0);
}
/*
* client side functions can be used to interface ioctls to vold.
* vold is responding for the ioctls by using vold_xx functions.
*
* vol_ioctl_init()
* vol_ioctl_fini()
* initialize/destroy mutex, condvar etc.
*
* vol_ioctl_enter()
* serialize ioctl request to make sure only one thread
*
* vol_ioctl_wait()
* set argument in argp which will be used by by vold ioctl, and
*
* vol_ioctl_exit()
* exit from critical section and let the next thread go into
* the ioctl.
*
* vol_ioctl_fail()
* wakes up threads either waiting for entering ioctl or waiting
* for a response from vold. This will make those ioctls fail.
*/
static void
{
}
static void
{
}
static int
{
int r;
return (ENXIO);
}
/* I'm the last */
}
if (r == 0)
return (EINTR);
else
return (ENXIO);
}
if (r == 0) {
/* signal pending */
return (EINTR);
}
}
return (0);
}
static int
{
int err = 0;
/* we are interrupted */
break;
}
}
/* the daemon is dying, or has already died */
}
/* return data from vold */
return (err);
}
static void
{
/*
* I'm the last one. wake up the thread sleeping
* in vol_ioctl_fail.
*/
}
}
static void
{
/*
* client is waiting for response.
* vold may or may not have responded to the request.
* If vold has already responded, rval has been set.
* We don't reset rval in such case. If vold has not
* yet responded, we set rval to pull thread out from
* loop in ioctl_wait. vold will see vic->closing, so
* it never reset rval again.
*/
}
}
/*
* If ioctl is active, or someone is waiting for ioctl.
*/
}
}
static void
{
}
/*
* vold side ioctl functions to interface with client side.
*
* vold_ioctl_enter()
* double check to see if there is a request and vold can
* respond. Retrieve data passed by vol_ioctl_wait().
*
* vold_ioctl_respond()
* set return values, and signal thread waiting for a
* response.
*
* vold_ioctl_exit()
* just exit. release lock.
*/
static int
{
/* should not happen, but */
return (ENXIO);
}
return (EAGAIN);
}
return (0);
}
static void
{
}
static void
{
}
/*
* but need vol specific version which could prevent deadlock and race
* condition mainly created by lock upgrade sequence.
*
* vol_tab_init(), vol_tab_fini()
* initialize/destroy the mutex/condvars.
*
* vol_tab_rlock_unlocked()
* vol_tab_rlock()/vol_tab_rlock_sig()
* acquire reader lock. If write locked, just sleep. If read locked,
* and set reader count to 1.
*
* vol_tab_unlock()/vol_tab_unlock_unlocked()
* release lock. If read locked, decrement reader count. If reader
* count becomes 0, clear lock word. If write locked, just clear lock
* flag. If anyone waiting for lock, wake up the threads sleeping
* in either rlock or upgrade.
*
* vol_tab_rwlock_upgrade()
* upgrades the acquired reader lock to writer lock.
* If there is no other thread holding rlock, then straight upgrade
* to write lock. If no, unlock the read lock, and wait until lock
* is released.
*
* vol_tab_rele()
* decrement the reference count. reference count is incremented
* in vol_gettab(). If thread calling close() is waiting for
* vol_tab to be released(no reference), wake up the thread.
*
* vol_tab_unlock_and_rele()
* release lock, and decrement the reference count.
*
* vol_tab_rele_wait()
* waiting until no other thread has referece to the vol_tab.
*/
static void
{
}
static void
{
}
static int
{
/* wait until wlock is released */
tp->vol_lckwaiter++;
if (waitsig) {
&tp->vol_rwlck_mutex) == 0) {
tp->vol_lckwaiter--;
return (EINTR);
}
} else {
}
tp->vol_lckwaiter--;
}
tp->vol_nreader++;
return (0);
}
static void
{
}
static int
{
int r;
return (r);
}
static void
{
if (--tp->vol_nreader == 0) {
}
}
}
}
static void
{
}
static int
{
tp->vol_nreader = 0;
return (0);
}
tp->vol_lckwaiter++;
if (waitsig) {
&tp->vol_rwlck_mutex) == 0) {
tp->vol_lckwaiter--;
return (EINTR);
}
} else {
}
tp->vol_lckwaiter--;
}
return (0);
}
static void
{
}
static int
{
int r;
return (r);
}
static void
{
tp->vol_refcnt--;
/* I'm the last */
}
}
static void
{
}
static void
{
tp->vol_refcnt--;
while (tp->vol_refcnt > 0) {
tp->vol_relewait = 0;
}
}