summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/vol.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/io/vol.c')
-rw-r--r--usr/src/uts/common/io/vol.c3717
1 files changed, 0 insertions, 3717 deletions
diff --git a/usr/src/uts/common/io/vol.c b/usr/src/uts/common/io/vol.c
deleted file mode 100644
index 64db74b970..0000000000
--- a/usr/src/uts/common/io/vol.c
+++ /dev/null
@@ -1,3717 +0,0 @@
-/*
- * 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
- * or http://www.opensolaris.org/os/licensing.
- * 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/types.h>
-#include <sys/param.h>
-#include <sys/buf.h>
-#include <sys/conf.h>
-#include <sys/proc.h>
-#include <sys/user.h>
-#include <sys/cred.h>
-#include <sys/file.h>
-#include <sys/open.h>
-#include <sys/poll.h>
-#include <sys/errno.h>
-#include <sys/ioccom.h>
-#include <sys/cmn_err.h>
-#include <sys/kmem.h>
-#include <sys/uio.h>
-#include <sys/cpu.h>
-#include <sys/modctl.h>
-#include <sys/stat.h>
-#include <sys/dkio.h>
-#include <sys/cdio.h>
-#include <sys/fdio.h>
-#include <sys/sysmacros.h>
-#include <sys/ddi.h>
-#include <sys/sunddi.h>
-#include <sys/fdio.h>
-#include <sys/vol.h>
-#include <sys/session.h>
-#include <sys/systm.h>
-#include <sys/debug.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%"
-#define VOLNBUFPROP "nbuf" /* number of bufs property */
-
-
-static char *vold_root = NULL; /* location of vol root */
-static size_t vold_root_len = 0;
-#define VOL_ROOT_DEFAULT "/vol" /* default vold_root */
-#define VOL_ROOT_DEFAULT_LEN (strlen(VOL_ROOT_DEFAULT)) /* it's length */
-
-
-/*
- * debug stuff
- */
-
-#ifndef VOLDEBUG
-#define VOLDEBUG 0
-#endif
-
-int voldebug = VOLDEBUG;
-
-#define DPRINTF if (voldebug > 0) printf
-#define DPRINTF2 if (voldebug > 1) printf
-#define DPRINTF3 if (voldebug > 2) printf
-#define DPRINTF4 if (voldebug > 3) printf
-
-
-/*
- * 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 {
- kmutex_t mutex;
- kcondvar_t cv; /* cond to wait for ioctl resp */
- kcondvar_t s_cv; /* cond to serialize the ioctl */
- kcondvar_t close_cv; /* cond to wait for unref */
- uintptr_t argp; /* value passed to vold ioctl */
- uintptr_t rptr; /* return pointer from vold */
- 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 {
- dev_info_t *ctl_dip; /* dev info */
- struct buf *ctl_bhead; /* bufs to use for strategy */
- uint_t ctl_evcnt; /* count of events on queue */
- uint_t ctl_maxunit; /* largest unit # we've seen */
- uint_t ctl_open; /* control port open flag */
- int ctl_daemon_pid; /* pid of daemon at work */
- struct kvioc_queue ctl_events; /* queue of events for vold */
- struct kvioc_event *ctl_evend; /* pointer to end of queue */
- ksema_t ctl_bsema; /* semaphore for vol_bhead */
- kmutex_t ctl_bmutex; /* mutex for vol_bhead */
- kmutex_t ctl_muxmutex; /* mutex for voltab */
- kmutex_t ctl_evmutex; /* mutex for events */
- kmutex_t ctl_volrmutex; /* mutex for vold_root */
- struct vol_ioctl ctl_insert;
- struct vol_ioctl ctl_inuse;
- struct vol_ioctl ctl_symname;
- struct vol_ioctl ctl_symdev;
- uint_t ctl_closing; /* device being closed */
-} volctl;
-
-static struct pollhead vol_pollhead;
-
-/*
- * private device info, per active minor node.
- */
-struct vol_tab {
- dev_t vol_dev; /* stacked device */
- struct dev_ops *vol_devops; /* stacked dev_ops */
- uint_t vol_bocnt; /* open count, block */
- uint_t vol_cocnt; /* open count, character */
- uint_t vol_locnt; /* open count, layered */
- uint_t vol_flags; /* miscellaneous flags */
- int vol_cancel; /* cancel flag */
- int vol_unit; /* minor number of this struct */
- int vol_mtype; /* type of media (for checking) */
- uint64_t vol_id; /* id of the volume */
- char *vol_path; /* path of mapped device */
- size_t vol_pathlen; /* length of above path */
- kcondvar_t vol_incv; /* insertion condvar */
- struct vol_ioctl vol_eject;
- struct vol_ioctl vol_attr;
- kmutex_t vol_rwlck_mutex; /* mutex for rw lock */
- kcondvar_t vol_rwlck_cv; /* cv for reader/writer lock */
- uint_t vol_nreader; /* number of readers */
- uint_t vol_lckwaiter; /* number of lock waiter */
- uint_t vol_refcnt; /* number of references */
- kcondvar_t vol_rele_cv; /* cv waiting for release */
- 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 */
-#define ST_OPEN 0x0001 /* device is open */
-#define ST_EXCL 0x0002 /* device is open exclusively */
-#define ST_ENXIO 0x0004 /* return enxio till close */
-#define ST_CHKMEDIA 0x0008 /* device should be checked b4 i/o */
-#define ST_RDONLY 0x0010 /* volume is read-only */
-
-/* vol_mtype */
-#define MT_FLOPPY 0x0001 /* floppy that supports FDGETCHANGE */
-
-/* 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_enqueue(enum vie_event type, void *data);
-static int vol_done(struct buf *bp);
-static void vol_cleanup(void);
-static void vol_unmap(struct vol_tab *);
-static void vol_checkwrite(struct vol_tab *tp,
- struct uio *uiop, int unit);
-#ifdef _SYSCALL32_IMPL
-static int vol_event32(struct vioc_event32 *e32p,
- struct vioc_event *e);
-#endif
-static struct vol_tab *vol_gettab(int unit,
- uint_t flags, int *error);
-static int vol_checkmedia(struct vol_tab *tp, int *found_media);
-static int vol_checkmedia_machdep(struct vol_tab *tp);
-
-static void vol_release_driver(struct vol_tab *tp);
-static int vol_daemon_check(void);
-
-static void vol_ioctl_init(struct vol_ioctl *vic);
-static void vol_ioctl_fini(struct vol_ioctl *vic);
-static int vol_ioctl_enter(struct vol_ioctl *vic);
-static int vol_ioctl_wait(struct vol_ioctl *vic,
- int *rvalp, void *);
-static void vol_ioctl_exit(struct vol_ioctl *vic);
-static void vol_ioctl_fail(struct vol_ioctl *vic);
-static void vol_ioctl_enable(struct vol_ioctl *vic);
-static int vold_ioctl_enter(struct vol_ioctl *vic, void *rptrp);
-static void vold_ioctl_respond(struct vol_ioctl *vic,
- int rval, void *rptr);
-static void vold_ioctl_exit(struct vol_ioctl *vic);
-
-static void vol_tab_init(struct vol_tab *tp);
-static void vol_tab_fini(struct vol_tab *tp);
-static void vol_tab_rlock(struct vol_tab *tp);
-static int vol_tab_rlock_sig(struct vol_tab *tp);
-static void vol_tab_rwlock_upgrade(struct vol_tab *tp);
-static int vol_tab_rwlock_upgrade_sig(struct vol_tab *tp);
-static void vol_tab_unlock(struct vol_tab *tp);
-static void vol_tab_rele(struct vol_tab *tp);
-static void vol_tab_unlock_and_rele(struct vol_tab *tp);
-static void vol_tab_rele_wait(struct vol_tab *tp);
-
-/* defaults */
-#define DEFAULT_NBUF 20 /* default number of bufs to allocate */
-#define DEFAULT_MAXUNIT 100 /* default number of minor units to alloc */
-
-/* devsw ops */
-static int volopen(dev_t *devp, int flag, int otyp, cred_t *credp);
-static int volclose(dev_t dev, int flag, int otyp, cred_t *credp);
-static int volstrategy(struct buf *bp);
-static int volread(dev_t dev, struct uio *uiop, cred_t *credp);
-static int volwrite(dev_t dev, struct uio *uiop, cred_t *credp);
-static int volprop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
- int flags, char *name, caddr_t valuep, int *lengthp);
-static int volioctl(dev_t dev, int cmd, intptr_t arg, int mode,
- cred_t *credp, int *rvalp);
-static int volpoll(dev_t dev, short events, int anyyet,
- short *reventsp, struct pollhead **phpp);
-
-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 */
- D_NEW | D_MP | D_64BIT, /* flags */
-};
-
-static int volattach(dev_info_t *dip, ddi_attach_cmd_t cmd);
-static int voldetach(dev_info_t *dip, ddi_detach_cmd_t cmd);
-static int volinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
- void **result);
-
-static struct dev_ops vol_ops = {
- 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;
-
-extern int ddi_create_internal_pathname(dev_info_t *dip, char *name,
- int spec_type, minor_t minor_num);
-
-static struct modldrv vol_driver_info = {
- &mod_driverops, /* modops */
- VOLLONGNAME, /* name */
- &vol_ops, /* dev_ops */
-};
-
-static struct modlinkage vol_linkage = {
- MODREV_1, /* rev */
- { /* linkage */
- &vol_driver_info,
- 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.
- */
- ret = ddi_soft_state_init(&voltab, sizeof (struct vol_tab),
- DEFAULT_MAXUNIT);
- if (ret != 0) {
- cmn_err(CE_CONT, "vol: _init, could not init soft state");
- return (-1);
- }
-
- ret = mod_install(&vol_linkage);
- if (ret != 0)
- ddi_soft_state_fini(&voltab);
-
- return (ret);
-}
-
-
-int
-_fini(void)
-{
- int ret;
-
- DPRINTF("vol: _fini\n");
- ret = mod_remove(&vol_linkage);
- if (ret != 0)
- return (ret);
-
- ddi_soft_state_fini(&voltab);
- return (0);
-}
-
-
-int
-_info(struct modinfo *modinfop)
-{
- DPRINTF("vol: _info: modinfop %p\n", (void *)modinfop);
- return (mod_info(&vol_linkage, modinfop));
-}
-
-
-/*
- * Driver administration entry points
- */
-static int
-volattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
-{
- struct vol_tab *tp;
- int unit;
- int length;
- int nbuf;
- int i;
- int err = DDI_SUCCESS;
-
- DPRINTF("vol: attach: %d: dip %p cmd 0x%x\n",
- ddi_get_instance(dip), (void *)dip, (int)cmd);
-
- unit = ddi_get_instance(dip);
-
- /* check unit */
- if (unit != 0)
- return (ENXIO);
-
- /* check command */
- if (cmd != DDI_ATTACH) {
- cmn_err(CE_CONT, "vol: attach: %d: unknown cmd %d\n",
- unit, cmd);
- return (DDI_FAILURE);
- }
-
- if (volctl.ctl_dip != NULL) {
- cmn_err(CE_CONT,
- "vol: attach: %d: already attached\n", unit);
- return (DDI_FAILURE);
- }
-
- /* clear device entry, initialize locks, and save dev info */
- bzero(&volctl, sizeof (volctl));
- volctl.ctl_dip = dip;
-
- /* get number of buffers, must use DDI_DEV_T_ANY */
- length = sizeof (nbuf);
- if ((err = ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
- 0, VOLNBUFPROP, (caddr_t)&nbuf, &length)) != DDI_SUCCESS) {
- DPRINTF("vol: couldn't get nbuf prop, using default %d\n",
- DEFAULT_NBUF);
- nbuf = DEFAULT_NBUF;
- err = 0; /* no biggie */
- }
-
- DPRINTF2("vol: attach: %d: nbuf %d\n", ddi_get_instance(dip), nbuf);
-
- sema_init(&volctl.ctl_bsema, 0, NULL, SEMA_DRIVER, NULL);
-
- /* allocate buffers to stack with */
- volctl.ctl_bhead = NULL;
- for (i = 0; (i < nbuf); ++i) {
- struct buf *bp;
-
- if ((bp = getrbuf(KM_NOSLEEP)) == NULL) {
- cmn_err(CE_CONT,
- "vol: attach: %d: could not allocate buf\n", unit);
- err = ENOMEM;
- goto out;
- }
- bp->b_chain = volctl.ctl_bhead;
- volctl.ctl_bhead = bp;
- sema_v(&volctl.ctl_bsema);
- }
-
- /* create minor node for /dev/volctl */
- if ((err = ddi_create_minor_node(dip, VOLCTLNAME, S_IFCHR,
- 0, DDI_PSEUDO, 0)) != DDI_SUCCESS) {
- cmn_err(CE_CONT,
- "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);
- if ((tp = (struct vol_tab *)ddi_get_soft_state(voltab, 0)) == NULL) {
- cmn_err(CE_CONT, "vol: attach, could not get soft state");
- err = DDI_FAILURE;
- goto out;
- }
-
- /* build the mapping */
- tp->vol_dev = NODEV;
- tp->vol_devops = NULL;
-
- /* initialize my linked list */
- volctl.ctl_events.kve_next =
- (struct kvioc_event *)&volctl.ctl_events;
- volctl.ctl_evcnt = 0;
- volctl.ctl_evend = NULL;
-
-out:
- /* cleanup or return success */
- if (err != DDI_SUCCESS) {
- ddi_remove_minor_node(dip, NULL);
- while (volctl.ctl_bhead != NULL) {
- struct buf *bp;
-
- bp = volctl.ctl_bhead;
- volctl.ctl_bhead = bp->b_chain;
- freerbuf(bp);
- }
- sema_destroy(&volctl.ctl_bsema);
- bzero(&volctl, sizeof (volctl));
- return (err);
- }
-
- mutex_init(&volctl.ctl_bmutex, NULL, MUTEX_DRIVER, NULL);
- mutex_init(&volctl.ctl_muxmutex, NULL, MUTEX_DRIVER, NULL);
- mutex_init(&volctl.ctl_evmutex, NULL, MUTEX_DRIVER, NULL);
- mutex_init(&volctl.ctl_volrmutex, NULL, MUTEX_DRIVER, NULL);
- vol_ioctl_init(&volctl.ctl_insert);
- vol_ioctl_init(&volctl.ctl_inuse);
- vol_ioctl_init(&volctl.ctl_symname);
- vol_ioctl_init(&volctl.ctl_symdev);
-
- ddi_report_dev(dip);
-
- return (DDI_SUCCESS);
-}
-
-
-static int
-voldetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
-{
- int unit;
- int stat;
-
- DPRINTF("vol: detach: %d: dip %p cmd %d\n", ddi_get_instance(dip),
- (void *)dip, (int)cmd);
-
- /* get and check unit */
- if ((unit = ddi_get_instance(dip)) != 0)
- return (ENXIO);
-
- switch (cmd) {
- /* cleanup and detach */
- case DDI_DETACH:
- /*
- * if the daemon has us open, say no without looking
- * any further.
- */
- if (volctl.ctl_open != 0)
- return (DDI_FAILURE);
- /*
- * Free various data structures that have been allocated
- * behind our back.
- */
- ddi_soft_state_free(voltab, 0);
-
- /*
- * Return our bufs to the world.
- */
- while (volctl.ctl_bhead != NULL) {
- struct buf *bp;
-
- bp = volctl.ctl_bhead;
- volctl.ctl_bhead = bp->b_chain;
- freerbuf(bp);
- }
-
- /*
- * Get rid of our various locks.
- */
- sema_destroy(&volctl.ctl_bsema);
- mutex_destroy(&volctl.ctl_bmutex);
- mutex_destroy(&volctl.ctl_muxmutex);
- mutex_destroy(&volctl.ctl_evmutex);
- mutex_destroy(&volctl.ctl_volrmutex);
- vol_ioctl_fini(&volctl.ctl_insert);
- vol_ioctl_fini(&volctl.ctl_inuse);
- vol_ioctl_fini(&volctl.ctl_symname);
- vol_ioctl_fini(&volctl.ctl_symdev);
-
- /*
- * A nice fresh volctl, for the next attach.
- */
- bzero(&volctl, sizeof (volctl));
-
- stat = DDI_SUCCESS;
- break;
-
- default:
- cmn_err(CE_CONT, "vol: detach: %d: unknown cmd %d\n",
- unit, cmd);
- stat = DDI_FAILURE;
- break;
- }
- return (stat);
-}
-
-
-/* ARGSUSED */
-static int
-volinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
-{
- dev_t dev = (dev_t)arg;
- int unit = getminor(dev);
- int err = DDI_FAILURE;
-
- DPRINTF("vol: info: dip %p cmd %d arg %p (%u.%u) result %p\n",
- (void *)dip, (int)cmd, arg, getmajor(dev), unit, (void *)result);
-
- /* process command */
- switch (cmd) {
- case DDI_INFO_DEVT2INSTANCE:
- *result = (void *)0; /* only instance zero */
- err = DDI_SUCCESS;
- break;
-
- case DDI_INFO_DEVT2DEVINFO:
- if (volctl.ctl_dip) {
- *result = (void *)volctl.ctl_dip;
- err = DDI_SUCCESS;
- }
- break;
-
- default:
- cmn_err(CE_CONT, "vol: info: %d: unknown cmd %d\n",
- unit, cmd);
- break;
- }
-
- return (err);
-}
-
-
-/*
- * Common entry points
- */
-
-/* ARGSUSED3 */
-static int
-volopen(dev_t *devp, int flag, int otyp, cred_t *credp)
-{
- int unit;
- struct vol_tab *tp;
- int err = 0;
- uint_t gflags;
-
- DPRINTF("vol: open: devp %p (%u.%u) flag %x otyp %x credp %p\n",
- (void *)devp, (int)getmajor(*devp), (int)getminor(*devp),
- flag, otyp, (void *)credp);
-
- unit = getminor(*devp);
- gflags = VGT_NEW | VGT_WAITSIG;
-
- /* implement non-blocking open */
- if (flag & FNDELAY)
- gflags |= VGT_NDELAY;
-
- if (unit == 0) {
- if (otyp != OTYP_CHR)
- return (EINVAL);
- gflags |= VGT_OPEN;
- }
-
- /* get our vol structure for this unit */
- if ((tp = vol_gettab(unit, gflags, &err)) == NULL) {
- DPRINTF("vol: open: gettab on unit %d, err %d\n", unit, err);
- if (err == EAGAIN)
- err = EIO; /* convert to usable errno */
- 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 */
- vol_tab_rele(tp);
- return (EINTR);
- }
-
- /* check for opening read-only with write flag set */
- if ((flag & FWRITE) && (tp->vol_flags & ST_RDONLY)) {
- vol_tab_unlock_and_rele(tp);
- return (EROFS);
- }
-
- /* implement exclusive use */
- if (((flag & FEXCL) && (tp->vol_flags & ST_OPEN)) ||
- (tp->vol_flags & ST_EXCL)) {
- vol_tab_unlock_and_rele(tp);
- return (EBUSY);
- }
-
- if (unit == 0 && (tp->vol_flags & ST_OPEN) == 0)
- volctl.ctl_open = 1;
-
- if (flag & FEXCL)
- tp->vol_flags |= ST_EXCL;
-
- /* count and flag open */
- if (otyp == OTYP_BLK) {
- tp->vol_bocnt = 1; /* user block device open */
- } else if (otyp == OTYP_CHR) {
- tp->vol_cocnt = 1; /* user character device */
- } else {
- tp->vol_locnt++; /* kernel open */
- }
-
- tp->vol_flags |= ST_OPEN;
-
- /* release lock, and return */
- vol_tab_unlock_and_rele(tp);
- return (0);
-}
-
-
-/* ARGSUSED3 */
-static int
-volclose(dev_t dev, int flag, int otyp, cred_t *credp)
-{
- int unit;
- struct vol_tab *tp;
- int err;
-
- DPRINTF("vol: close: dev %u.%u flag %x otyp %x credp %p\n",
- (int)getmajor(dev), (int)getminor(dev), flag, otyp, (void *)credp);
-
- unit = getminor(dev);
-
- if ((tp = vol_gettab(unit, VGT_NOWAIT, &err)) == NULL) {
- /* unit 0 has already been closed */
- return (0);
- }
-
- vol_tab_rwlock_upgrade(tp);
-
- if (otyp == OTYP_BLK) {
- tp->vol_bocnt = 0; /* block opens */
- } else if (otyp == OTYP_CHR) {
- tp->vol_cocnt = 0; /* character opens */
- } else {
- tp->vol_locnt--; /* kernel opens */
- }
-
- if ((tp->vol_bocnt == 0) && (tp->vol_cocnt == 0) &&
- (tp->vol_locnt == 0)) {
- tp->vol_flags &= ~(ST_OPEN|ST_EXCL);
- /*
- * ST_ENXIO should be cleared on the last close.
- * Otherwise, there is no way to reset the flags
- * other than killing the vold.
- */
- tp->vol_flags &= ~ST_ENXIO;
- }
-
- /*
- * If we've closed the device clean up after ourselves
- */
- if (((tp->vol_flags & ST_OPEN) == 0) && (unit != 0)) {
-#ifdef NOT_NEEDED
- /*
- * unmapping here means that every close unmaps so every open
- * will have to remap
- */
- (void) mutex_enter(&volctl.ctl_muxmutex);
- vol_unmap(tp);
- /* "tp" is invalid after vol_unmap!!! */
- (void) mutex_exit(&volctl.ctl_muxmutex);
-#endif
- vol_enqueue(VIE_CLOSE, (void *)&unit);
- }
-
- 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.
- */
- volctl.ctl_open = 0;
- }
-
- /* release lock */
- vol_tab_unlock_and_rele(tp);
-
- /* done */
- return (0);
-}
-
-
-static int
-volstrategy(struct buf *bp)
-{
- int unit;
- struct vol_tab *tp;
- struct buf *mybp;
- int err = 0;
-
- DPRINTF2("vol: strategy: bp %p dev %u.%u off %lu len %ld\n",
- (void *)bp, getmajor(bp->b_edev), getminor(bp->b_edev),
- (unsigned long)dbtob(bp->b_blkno), bp->b_bcount);
-
- unit = getminor(bp->b_edev);
- if (unit == 0) {
- bp->b_resid = bp->b_bcount;
- bioerror(bp, ENXIO);
- biodone(bp);
- return (0);
- }
-
- if ((tp = vol_gettab(unit, VGT_WAITSIG, &err)) == NULL) {
- bp->b_resid = bp->b_bcount;
- bioerror(bp, err);
- biodone(bp);
- DPRINTF("vol: strategy: gettab error %d\n", err);
- return (0);
- }
-
- if ((tp->vol_flags & ST_OPEN) == 0) {
- /* it's not even open */
- bp->b_resid = bp->b_bcount;
- bioerror(bp, ENXIO);
- vol_tab_unlock_and_rele(tp);
- biodone(bp);
- DPRINTF("vol: strategy: device not even open (ENXIO)\n");
- return (0);
- }
-
- /* allocate new buffer */
- sema_p(&volctl.ctl_bsema);
- mutex_enter(&volctl.ctl_bmutex);
-
- if (volctl.ctl_bhead == NULL) {
- panic("vol: strategy: bhead == NULL");
- /*NOTREACHED*/
- }
-
- mybp = volctl.ctl_bhead;
- volctl.ctl_bhead = mybp->b_chain;
- mutex_exit(&volctl.ctl_bmutex);
-
- /* setup buffer */
- ASSERT(tp->vol_dev != NODEV);
- *mybp = *bp; /* structure copy */
- mybp->b_forw = mybp->b_back = mybp;
- mybp->av_forw = mybp->av_back = NULL;
- mybp->b_dev = cmpdev(tp->vol_dev);
- mybp->b_iodone = vol_done;
- mybp->b_edev = tp->vol_dev;
- mybp->b_chain = bp; /* squirrel away old buf for vol_done */
- sema_init(&mybp->b_io, 0, NULL, SEMA_DRIVER, NULL);
- sema_init(&mybp->b_sem, 0, NULL, SEMA_DRIVER, NULL);
-
- if (tp->vol_flags & ST_CHKMEDIA) {
- err = vol_checkmedia(tp, NULL);
- if (err) {
- if (err == EINTR)
- vol_tab_rele(tp);
- else
- vol_tab_unlock_and_rele(tp);
-
- /* free buffer */
- mutex_enter(&volctl.ctl_bmutex);
- mybp->b_chain = volctl.ctl_bhead;
- volctl.ctl_bhead = mybp;
- mutex_exit(&volctl.ctl_bmutex);
-
- /* release semaphore */
- sema_v(&volctl.ctl_bsema);
-
- bp->b_resid = bp->b_bcount;
- bioerror(bp, err);
- biodone(bp);
- DPRINTF("vol: strategy: precheck failed, error %d\n",
- err);
- return (0);
- }
- }
-
- /* release lock, pass request on to stacked driver */
- vol_tab_unlock_and_rele(tp);
-
- /* pass request on to stacked driver */
- return (bdev_strategy(mybp));
-}
-
-
-static int
-vol_done(struct buf *mybp)
-{
- struct buf *bp = mybp->b_chain;
-
- DPRINTF2("vol: done: mybp %p bp %p dev %u.%u off %lu len %ld\n",
- (void *)mybp, (void *)bp, getmajor(bp->b_edev),
- getminor(bp->b_edev), (unsigned long)dbtob(bp->b_blkno),
- bp->b_bcount);
-
- /*
- * See NOTE comment at beginning of this module about code that
- * used to queue failed IO requests.
- */
- if (mybp->b_error) {
- DPRINTF("vol: error %d from device (should retry)\n",
- mybp->b_error);
- }
-
- /* copy status */
- bp->b_flags = mybp->b_flags;
- bp->b_un = mybp->b_un;
- bp->b_resid = mybp->b_resid;
- bp->b_error = mybp->b_error;
-
- /* free buffer */
- mutex_enter(&volctl.ctl_bmutex);
- mybp->b_chain = volctl.ctl_bhead;
- volctl.ctl_bhead = mybp;
- mutex_exit(&volctl.ctl_bmutex);
-
- /* release semaphore */
- sema_v(&volctl.ctl_bsema);
-
- /* continue on with biodone() */
- biodone(bp);
- return (0);
-}
-
-
-/* ARGSUSED */
-static int
-volread(dev_t dev, struct uio *uiop, cred_t *credp)
-{
- struct vol_tab *tp;
- int unit;
- int err = 0;
- int err1;
- int found_media;
-
- DPRINTF2("vol: read: dev %u.%u uiop %p credp %p\n",
- getmajor(dev), getminor(dev), (void *)uiop,
- (void *)credp);
-
- unit = getminor(dev);
- if (unit == 0) {
- return (ENXIO);
- }
-
- if ((tp = vol_gettab(unit, VGT_WAITSIG, &err)) == NULL) {
- DPRINTF("vol: read: gettab on unit %d, err %d\n", unit, err);
- if (err == EAGAIN) {
- err = EIO; /* convert to usable errno */
- }
- return (err);
- }
-
- if ((tp->vol_flags & ST_OPEN) == 0) {
- err = ENXIO;
- goto out;
- }
-
- if (tp->vol_flags & ST_CHKMEDIA) {
- err = vol_checkmedia(tp, NULL);
- if (err) {
- if (err == EINTR) {
- vol_tab_rele(tp);
- return (EINTR);
- }
- goto out;
- }
- }
-
- for (;;) {
- /* read data */
- if (tp->vol_dev == NODEV) {
- DPRINTF("vol: read: no device\n");
- err = ENXIO;
- goto out;
- }
-
- err = cdev_read(tp->vol_dev, uiop, kcred);
-
- if (err && tp->vol_flags & ST_CHKMEDIA) {
- err1 = vol_checkmedia(tp, &found_media);
- if (err1 == EINTR) {
- vol_tab_rele(tp);
- 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) {
- err = err1;
- break;
- }
- } else {
- break;
- }
- }
-
- /* release lock, return success */
-out:
- vol_tab_unlock_and_rele(tp);
- return (err);
-}
-
-
-/* ARGSUSED */
-static int
-volwrite(dev_t dev, struct uio *uiop, cred_t *credp)
-{
- struct vol_tab *tp;
- int unit;
- int err = 0;
- int err1;
- int found_media;
-
- DPRINTF2("vol: write: dev %u.%u uiop %p credp %p\n",
- getmajor(dev), getminor(dev), (void *)uiop,
- (void *)credp);
-
- unit = getminor(dev);
- if (unit == 0) {
- return (ENXIO);
- }
-
- if ((tp = vol_gettab(unit, VGT_WAITSIG, &err)) == NULL) {
- DPRINTF("vol: write: gettab on unit %d, err %d\n", unit, err);
- if (err == EAGAIN) {
- err = EIO; /* convert to usable errno */
- }
- return (err);
- }
-
- if ((tp->vol_flags & ST_OPEN) == 0) {
- err = ENXIO;
- goto out;
- }
-
- vol_checkwrite(tp, uiop, unit);
-
- if (tp->vol_flags & ST_CHKMEDIA) {
- err = vol_checkmedia(tp, NULL);
- if (err) {
- if (err == EINTR) {
- vol_tab_rele(tp);
- return (EINTR);
- }
- goto out;
- }
- }
-
- for (;;) {
- /* write data */
- if (tp->vol_dev == NODEV) {
- DPRINTF("vol: write: no device");
- err = ENXIO;
- goto out;
- }
-
- err = cdev_write(tp->vol_dev, uiop, kcred);
-
- if (err && tp->vol_flags & ST_CHKMEDIA) {
- err1 = vol_checkmedia(tp, &found_media);
- if (err1 == EINTR) {
- vol_tab_rele(tp);
- 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) {
- err = err1;
- break;
- }
- } else {
- break;
- }
- }
-
- /* release lock, return err */
-out:
- vol_tab_unlock_and_rele(tp);
- 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
-vol_checkwrite(struct vol_tab *tp, struct uio *uiop, int unit)
-{
- /*
- * XXX: this is VERY incomplete.
- * This only works with a full label write of the Sun label.
- */
- if (uiop->uio_loffset == 0) {
- vol_enqueue(VIE_NEWLABEL, (void *)&unit);
- /*
- * 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.
- */
- binval(tp->vol_dev); /* XXX: not DDI compliant */
- }
-}
-
-
-static int
-volprop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
- int flags, char *name, caddr_t valuep, int *lengthp)
-{
- int unit;
- struct vol_tab *tp;
- dev_info_t *stackdip;
- int err = 0;
-
- DPRINTF2("vol: prop_op: dev %u.%u dip %p prop_op %d flags %x\n",
- getmajor(dev), getminor(dev), (void *)dip, (int)prop_op,
- flags);
- DPRINTF2(" name '%s' valuep %p lengthp %p\n",
- name, (void *)valuep, (void *)lengthp);
-
- /* send our props on to ddi_prop_op */
- if (strcmp(name, "instance") == 0 ||
- strcmp(name, VOLNBUFPROP) == 0) {
- err = ddi_prop_op(dev, dip, prop_op, flags,
- name, valuep, lengthp);
- return (err);
- }
-
- unit = getminor(dev);
- if (unit == 0) {
- return (DDI_PROP_NOT_FOUND);
- }
-
- if ((tp = vol_gettab(unit, VGT_NOWAIT, &err)) == NULL) {
- DPRINTF("vol: prop_op: gettab on unit %d, err %d\n", unit,
- err);
- return (DDI_PROP_NOT_FOUND);
- }
-
- if (err) {
- err = DDI_PROP_NOT_FOUND;
- goto out;
- }
-
- /* get stacked dev info */
- ASSERT(tp->vol_devops != NULL);
- if ((err = (*(tp->vol_devops->devo_getinfo))(NULL,
- DDI_INFO_DEVT2DEVINFO, (void *)tp->vol_dev, (void *)&stackdip))
- != DDI_SUCCESS) {
- cmn_err(CE_CONT,
- "vol: prop_op: %d: could not get child dev info err %d\n",
- unit, err);
- err = DDI_PROP_NOT_FOUND;
- goto out;
- }
-
- /* pass request on to stacked driver */
- err = cdev_prop_op(tp->vol_dev, stackdip, prop_op, flags,
- name, valuep, lengthp);
-
- if (err) {
- DPRINTF("vol: cdev_prop_op: err = %d\n", err);
- }
-
- /* release lock, return err */
-out:
- vol_tab_unlock_and_rele(tp);
- return (err);
-}
-
-
-/* ARGSUSED */
-static int
-volioctl(dev_t dev, int cmd, intptr_t arg, int mode,
- cred_t *credp, int *rvalp)
-{
- int unit, tunit;
- struct vol_tab *tp;
- int err = 0;
- dev_t udev;
-
- DPRINTF("volioctl: dev=%u.%u cmd=%x arg=%lx mode=%x\n",
- getmajor(dev), getminor(dev), cmd, arg, mode);
-
- unit = getminor(dev);
- if (unit == 0) {
- /* commands from vold */
- switch (cmd) {
-
- /*
- * The daemon will call this if it's using the driver.
- */
- case VOLIOCDAEMON: {
- pid_t pid = (pid_t)arg;
-
- if (drv_priv(credp) != 0) {
- err = EPERM;
- break;
- }
- mutex_enter(&volctl.ctl_muxmutex);
- if (volctl.ctl_daemon_pid == 0) {
- volctl.ctl_daemon_pid = pid;
- } else {
- if (vol_daemon_check() == 0) {
- /* i'm vold who can change pid */
- volctl.ctl_daemon_pid = pid;
- } else {
- /* already a daemon running! */
- err = EBUSY;
- }
- }
- mutex_exit(&volctl.ctl_muxmutex);
-#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: {
- STRUCT_DECL(vioc_map, vim);
- struct dev_ops *devp;
- char *path;
- uint_t pathlen;
-
- if ((err = vol_daemon_check()) != 0)
- break;
-
- STRUCT_INIT(vim, mode & FMODELS);
- if (ddi_copyin((void *)arg, STRUCT_BUF(vim),
- STRUCT_SIZE(vim), mode) != 0) {
- DPRINTF("vol: map:copyin broke\n");
- err = EFAULT;
- break;
- }
- /* don't allow mapping for unit 0 */
- if ((tunit = STRUCT_FGET(vim, vim_unit)) == 0) {
- err = EINVAL;
- break;
- }
- /* pathname cannot be longer than MAXPATHLEN */
- pathlen = STRUCT_FGET(vim, vim_pathlen);
- if (pathlen >= MAXPATHLEN) {
- err = ENAMETOOLONG;
- break;
- }
- path = NULL;
- if (pathlen != 0) {
- path = kmem_alloc(pathlen + 1, KM_SLEEP);
- if (ddi_copyin(STRUCT_FGETP(vim, vim_path),
- path, pathlen, mode)) {
- kmem_free(path, pathlen + 1);
- err = EFAULT;
- break;
- }
- path[pathlen] = '\0';
- }
-
- /* copyins complete, get vol_tab */
- tp = vol_gettab(tunit, VGT_NOWAIT|VGT_NEW, &err);
- if (tp == NULL) {
- DPRINTF("vol: map:null on vol %u, err %d\n",
- (uint_t)tunit, err);
- if (path != NULL)
- kmem_free(path, pathlen + 1);
- break;
- }
- err = 0;
-
- if ((mode & FMODELS) == FNATIVE)
- udev = STRUCT_FGET(vim, vim_dev);
- else
- udev = expldev(STRUCT_FGET(vim, vim_dev));
-
- /* ready to grab the driver */
- if (udev != NODEV) {
- devp = ddi_hold_driver(getmajor(udev));
- if (devp == NULL) {
- DPRINTF("vol: map:hold_inst broke\n");
- vol_tab_unlock_and_rele(tp);
- if (path != NULL)
- kmem_free(path, pathlen + 1);
- err = ENODEV;
- break;
- }
- DPRINTF3("vol: ioctl: holding driver %u\n",
- getmajor(udev));
- }
-
- /* changing vol_tab, needs wlock(no interrupt) */
- vol_tab_rwlock_upgrade(tp);
-
- /* release old driver if it's been held */
- vol_release_driver(tp);
-
- tp->vol_path = path;
- tp->vol_pathlen = pathlen;
- tp->vol_id = STRUCT_FGET(vim, vim_id);
-
- if (STRUCT_FGET(vim, vim_flags) & VIM_FLOPPY) {
- tp->vol_flags |= ST_CHKMEDIA;
- tp->vol_mtype = MT_FLOPPY;
- } else {
- /* clear data (in case of previous use) */
- tp->vol_flags &= ~ST_CHKMEDIA;
- tp->vol_mtype = 0;
- }
- /* is this a read-only volume? */
- if (STRUCT_FGET(vim, vim_flags) & VIM_RDONLY) {
- tp->vol_flags |= ST_RDONLY;
- }
-
- mutex_enter(&volctl.ctl_muxmutex);
- /*
- * if vim.vim_dev == NODEV, it means that the daemon
- * is unblocking a "nodelay" request.
- */
- if (udev != NODEV) {
- /* build the mapping */
- tp->vol_dev = udev;
- tp->vol_devops = devp;
- /* clear any pending cancel */
- tp->vol_cancel = FALSE;
- }
- cv_broadcast(&tp->vol_incv);
- vol_tab_unlock(tp);
- mutex_exit(&volctl.ctl_muxmutex);
- vol_tab_rele(tp);
- break;
- }
-
- /*
- * Break a mapping established with the above
- * map ioctl.
- */
- case VOLIOCUNMAP: {
- if ((err = vol_daemon_check()) != 0)
- break;
- if (ddi_copyin((caddr_t)arg, &tunit,
- sizeof (tunit), mode) != 0) {
- DPRINTF("vol: unmap:copyin broke\n");
- err = EFAULT;
- break;
- }
- if (tunit == 0) {
- err = EINVAL;
- break;
- }
- if ((tp = vol_gettab(tunit, VGT_NOWAIT, &err)) == NULL)
- break;
- vol_tab_rwlock_upgrade(tp);
- vol_release_driver(tp);
- vol_tab_unlock_and_rele(tp);
- 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: {
- struct kvioc_event *kve = NULL;
-
- if ((err = vol_daemon_check()) != 0)
- break;
- mutex_enter(&volctl.ctl_evmutex);
- if (volctl.ctl_evcnt) {
- kve = volctl.ctl_events.kve_next;
- volctl.ctl_evcnt--;
- if (volctl.ctl_evcnt == 0)
- volctl.ctl_evend = NULL;
- remque(kve);
- }
- mutex_exit(&volctl.ctl_evmutex);
- if (kve == NULL) {
- err = EWOULDBLOCK;
- break;
- }
- if ((mode & FMODELS) == FNATIVE) {
- if (ddi_copyout(&kve->kve_event, (caddr_t)arg,
- sizeof (kve->kve_event), mode) != 0) {
- err = EFAULT;
- }
- }
-#ifdef _SYSCALL32_IMPL
- else {
- struct vioc_event32 e32;
-
- err = vol_event32(&e32, &kve->kve_event);
- if (err == 0) {
- if (ddi_copyout(&e32, (caddr_t)arg,
- sizeof (e32), mode) != 0) {
- err = EFAULT;
- }
- }
- }
-#endif /* _SYSCALL32_IMPL */
- if (err != 0 && err != EOVERFLOW) {
- /* add it back on err */
- mutex_enter(&volctl.ctl_evmutex);
- insque(kve, &volctl.ctl_events);
- volctl.ctl_evcnt++;
- mutex_exit(&volctl.ctl_evmutex);
- DPRINTF("vol: event: copyout %d\n", err);
- break;
- }
- kmem_free(kve, sizeof (*kve));
- 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: {
- STRUCT_DECL(vioc_eject, viej);
- enum eject_state status;
-
- if ((err = vol_daemon_check()) != 0)
- break;
- STRUCT_INIT(viej, get_udatamodel());
- if (ddi_copyin((caddr_t)arg, STRUCT_BUF(viej),
- STRUCT_SIZE(viej), mode) != 0) {
- DPRINTF("VOLIOCEJECT: copyin error\n");
- err = EFAULT;
- break;
- }
- if ((tunit = STRUCT_FGET(viej, viej_unit)) == 0) {
- err = EINVAL;
- break;
- }
- tp = vol_gettab(tunit, VGT_NOWAIT, &err);
- if (tp == NULL) {
- DPRINTF("VOLIOCEJECT: gettab error\n");
- break;
- }
- status = STRUCT_FGET(viej, viej_state);
- if (status != VEJ_YES &&
- status != VEJ_NO &&
- status != VEJ_YESSTOP) {
- vol_tab_unlock_and_rele(tp);
- err = EINVAL;
- break;
- }
- if ((err = vold_ioctl_enter(&tp->vol_eject,
- NULL)) != 0) {
- vol_tab_unlock_and_rele(tp);
- break;
- }
- vold_ioctl_respond(&tp->vol_eject, status, NULL);
- vold_ioctl_exit(&tp->vol_eject);
- vol_tab_unlock_and_rele(tp);
- break;
- }
-
- /* Daemon response to setattr from user */
- case VOLIOCDSATTR: {
- STRUCT_DECL(vioc_dattr, vda);
- int attr_err;
- struct ve_attr *attr_ptr;
-
- if ((err = vol_daemon_check()) != 0)
- break;
- STRUCT_INIT(vda, get_udatamodel());
- if (ddi_copyin((caddr_t)arg, STRUCT_BUF(vda),
- STRUCT_SIZE(vda), mode) != 0) {
- err = EFAULT;
- break;
- }
- if ((tunit = STRUCT_FGET(vda, vda_unit)) == 0) {
- err = EINVAL;
- break;
- }
- if ((attr_err = STRUCT_FGET(vda, vda_errno)) < 0) {
- err = EINVAL;
- break;
- }
- tp = vol_gettab(tunit, VGT_NOWAIT, &err);
- if (tp == NULL)
- break;
- if ((err = vold_ioctl_enter(&tp->vol_attr,
- &attr_ptr)) != 0) {
- vol_tab_unlock_and_rele(tp);
- break;
- }
- if (attr_ptr == NULL || attr_ptr->viea_unit != tunit) {
- /* shouldn't happen, but just in case */
- err = EAGAIN;
- } else {
- vold_ioctl_respond(&tp->vol_attr,
- attr_err, attr_ptr);
- }
- vold_ioctl_exit(&tp->vol_attr);
- vol_tab_unlock_and_rele(tp);
- break;
- }
- /* Daemon response to getattr from user */
- case VOLIOCDGATTR: {
- STRUCT_DECL(vioc_dattr, vda);
- int attr_err;
- struct ve_attr *attr_ptr = NULL;
-
- if ((err = vol_daemon_check()) != 0)
- break;
- STRUCT_INIT(vda, get_udatamodel());
- if (ddi_copyin((caddr_t)arg, STRUCT_BUF(vda),
- STRUCT_SIZE(vda), mode) != 0) {
- err = EFAULT;
- break;
- }
- if ((tunit = STRUCT_FGET(vda, vda_unit)) == 0) {
- err = EINVAL;
- break;
- }
- if ((attr_err = STRUCT_FGET(vda, vda_errno)) < 0) {
- err = EINVAL;
- break;
- }
- tp = vol_gettab(tunit, VGT_NOWAIT, &err);
- if (tp == NULL)
- break;
- if ((err = vold_ioctl_enter(&tp->vol_attr,
- &attr_ptr)) != 0) {
- vol_tab_unlock_and_rele(tp);
- break;
- }
- if (attr_err == 0 && attr_ptr != NULL) {
- char *vstr;
- int len;
-
- vstr = STRUCT_FGETP(vda, vda_value);
- /* put end mark so that strlen never overrun */
- vstr[MAX_ATTR_LEN -1] = '\0';
- len = strlen(vstr);
- bcopy(vstr, attr_ptr->viea_value, len);
- attr_ptr->viea_value[len] = '\0';
- }
- vold_ioctl_respond(&tp->vol_attr, attr_err, attr_ptr);
- vold_ioctl_exit(&tp->vol_attr);
- vol_tab_unlock_and_rele(tp);
- break;
- }
-
- /* Daemon response to insert from user */
- case VOLIOCDCHECK:
- if ((err = vol_daemon_check()) != 0)
- break;
- if ((int)arg < 0) {
- err = EINVAL;
- break;
- }
- if ((err = vold_ioctl_enter(&volctl.ctl_insert,
- NULL)) != 0)
- break;
- vold_ioctl_respond(&volctl.ctl_insert, (int)arg, NULL);
- vold_ioctl_exit(&volctl.ctl_insert);
- break;
-
- /* Daemon response to inuse from user */
- case VOLIOCDINUSE:
- if ((err = vol_daemon_check()) != 0)
- break;
- if ((int)arg < 0) {
- err = EINVAL;
- break;
- }
- if ((err = vold_ioctl_enter(&volctl.ctl_inuse,
- NULL)) != 0)
- break;
- vold_ioctl_respond(&volctl.ctl_inuse, (int)arg, NULL);
- vold_ioctl_exit(&volctl.ctl_inuse);
- break;
-
- /* Daemon response to inuse from user */
- case VOLIOCFLAGS: {
- STRUCT_DECL(vioc_flags, vfl);
-
- if ((err = vol_daemon_check()) != 0)
- break;
- STRUCT_INIT(vfl, get_udatamodel());
- if (ddi_copyin((caddr_t)arg, STRUCT_BUF(vfl),
- STRUCT_SIZE(vfl), mode) != 0) {
- err = EFAULT;
- break;
- }
- if ((tunit = STRUCT_FGET(vfl, vfl_unit)) == 0) {
- err = EINVAL;
- break;
- }
- tp = vol_gettab(tunit, VGT_NOWAIT, &err);
- if (tp == NULL)
- break;
- /* if we had an ENXIO error, then that's okay */
- if (err == ENXIO || err == ENODEV) {
- DPRINTF(
- "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
- */
- vol_tab_rwlock_upgrade(tp);
-
- /* set or clear the ST_ENXIO flag */
- if (STRUCT_FGET(vfl, vfl_flags) & VFL_ENXIO) {
- tp->vol_flags |= ST_ENXIO;
-#ifdef DEBUG_ENXIO
- (void) printf(
- "volioctl: VOLIOCFLAGS(ST_ENXIO), unit %d (flags=0x%x, tp=%p)\n",
- tunit, tp->vol_flags, (void *)tp);
-#endif
- } else {
- tp->vol_flags &= ~ST_ENXIO;
-#ifdef DEBUG_ENXIO
- (void) printf(
- "volioctl: VOLIOCFLAGS(0), unit %d (flags=0x%x, tp=%p)\n",
- tunit, tp->vol_flags, (void *)tp);
-#endif
- }
- DPRINTF(
- "vol: volioctl: %s ST_ENXIO flag for unit %u\n",
- (STRUCT_FGET(vfl, vfl_flags) & VFL_ENXIO) ?
- "set" : "cleared", tunit);
-
- vol_tab_unlock_and_rele(tp);
- break;
- }
-
- /* daemon response to symname request from user */
- case VOLIOCDSYMNAME: {
- STRUCT_DECL(vol_str, vstr);
- char *symname;
- size_t symname_len;
-
- if ((err = vol_daemon_check()) != 0)
- break;
- /* get the string struct */
- STRUCT_INIT(vstr, get_udatamodel());
- if (ddi_copyin((caddr_t)arg, STRUCT_BUF(vstr),
- STRUCT_SIZE(vstr), mode) != 0) {
- err = EFAULT;
- break;
- }
- symname_len = STRUCT_FGET(vstr, data_len);
- if (symname_len > VOL_SYMNAME_LEN) {
- err = EINVAL;
- break;
- }
- /* if any string to get then get it */
- if (symname_len != 0) {
- /* allocate some memory for string */
- symname = kmem_alloc(symname_len + 1, KM_SLEEP);
- /* grab the string */
- if (ddi_copyin(STRUCT_FGETP(vstr, data),
- symname, symname_len, mode) != 0) {
- kmem_free(symname, symname_len + 1);
- err = EFAULT;
- break;
- }
- symname[symname_len] = '\0';
- } else {
- symname = NULL;
- }
- if ((err = vold_ioctl_enter(&volctl.ctl_symname,
- NULL)) != 0) {
- if (symname != NULL)
- kmem_free(symname, symname_len + 1);
- break;
- }
- /* signal waiter that we have the info */
- vold_ioctl_respond(&volctl.ctl_symname,
- symname_len, symname);
- vold_ioctl_exit(&volctl.ctl_symname);
- break;
- }
-
- /* daemon response to symdev request from user */
- case VOLIOCDSYMDEV: {
- STRUCT_DECL(vol_str, vstr);
- char *symdev;
- size_t symdev_len;
-
- if ((err = vol_daemon_check()) != 0)
- break;
- /* get the string */
- STRUCT_INIT(vstr, get_udatamodel());
- if (ddi_copyin((caddr_t)arg, STRUCT_BUF(vstr),
- STRUCT_SIZE(vstr), mode) != 0) {
- err = EFAULT;
- break;
- }
- symdev_len = STRUCT_FGET(vstr, data_len);
- if (symdev_len >= VOL_SYMDEV_LEN) {
- err = EINVAL;
- break;
- }
- if (symdev_len != 0) {
- /* get memory for string */
- symdev = kmem_alloc(symdev_len + 1, KM_SLEEP);
- /* now get the dev string */
- if (ddi_copyin(STRUCT_FGETP(vstr, data),
- symdev, symdev_len, mode) != 0) {
- kmem_free(symdev, symdev_len + 1);
- err = EFAULT;
- break;
- }
- symdev[symdev_len] = '\0';
- } else {
- symdev = NULL;
- }
- /* lock data struct */
- if ((err = vold_ioctl_enter(&volctl.ctl_symdev,
- NULL)) != 0) {
- if (symdev != NULL)
- kmem_free(symdev, symdev_len + 1);
- break;
- }
- /* signal waiter that we have the info */
- vold_ioctl_respond(&volctl.ctl_symdev,
- symdev_len, symdev);
- vold_ioctl_exit(&volctl.ctl_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: {
- dev_t dev;
- int rval;
- /*
- * If there's no daemon, we already know the answer.
- */
- if (volctl.ctl_daemon_pid == 0) {
- err = ENXIO;
- break;
- }
- if ((err = vol_ioctl_enter(&volctl.ctl_insert)) != 0)
- break;
- if ((mode & FMODELS) == FNATIVE)
- dev = (dev_t)arg;
- else
- dev = expldev((dev32_t)arg);
- vol_enqueue(VIE_CHECK, (void *)&dev);
- err = vol_ioctl_wait(&volctl.ctl_insert, &rval, NULL);
- if (err == 0)
- err = rval;
- vol_ioctl_exit(&volctl.ctl_insert);
- 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: {
- dev_t dev;
- int rval;
- /*
- * If there's no daemon, we already know the answer.
- */
- if (volctl.ctl_daemon_pid == 0) {
- err = ENXIO;
- break;
- }
- if ((err = vol_ioctl_enter(&volctl.ctl_inuse)) != 0)
- break;
- if ((mode & FMODELS) == FNATIVE)
- dev = (dev_t)arg;
- else
- dev = expldev((dev32_t)arg);
- vol_enqueue(VIE_INUSE, (void *)&dev);
- err = vol_ioctl_wait(&volctl.ctl_inuse, &rval, NULL);
- if (err == 0)
- err = rval;
- vol_ioctl_exit(&volctl.ctl_inuse);
- break;
- }
-
- /* Cancel initiated from the daemon */
- case VOLIOCCANCEL: {
- if ((err = vol_daemon_check()) != 0)
- break;
- if (ddi_copyin((caddr_t)arg, &tunit,
- sizeof (tunit), mode) != 0) {
- DPRINTF("vol: cancel:copyin broke\n");
- err = EFAULT;
- break;
- }
- if (tunit == 0) {
- err = EINVAL;
- break;
- }
- tp = vol_gettab(tunit, VGT_NOWAIT, &err);
- if (tp == NULL)
- break;
- if (err == ENXIO || err == ENODEV)
- err = 0;
- /*
- * need wrlock as we are changing vol_cancel.
- */
- vol_tab_rwlock_upgrade(tp);
- mutex_enter(&volctl.ctl_muxmutex);
- DPRINTF("vol: doing cancel on %u\n", tunit);
- tp->vol_cancel = TRUE;
- cv_broadcast(&tp->vol_incv);
- vol_tab_unlock(tp);
- mutex_exit(&volctl.ctl_muxmutex);
- vol_tab_rele(tp);
- break;
- }
-
- /* set the volmgt root dir (defaults to "/vol") */
- case VOLIOCDROOT: {
- STRUCT_DECL(vol_str, vstr);
- char *rptr;
- size_t rlen;
-
- if ((err = vol_daemon_check()) != 0)
- break;
-
- mutex_enter(&volctl.ctl_volrmutex);
- /* can't set if already set */
- if (vold_root != NULL) {
- /* error */
- mutex_exit(&volctl.ctl_volrmutex);
- err = EAGAIN;
- break;
- }
- mutex_exit(&volctl.ctl_volrmutex);
-
- STRUCT_INIT(vstr, get_udatamodel());
- if (ddi_copyin((caddr_t)arg, STRUCT_BUF(vstr),
- STRUCT_SIZE(vstr), mode) != 0) {
- err = EFAULT;
- break;
- }
- rlen = STRUCT_FGET(vstr, data_len);
- if (rlen == 0 || rlen >= MAXPATHLEN) {
- err = EINVAL;
- break;
- }
- rptr = kmem_alloc(rlen + 1, KM_SLEEP);
- if (ddi_copyin(STRUCT_FGETP(vstr, data),
- rptr, rlen, mode) != 0) {
- kmem_free(rptr, rlen + 1);
- err = EFAULT;
- break;
- }
- rptr[rlen] = '\0';
-
- mutex_enter(&volctl.ctl_volrmutex);
- if (vold_root != NULL) {
- /* someone has set the root */
- kmem_free(rptr, rlen + 1);
- err = EAGAIN;
- } else {
- vold_root = rptr;
- vold_root_len = rlen;
- }
- mutex_exit(&volctl.ctl_volrmutex);
- break;
- }
-
- /* return where the vol root is */
- case VOLIOCROOT: {
- STRUCT_DECL(vol_str, vd);
- char path[64], *rptr;
-
- mutex_enter(&volctl.ctl_volrmutex);
- /* if no root set then punt */
- if (vold_root == NULL) {
- /* allocate a default vol root */
- vold_root = kmem_alloc(VOL_ROOT_DEFAULT_LEN + 1,
- KM_SLEEP);
- vold_root_len = VOL_ROOT_DEFAULT_LEN;
- (void) strcpy(vold_root, VOL_ROOT_DEFAULT);
- }
- if (vold_root_len >= sizeof (path))
- rptr = kmem_alloc(vold_root_len + 1, KM_SLEEP);
- else
- rptr = path;
- (void) bcopy(vold_root, rptr, vold_root_len + 1);
- mutex_exit(&volctl.ctl_volrmutex);
-
- /*
- * copy in struct to know buf size at target
- * for vold_root
- */
- STRUCT_INIT(vd, get_udatamodel());
- if (ddi_copyin((caddr_t)arg, STRUCT_BUF(vd),
- STRUCT_SIZE(vd), mode) != 0) {
- err = EFAULT;
- goto rootout;
- }
- /* check if our str len is out of range */
- if ((vold_root_len + 1) > STRUCT_FGET(vd, data_len)) {
- err = EINVAL;
- goto rootout;
- }
- /* all is ok, send back the vold_root */
- if (ddi_copyout(rptr, STRUCT_FGETP(vd, data),
- vold_root_len + 1, mode) != 0) {
- err = EFAULT;
- }
-rootout:
- if (rptr != path)
- kmem_free(rptr, vold_root_len + 1);
- break;
- }
-
- /* find a symname given a dev */
- case VOLIOCSYMNAME: {
- STRUCT_DECL(vioc_symname, sn);
- dev_t dev;
- char *symname = NULL;
- int symname_len = 0;
-
- /* if there's no daemon then we can't check */
- if (volctl.ctl_daemon_pid == 0) {
- err = ENXIO;
- break;
- }
- /* get the struct */
- STRUCT_INIT(sn, get_udatamodel());
- if (ddi_copyin((void *)arg, STRUCT_BUF(sn),
- STRUCT_SIZE(sn), mode)) {
- err = EFAULT;
- break;
- }
- /* lock out others */
- if ((err = vol_ioctl_enter(&volctl.ctl_symname)) != 0)
- break;
- /* tell the daemon that we want a symname */
- if ((mode & FMODELS) == FNATIVE)
- dev = STRUCT_FGET(sn, sn_dev);
- else
- dev = expldev(STRUCT_FGET(sn, sn_dev));
- vol_enqueue(VIE_SYMNAME, (void *)&dev);
- err = vol_ioctl_wait(&volctl.ctl_symname,
- &symname_len, &symname);
-
- /* return result (if not interrupted) */
- if (err == 0) {
- /* is there enough room for the result ? */
- if (symname_len >=
- STRUCT_FGET(sn, sn_pathlen)) {
- DPRINTF(
- "volctl: no room for symname result\n");
- err = EINVAL;
- } else if (symname_len == 0 ||
- symname == NULL) {
- DPRINTF(
- "volctl: no symname to copy out\n");
- err = ENOENT;
- } else {
- if (ddi_copyout(symname,
- STRUCT_FGETP(sn, sn_symname),
- symname_len + 1, mode) != 0) {
- err = EFAULT;
- }
- }
- }
- /*
- * vol_ioctl_wait() may have failed, but vold
- * may have allocated symname.
- */
- if (symname_len != 0 && symname != NULL)
- kmem_free(symname, symname_len + 1);
- /* release lock */
- vol_ioctl_exit(&volctl.ctl_symname);
- break;
- }
-
- /* find a dev path given a symname */
- case VOLIOCSYMDEV: {
- STRUCT_DECL(vioc_symdev, sd);
- struct ve_symdev vesd;
- char *symdev = NULL;
- int symdev_len = 0;
- size_t symname_len;
-
- /* if there's no daemon then we can't check */
- if (volctl.ctl_daemon_pid == 0) {
- err = ENXIO;
- break;
- }
- /* get the struct */
- STRUCT_INIT(sd, get_udatamodel());
- if (ddi_copyin((void *)arg, STRUCT_BUF(sd),
- STRUCT_SIZE(sd), mode)) {
- err = EFAULT;
- break;
- }
- /* see if user is providing a length too long */
- symname_len = STRUCT_FGET(sd, sd_symnamelen);
- if (symname_len == 0 || symname_len > VOL_SYMNAME_LEN) {
- err = EINVAL;
- break;
- }
-
- /* don't copyout garbage */
- bzero(&vesd, sizeof (vesd));
-
- /* get the symname */
- if (ddi_copyin(STRUCT_FGETP(sd, sd_symname),
- vesd.vied_symname, symname_len, mode) != 0) {
- err = EFAULT;
- break;
- }
- vesd.vied_symname[symname_len] = '\0';
-
- /* lock out others */
- if ((err = vol_ioctl_enter(&volctl.ctl_symdev)) != 0)
- break;
-
- /* tell the daemon that we want a symdev */
- vol_enqueue(VIE_SYMDEV, (void *)&vesd);
-
- /* wait for daemon to reply */
- err = vol_ioctl_wait(&volctl.ctl_symdev,
- &symdev_len, &symdev);
-
- /* return result (if not interrupted) */
- if (err == 0) {
- /* is there enough room for the result ? */
- if (symdev_len >= STRUCT_FGET(sd, sd_pathlen)) {
- DPRINTF(
- "volctl: no room for symdev result\n");
- err = EINVAL;
- } else if (symdev_len == 0 ||
- symdev == NULL) {
- DPRINTF(
- "volctl: no symdev to copy out\n");
- err = ENOENT;
- } else {
- if (ddi_copyout(symdev,
- STRUCT_FGETP(sd, sd_symdevname),
- symdev_len + 1, mode) != 0) {
- err = EFAULT;
- }
- }
- }
- /* free room */
- if (symdev_len != 0 && symdev != NULL)
- kmem_free(symdev, symdev_len + 1);
- /* release lock */
- vol_ioctl_exit(&volctl.ctl_symdev);
- break;
- }
-
- /*
- * Create minor name for unit
- */
- case VOLIOCCMINOR: {
- char mname_chr[16];
- char mname_blk[16];
-
- if ((err = vol_daemon_check()) != 0)
- break;
-
- if ((tunit = (minor_t)arg) == 0) {
- err = EINVAL;
- break;
- }
- ASSERT(volctl.ctl_dip);
- (void) snprintf(mname_blk, sizeof (mname_blk),
- VOLUNITNAME_BLK, (int)tunit);
- (void) snprintf(mname_chr, sizeof (mname_chr),
- VOLUNITNAME_CHR, tunit);
-
- if (ddi_create_internal_pathname(volctl.ctl_dip,
- mname_blk, S_IFBLK, tunit) != DDI_SUCCESS)
- err = ENODEV;
- else if (ddi_create_internal_pathname(volctl.ctl_dip,
- mname_chr, S_IFCHR, tunit) != DDI_SUCCESS) {
- err = ENODEV;
- ddi_remove_minor_node(volctl.ctl_dip,
- mname_blk);
- }
- break;
- }
-
- /*
- * Remove minor name for unit
- */
- case VOLIOCRMINOR: {
- char mname[16];
-
- if ((err = vol_daemon_check()) != 0)
- break;
-
- if ((tunit = (minor_t)arg) == 0) {
- err = EINVAL;
- break;
- }
- ASSERT(volctl.ctl_dip);
- (void) snprintf(mname, sizeof (mname),
- VOLUNITNAME_BLK, (int)tunit);
- ddi_remove_minor_node(volctl.ctl_dip, mname);
-
- (void) snprintf(mname, sizeof (mname),
- VOLUNITNAME_CHR, (int)tunit);
- ddi_remove_minor_node(volctl.ctl_dip, mname);
- break;
- }
-
- default:
- err = ENOTTY;
- break;
- }
-
- if ((err != 0) && (err != EWOULDBLOCK)) {
- DPRINTF("vol: ioctl: err=%d (cmd=%x)\n", err, cmd);
- }
-
- return (err);
- /*NOTREACHED*/
- }
-
- /*
- * This set of ioctls are available to be executed without
- * having the unit available. vol_gettab() can be interrupted
- * by signal.
- */
- tp = vol_gettab(unit, VGT_NDELAY|VGT_NOWAIT|VGT_WAITSIG, &err);
- if (tp == NULL)
- 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 ... ??
- */
- STRUCT_DECL(vioc_info, info);
-
- STRUCT_INIT(info, get_udatamodel());
- if (ddi_copyin((caddr_t)arg, STRUCT_BUF(info),
- STRUCT_SIZE(info), mode) != 0) {
- vol_tab_unlock_and_rele(tp);
- return (EFAULT);
- }
- STRUCT_FSET(info, vii_inuse, tp->vol_bocnt + tp->vol_cocnt +
- tp->vol_locnt);
- STRUCT_FSET(info, vii_id, tp->vol_id);
-
- if (ddi_copyout(STRUCT_BUF(info), (caddr_t)arg,
- STRUCT_SIZE(info), mode) != 0) {
- err = EFAULT;
- }
- if (err == 0 &&
- STRUCT_FGETP(info, vii_devpath) != NULL &&
- STRUCT_FGET(info, vii_pathlen) != 0 &&
- tp->vol_path != NULL) {
- if (ddi_copyout(tp->vol_path, STRUCT_FGETP(info,
- vii_devpath), min(STRUCT_FGET(info, vii_pathlen),
- tp->vol_pathlen) + 1, mode) != 0) {
- err = EFAULT;
- }
- }
- vol_tab_unlock_and_rele(tp);
- 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)) {
- vol_tab_rele(tp);
- err = EINTR;
- break;
- }
- mutex_enter(&volctl.ctl_muxmutex);
- DPRINTF("vol: doing cancel on %d\n", unit);
- tp->vol_cancel = TRUE;
- cv_broadcast(&tp->vol_incv);
- vol_tab_unlock(tp);
- mutex_exit(&volctl.ctl_muxmutex);
- vol_tab_rele(tp);
- vol_enqueue(VIE_CANCEL, (void *)&unit);
- return (0);
-
- case VOLIOCSATTR: {
- STRUCT_DECL(vioc_sattr, sa);
- int attr_err;
- size_t len;
- struct ve_attr vea, *attr_ptr;
-
- if (volctl.ctl_daemon_pid == 0) {
- err = ENXIO;
- goto sattr_err;
- }
- STRUCT_INIT(sa, get_udatamodel());
- if (ddi_copyin((caddr_t)arg, STRUCT_BUF(sa), STRUCT_SIZE(sa),
- mode) != 0) {
- err = EFAULT;
- goto sattr_err;
- }
-
- /* zero out, otherwise, copyout kernel stack */
- bzero(&vea, sizeof (vea));
-
- len = STRUCT_FGET(sa, sa_attr_len);
- if (len > MAX_ATTR_LEN) {
- err = EINVAL;
- goto sattr_err;
- }
- if (ddi_copyin(STRUCT_FGETP(sa, sa_attr),
- vea.viea_attr, len, mode) != 0) {
- err = EFAULT;
- goto sattr_err;
- }
- vea.viea_attr[len] = '\0';
-
- len = STRUCT_FGET(sa, sa_value_len);
- if (len > MAX_ATTR_LEN) {
- err = EINVAL;
- goto sattr_err;
- }
- if (ddi_copyin(STRUCT_FGETP(sa, sa_value),
- vea.viea_value, len, mode) != 0) {
- err = EFAULT;
- goto sattr_err;
- }
- vea.viea_value[len] = '\0';
-
- vea.viea_unit = unit;
-
- /*
- * We need to release lock. Otherwise we fall into
- * deadlock if VOLIOCMAP/UNMAP was acquiring WRITE lock.
- * We are safe here; still tp is hold by refcnt, and
- * also vol_attr is not used until we call vol_ioctl_exit().
- */
- vol_tab_unlock(tp);
-
- if ((err = vol_ioctl_enter(&tp->vol_attr)) != 0) {
- vol_tab_rele(tp);
- return (err);
- }
- vol_enqueue(VIE_SETATTR, &vea);
- attr_ptr = &vea;
- err = vol_ioctl_wait(&tp->vol_attr, &attr_err, &attr_ptr);
- if (err == 0) {
- err = attr_err;
- /* check response */
- if (attr_ptr != &vea)
- err = EINVAL;
- }
- vol_ioctl_exit(&tp->vol_attr);
- vol_tab_rele(tp);
- return (err);
-sattr_err:
- vol_tab_unlock_and_rele(tp);
- return (err);
- }
-
- case VOLIOCGATTR: {
- STRUCT_DECL(vioc_gattr, ga);
- int attr_err;
- size_t len;
- struct ve_attr vea, *attr_ptr;
-
- if (volctl.ctl_daemon_pid == 0) {
- err = ENXIO;
- goto gattr_dun;
- }
- STRUCT_INIT(ga, get_udatamodel());
- if (ddi_copyin((caddr_t)arg, STRUCT_BUF(ga), STRUCT_SIZE(ga),
- mode) != 0) {
- err = EFAULT;
- goto gattr_dun;
- }
-
- bzero(&vea, sizeof (vea));
-
- len = STRUCT_FGET(ga, ga_attr_len);
- if (len > MAX_ATTR_LEN) {
- err = EINVAL;
- goto gattr_dun;
- }
- if (ddi_copyin(STRUCT_FGETP(ga, ga_attr),
- vea.viea_attr, len, mode) != 0) {
- err = EFAULT;
- goto gattr_dun;
- }
- vea.viea_attr[len] = '\0';
-
- vea.viea_unit = unit;
-
- /*
- * do unlock, othewise deadlock.
- */
- vol_tab_unlock(tp);
-
- if ((err = vol_ioctl_enter(&tp->vol_attr)) != 0) {
- vol_tab_rele(tp);
- return (err);
- }
- vol_enqueue(VIE_GETATTR, &vea);
- attr_ptr = &vea;
- err = vol_ioctl_wait(&tp->vol_attr, &attr_err, &attr_ptr);
- if (err == 0) {
- err = attr_err;
- if (attr_ptr != &vea)
- err = EINVAL;
- }
- vol_ioctl_exit(&tp->vol_attr);
- if (err == 0 &&
- (strlen(vea.viea_value) + 1) >
- STRUCT_FGET(ga, ga_val_len)) {
- err = EINVAL;
- }
- if (err == 0) {
- if (ddi_copyout(vea.viea_value,
- STRUCT_FGETP(ga, ga_value),
- strlen(vea.viea_value) + 1, mode) != 0) {
- err = EFAULT;
- }
- }
- vol_tab_rele(tp);
- return (err);
-gattr_dun:
- vol_tab_unlock_and_rele(tp);
- return (err);
- }
-
- case VOLIOCREMOUNT: /* the medium has a new partition structure */
- vol_enqueue(VIE_REMOUNT, (void *)&unit);
- vol_tab_unlock_and_rele(tp);
- /* may return ENODEV, even though event was queued ?? */
- return (err);
- case CDROMEJECT:
- case FDEJECT:
- case DKIOCEJECT:
- if (tp->vol_devops == NULL) {
- vol_tab_unlock_and_rele(tp);
- return (EAGAIN);
- }
- vol_tab_unlock_and_rele(tp);
- break;
-
- default:
- vol_tab_unlock_and_rele(tp);
- break;
- }
-
- /*
- * This is the part that passes ioctls on to the lower
- * level devices. Some of these may have to be trapped
- * and remapped.
- */
- if ((tp = vol_gettab(unit, VGT_WAITSIG, &err)) == NULL) {
- DPRINTF("vol: ioctl (to pass on): gettab on unit %d, err %d\n",
- unit, err);
- return (err);
- }
-
- /*
- * this is almost certainly the ENXIO case for that special
- * flag we set.
- */
- if (err)
- goto out;
-
- if (!(tp->vol_flags & ST_OPEN)) {
- err = ENXIO;
- 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: {
- dev_t savedev = tp->vol_dev;
- struct ve_eject vej;
- int status;
-
- if (volctl.ctl_daemon_pid == 0) {
- vol_tab_unlock_and_rele(tp);
- return (ENXIO);
- }
-
- vej.viej_unit = unit;
- vej.viej_force = 0;
-
- vol_tab_unlock(tp);
-
- if ((err = vol_ioctl_enter(&tp->vol_eject)) != 0) {
- vol_tab_rele(tp);
- return (err);
- }
- /* ask daemon for permission to eject */
- vol_enqueue(VIE_EJECT, (void *)&vej);
- err = vol_ioctl_wait(&tp->vol_eject, &status, NULL);
- if (err == 0) {
- if (status == VEJ_NO)
- err = EBUSY;
- }
- vol_ioctl_exit(&tp->vol_eject);
- if (err != 0) {
- /* ioctl is either signalled or rejected by vold */
- vol_tab_rele(tp);
- return (err);
- }
-
- ASSERT(savedev != NODEV);
- err = cdev_ioctl(savedev, cmd, arg,
- (mode & FMODELS) | FREAD, credp, rvalp);
- /*
- * clean out the block device.
- */
- binval(savedev); /* XXX: not DDI compliant */
-
- vol_tab_rele(tp);
- 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 */
- vol_enqueue(VIE_NEWLABEL, (void *)&unit);
- /* FALL THROUGH */
- default:
- /*
- * Pass the ioctl on down.
- */
- if (tp->vol_dev == NODEV) {
- err = EIO;
-
- DPRINTF("vol: tp->vol_dev = NODEV\n");
- DPRINTF("vol: ioctl: dev %u.%u cmd %x arg %lx "
- "mode %x credp %p rvalp %p\n",
- getmajor(dev), getminor(dev),
- cmd, arg, mode, (void *)credp, (void *)rvalp);
- break;
- }
- err = cdev_ioctl(tp->vol_dev, cmd, arg,
- mode & ~FWRITE, credp, rvalp);
- break;
- }
- /* release lock, return err */
-out:
- vol_tab_unlock_and_rele(tp);
- return (err);
-}
-
-
-static int
-volpoll(dev_t dev, short events, int anyyet, short *reventsp,
- struct pollhead **phpp)
-{
- int unit;
- struct vol_tab *tp;
- int err = 0;
-
- DPRINTF4(
- "vol: poll: dev %u.%u events 0x%x anyyet 0x%x revents 0x%x\n",
- getmajor(dev), getminor(dev), (int)events, anyyet,
- (int)*reventsp);
-
- unit = getminor(dev);
- if (unit == 0) {
- if (volctl.ctl_open == 0 || volctl.ctl_daemon_pid == 0) {
- *reventsp |= POLLNVAL;
- return (EINVAL);
- }
- if (events & POLLRDNORM) {
- DPRINTF4("vol: poll: got a POLLRDNORM\n");
- mutex_enter(&volctl.ctl_evmutex);
- if (volctl.ctl_evcnt) {
- DPRINTF3("vol: poll: we have data\n");
- *reventsp |= POLLRDNORM;
- mutex_exit(&volctl.ctl_evmutex);
- return (0);
- }
- mutex_exit(&volctl.ctl_evmutex);
- }
- if (!anyyet) {
- *phpp = &vol_pollhead;
- *reventsp = 0;
- }
- return (0);
- }
-
- if ((tp = vol_gettab(unit, VGT_WAITSIG, &err)) == NULL) {
- *reventsp |= POLLERR;
- return (err);
- }
-
- if ((tp->vol_flags & ST_OPEN) == 0) {
- *reventsp |= POLLERR;
- err = ENXIO;
- goto out;
- }
- ASSERT(tp->vol_dev != NODEV);
- err = cdev_poll(tp->vol_dev, events, anyyet, reventsp, phpp);
-
-out:
- vol_tab_unlock_and_rele(tp);
- return (err);
-}
-
-
-static void
-vol_enqueue(enum vie_event type, void *data)
-{
- struct kvioc_event *kvie;
- cred_t *c;
- proc_t *p;
- uid_t uid;
- gid_t gid;
- dev_t ctty;
-
-
- kvie = kmem_alloc(sizeof (*kvie), KM_SLEEP);
- kvie->kve_event.vie_type = type;
-
- /* 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 {
- uid = crgetuid(c);
- gid = crgetgid(c);
- }
-
- if (drv_getparm(UPROCP, &p) != 0) {
- DPRINTF("vol: vol_enqueue: couldn't get uprocp\n");
- ctty = NODEV;
- } else {
- ctty = cttydev(p);
- }
-
- DPRINTF2("vol: vol_enqueue: uid=%d, gid=%d, ctty=0x%lx\n", uid,
- gid, ctty);
-
- switch (type) {
- case VIE_MISSING:
- kvie->kve_event.vie_missing = *(struct ve_missing *)data;
- kvie->kve_event.vie_missing.viem_user = uid;
- kvie->kve_event.vie_missing.viem_tty = ctty;
- break;
- case VIE_INSERT:
- kvie->kve_event.vie_insert.viei_dev = *(dev_t *)data;
- break;
- case VIE_CHECK:
- kvie->kve_event.vie_check.viec_dev = *(dev_t *)data;
- break;
- case VIE_INUSE:
- kvie->kve_event.vie_inuse.vieu_dev = *(dev_t *)data;
- break;
- case VIE_EJECT:
- kvie->kve_event.vie_eject = *(struct ve_eject *)data;
- kvie->kve_event.vie_eject.viej_user = uid;
- kvie->kve_event.vie_eject.viej_tty = ctty;
- break;
- case VIE_DEVERR:
- kvie->kve_event.vie_error = *(struct ve_error *)data;
- break;
- case VIE_CLOSE:
- kvie->kve_event.vie_close.viecl_unit = *(minor_t *)data;
- break;
- case VIE_REMOVED:
- kvie->kve_event.vie_rm.virm_unit = *(minor_t *)data;
- break;
- case VIE_CANCEL:
- kvie->kve_event.vie_cancel.viec_unit = *(minor_t *)data;
- break;
- case VIE_NEWLABEL:
- kvie->kve_event.vie_newlabel.vien_unit = *(minor_t *)data;
- break;
- case VIE_GETATTR:
- case VIE_SETATTR:
- kvie->kve_event.vie_attr = *(struct ve_attr *)data;
- kvie->kve_event.vie_attr.viea_uid = uid;
- kvie->kve_event.vie_attr.viea_gid = gid;
- break;
- case VIE_SYMNAME:
- kvie->kve_event.vie_symname.vies_dev = *(dev_t *)data;
- break;
- case VIE_SYMDEV:
- kvie->kve_event.vie_symdev = *(struct ve_symdev *)data;
- break;
- case VIE_REMOUNT:
- kvie->kve_event.vie_remount.vier_unit = *(minor_t *)data;
- break;
- default:
- cmn_err(CE_WARN, "vol_enqueue: bad type %d", type);
- kmem_free(kvie, sizeof (*kvie));
- return;
- }
-
- mutex_enter(&volctl.ctl_evmutex);
- if (volctl.ctl_evend) {
- insque(kvie, volctl.ctl_evend);
- } else {
- insque(kvie, &volctl.ctl_events);
- }
- volctl.ctl_evend = kvie;
- volctl.ctl_evcnt++;
- mutex_exit(&volctl.ctl_evmutex);
- pollwakeup(&vol_pollhead, POLLRDNORM);
-}
-
-#ifdef _SYSCALL32_IMPL
-static int
-vol_event32(struct vioc_event32 *e32, struct vioc_event *e)
-{
- int err = 0;
-
- bzero(e32, sizeof (*e32));
-
-#define E2E32(x) e32->x = e->x
-
- E2E32(vie_type);
- switch (e->vie_type) {
- case VIE_MISSING:
- E2E32(vie_missing.viem_unit);
- E2E32(vie_missing.viem_ndelay);
- E2E32(vie_missing.viem_user);
- if (!cmpldev(&(e32->vie_missing.viem_tty),
- e->vie_missing.viem_tty))
- err = EOVERFLOW;
- break;
- case VIE_EJECT:
- E2E32(vie_eject.viej_unit);
- E2E32(vie_eject.viej_user);
- if (!cmpldev(&(e32->vie_eject.viej_tty),
- e->vie_eject.viej_tty))
- err = EOVERFLOW;
- E2E32(vie_eject.viej_force);
- break;
- case VIE_DEVERR:
- if (!cmpldev(&(e32->vie_error.viee_dev),
- e->vie_error.viee_dev))
- err = EOVERFLOW;
- E2E32(vie_error.viee_errno);
- break;
- case VIE_CLOSE:
- E2E32(vie_close.viecl_unit);
- break;
- case VIE_CANCEL:
- E2E32(vie_cancel.viec_unit);
- break;
- case VIE_NEWLABEL:
- E2E32(vie_newlabel.vien_unit);
- break;
- case VIE_INSERT:
- if (!cmpldev(&(e32->vie_insert.viei_dev),
- e->vie_insert.viei_dev))
- err = EOVERFLOW;
- break;
- case VIE_SETATTR:
- case VIE_GETATTR:
- E2E32(vie_attr.viea_unit);
- /* copy all the data including the null terminate */
- bcopy(e->vie_attr.viea_attr, e32->vie_attr.viea_attr,
- MAX_ATTR_LEN + 1);
- bcopy(e->vie_attr.viea_value, e32->vie_attr.viea_value,
- MAX_ATTR_LEN + 1);
- E2E32(vie_attr.viea_uid);
- E2E32(vie_attr.viea_gid);
- break;
- case VIE_INUSE:
- if (!cmpldev(&(e32->vie_inuse.vieu_dev),
- e->vie_inuse.vieu_dev))
- err = EOVERFLOW;
- break;
- case VIE_CHECK:
- if (!cmpldev(&(e32->vie_check.viec_dev),
- e->vie_check.viec_dev))
- err = EOVERFLOW;
- break;
- case VIE_REMOVED:
- E2E32(vie_rm.virm_unit);
- break;
- case VIE_SYMNAME:
- if (!cmpldev(&(e32->vie_symname.vies_dev),
- e->vie_symname.vies_dev))
- err = EOVERFLOW;
- break;
- case VIE_SYMDEV:
- bcopy(e->vie_symdev.vied_symname, e32->vie_symdev.vied_symname,
- VOL_SYMNAME_LEN + 1);
- break;
- case VIE_REMOUNT:
- E2E32(vie_remount.vier_unit);
- 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
- * returned has the read/write lock held as a reader
- */
-static struct vol_tab *
-vol_gettab(int unit, uint_t flags, int *err)
-{
- struct vol_tab *tp;
- int rv;
- struct ve_missing ve;
-
- *err = 0;
-
- mutex_enter(&volctl.ctl_muxmutex);
-
- /*
- * 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.
- */
- if ((volctl.ctl_open == 0 && (flags & VGT_OPEN) == 0) ||
- (volctl.ctl_closing && (flags & VGT_CLOSE) == 0)) {
- mutex_exit(&volctl.ctl_muxmutex);
- *err = ENXIO;
- DPRINTF("vol: gettab: ctl unit not open (ENXIO)!\n");
- return (NULL);
- }
-
- ASSERT(unit >= 0);
- if (unit < 0) {
- mutex_exit(&volctl.ctl_muxmutex);
- *err = ENOTTY;
- DPRINTF("vol: vol_gettab: negative unit number!\n");
- return (NULL);
- }
-
- tp = (struct vol_tab *)ddi_get_soft_state(voltab, unit);
- if (tp == NULL) {
- /* this unit not yet created */
- if (flags & VGT_NEW) {
- /* the "create" flag is set, so create it */
- *err = ddi_soft_state_zalloc(voltab, unit);
- if (*err) {
- DPRINTF("vol: zalloc broke vol %d\n", unit);
- *err = ENODEV;
- mutex_exit(&volctl.ctl_muxmutex);
- goto out;
- }
- tp = (struct vol_tab *)
- ddi_get_soft_state(voltab, unit);
- if (tp == NULL) {
- DPRINTF("vol: soft state was null!\n");
- *err = ENOTTY;
- mutex_exit(&volctl.ctl_muxmutex);
- goto out;
- }
- vol_tab_init(tp);
- tp->vol_unit = unit;
- } else {
- /* didn't build a new one and we don't have one */
- DPRINTF("vol: vol_gettab: ENODEV\n");
- *err = ENODEV;
- mutex_exit(&volctl.ctl_muxmutex);
- goto out;
- }
- }
- tp->vol_refcnt++;
-
- /* now we know the unit exists */
-
- /* keep track of the largest unit number we've seen */
- if (unit > volctl.ctl_maxunit) {
- volctl.ctl_maxunit = unit;
- }
-
- mutex_exit(&volctl.ctl_muxmutex);
-
- if (flags & VGT_WAITSIG) {
- if (vol_tab_rlock_sig(tp)) {
- vol_tab_rele(tp);
- *err = EINTR;
- return (NULL);
- }
- } else {
- vol_tab_rlock(tp);
- }
-
- /* check for ops already gotten, or for /dev/volctl */
- if ((tp->vol_devops != NULL) || (unit == 0)) {
- /* got it! */
- goto out;
- }
-
- /* no vol_devops yet (and unit not /dev/volctl) */
-
- 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);
- *err = ENODEV;
- goto out;
- }
-
- /* no vol_devops yet, but caller is willing to wait */
-
-#ifdef DEBUG_ENXIO
- if (tp->vol_flags & ST_ENXIO) {
- DPRINTF("vol_gettab: no mapping for %d: doing ENXIO\n", unit);
- } else {
- DPRINTF("vol_gettab: no mapping for %d: it's MISSING "
- "(flags=0x%x, tp=%p)\n", unit, tp->vol_flags, (void *)tp);
- }
-#endif
- if (tp->vol_flags & ST_ENXIO) {
- /*
- * 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);
- vol_tab_unlock_and_rele(tp);
- tp = NULL;
- *err = ENXIO;
- goto out;
- }
-
- /*
- * there isn't a mapping -- enqueue a missing message to the
- * daemon and wait around until it appears
- */
- ve.viem_unit = unit;
- ve.viem_ndelay = (flags & VGT_NDELAY) ? TRUE : FALSE;
-
- /*
- * hang around until a unit appears or we're cancelled.
- */
- while (tp->vol_devops == NULL) {
- 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 "
- "(ndelay=%d)\n", unit, ve.viem_ndelay);
- vol_enqueue(VIE_MISSING, (void *)&ve);
-
- /*
- * 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.
- */
- mutex_enter(&volctl.ctl_muxmutex);
-
- /* release rlock so that VOLIOCMAP can update devops */
- vol_tab_unlock(tp);
-
- /* wait right here */
- if (flags & VGT_WAITSIG) {
- rv = cv_wait_sig(&tp->vol_incv, &volctl.ctl_muxmutex);
- 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.
- */
- mutex_exit(&volctl.ctl_muxmutex);
- vol_tab_rele(tp);
- *err = EINTR;
- tp = NULL;
- break;
- }
- } else {
- /* can't be interrupted by a signal */
- cv_wait(&tp->vol_incv, &volctl.ctl_muxmutex);
- }
-
- /* we may have signalled from close(). */
- if (volctl.ctl_daemon_pid == 0) {
- mutex_exit(&volctl.ctl_muxmutex);
- vol_tab_rele(tp);
- *err = ENXIO;
- tp = NULL;
- break;
- }
-
- /*
- * muxmutex is no longer necessary. It should be released
- * before acquiring rlock, so that thread running VOLIOCMAP
- * etc won't deadlock.
- */
- mutex_exit(&volctl.ctl_muxmutex);
-
- /* 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)) {
- vol_tab_rele(tp);
- *err = EINTR;
- tp = NULL;
- break;
- }
- } else {
- vol_tab_rlock(tp);
- }
-
- DPRINTF2("vol: vol_gettab: insert cv wakeup rcvd\n");
-
- if ((flags & VGT_NDELAY) && (tp->vol_dev == NODEV))
- break;
- }
-out:
- /*
- * If the device is "cancelled", don't return the tp unless
- * the caller really wants it (nowait and ndelay).
- */
- if ((tp != NULL) && tp->vol_cancel && !(flags & VGT_NOWAIT) &&
- !(flags & VGT_NDELAY)) {
- DPRINTF("vol: vol_gettab: cancel (flags 0x%x)\n", flags);
- *err = EIO;
- vol_tab_unlock_and_rele(tp);
- tp = NULL;
- }
- if (*err != 0) {
- DPRINTF("vol: vol_gettab: err=%d unit=%d, tp=%p\n",
- *err, unit, (void *)tp);
- }
- return (tp);
-}
-
-
-/*
- * Unmap *tp. Removes it from the ddi_soft_state list.
- */
-static void
-vol_unmap(struct vol_tab *tp)
-{
- char mname[16];
-
- ASSERT(MUTEX_HELD(&volctl.ctl_muxmutex));
- ASSERT(volctl.ctl_dip);
-
- /* wait until everyone is done with it */
- vol_tab_rele_wait(tp);
-
- /* release underlying driver */
- vol_release_driver(tp);
-
- /* get rid of the thing */
- vol_tab_fini(tp);
-
- /* remove minor node */
- (void) snprintf(mname, sizeof (mname), VOLUNITNAME_BLK, tp->vol_unit);
- ddi_remove_minor_node(volctl.ctl_dip, mname);
-
- (void) snprintf(mname, sizeof (mname), VOLUNITNAME_CHR, tp->vol_unit);
- ddi_remove_minor_node(volctl.ctl_dip, mname);
-
- /* done */
- ddi_soft_state_free(voltab, tp->vol_unit);
-}
-
-
-/*
- * 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 vol_tab *tp;
- 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.
- */
- mutex_enter(&volctl.ctl_muxmutex);
- volctl.ctl_closing = 1;
- mutex_exit(&volctl.ctl_muxmutex);
-
- for (i = 1; i < (volctl.ctl_maxunit + 1); i++) {
-
- tp = vol_gettab(i, VGT_NOWAIT|VGT_CLOSE, &err);
- if (tp == NULL)
- continue;
-
- DPRINTF("vol_cleanup: unit %d\n", i);
-
- vol_ioctl_fail(&tp->vol_attr);
- /* cancel pending eject requests */
- vol_ioctl_fail(&tp->vol_eject);
-
- /*
- * 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.
- */
- vol_tab_rwlock_upgrade(tp);
- mutex_enter(&volctl.ctl_muxmutex);
-
- /* send a "cancel" for pending missing events */
- tp->vol_cancel = TRUE;
- cv_broadcast(&tp->vol_incv);
-
- vol_unmap(tp);
- /* tp is no longer valid after a vol_umnap() */
-
- mutex_exit(&volctl.ctl_muxmutex);
- }
-
- DPRINTF("vol: vol_cleanup: cleared from 0 to %d\n",
- volctl.ctl_maxunit);
-
- volctl.ctl_maxunit = 0;
-
- /*
- * handle threads waiting for replies from the daemon
- */
- vol_ioctl_fail(&volctl.ctl_inuse);
- vol_ioctl_fail(&volctl.ctl_insert);
- vol_ioctl_fail(&volctl.ctl_symname);
- vol_ioctl_fail(&volctl.ctl_symdev);
-
- /*
- * Free up anything lurking on the event queue.
- */
- mutex_enter(&volctl.ctl_evmutex);
- while (volctl.ctl_evcnt != 0) {
- kve = volctl.ctl_events.kve_next;
- volctl.ctl_evcnt--;
- remque(kve);
- kmem_free(kve, sizeof (*kve));
- }
- volctl.ctl_evend = NULL;
- mutex_exit(&volctl.ctl_evmutex);
-
- /* wake up threads if sleeping in poll() */
- pollwakeup(&vol_pollhead, POLLERR);
-
- /*
- * release memory only needed while the daemon is running
- */
- mutex_enter(&volctl.ctl_volrmutex);
- if (vold_root != NULL) {
- kmem_free(vold_root, vold_root_len + 1);
- vold_root = NULL;
- vold_root_len = 0;
- }
- mutex_exit(&volctl.ctl_volrmutex);
-
- /* re-enable the ioctls */
- vol_ioctl_enable(&volctl.ctl_inuse);
- vol_ioctl_enable(&volctl.ctl_insert);
- vol_ioctl_enable(&volctl.ctl_symname);
- vol_ioctl_enable(&volctl.ctl_symdev);
-
- 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
-vol_checkmedia(struct vol_tab *tp, int *found_media)
-{
- int err = 0;
- int badnews = 0;
- struct vol_tab *tp0;
-
- DPRINTF2("vol: checkmedia\n");
-
- /* do the grotty stuff to get the answer */
- badnews = vol_checkmedia_machdep(tp);
-
- /* check to see if there's no media in the drive */
- if (badnews) {
- /* there's no media in the drive */
-
- if (found_media) {
- *found_media = FALSE; /* return result */
- }
-
- if (vol_tab_rwlock_upgrade_sig(tp))
- return (EINTR);
-
- /* unmap the device */
- vol_release_driver(tp);
- vol_tab_unlock(tp);
-
- vol_enqueue(VIE_REMOVED, (void *)&tp->vol_unit);
-
- /* get the mapping for this device, waiting if needed */
- DPRINTF("vol: checkmedia: calling gettab\n");
- tp0 = vol_gettab(tp->vol_unit, VGT_WAITSIG, &err);
- if (tp0 == NULL && err == EINTR)
- return (EINTR);
- if (tp0 != NULL)
- vol_tab_unlock_and_rele(tp0);
-
- 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) {
- *found_media = TRUE; /* return results */
- }
- }
- /* all done */
- return (err);
-}
-
-
-/*
- * return the bad news: media there (0) or not (1).
- */
-static int
-vol_checkmedia_machdep(struct vol_tab *tp)
-{
- int err;
- int fl_rval = 0; /* bitmap word: all bits clear initially */
-
-
- switch (tp->vol_mtype) {
- case MT_FLOPPY:
- /* check for a floppy disk in the drive */
-
- /* ensure we have a dev to do the ioctl on */
- if (tp->vol_dev == NODEV) {
- /* 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.
- */
- mutex_enter(&floppy_chk_mutex);
- err = cdev_ioctl(tp->vol_dev, FDGETCHANGE,
- (intptr_t)&fl_rval, FNATIVE | FKIOCTL, kcred, NULL);
- mutex_exit(&floppy_chk_mutex);
-
- 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:
- DPRINTF("vol: checkmedia: bad mtype %d\n", tp->vol_mtype);
- return (1);
- }
- /*NOTREACHED*/
-}
-
-/*
- * Release the driver and cleanup unnecessary stuff in vol_tab.
- */
-static void
-vol_release_driver(struct vol_tab *tp)
-{
- if (tp->vol_dev != NODEV && tp->vol_devops != NULL) {
- ddi_rele_driver(getmajor(tp->vol_dev));
- DPRINTF3("vol: released driver %u\n", getmajor(tp->vol_dev));
- }
- tp->vol_dev = NODEV;
- tp->vol_devops = NULL;
- /* drop media related flags */
- tp->vol_flags &= ~(ST_CHKMEDIA|ST_RDONLY);
- if (tp->vol_path != NULL)
- kmem_free(tp->vol_path, tp->vol_pathlen + 1);
- tp->vol_path = NULL;
- tp->vol_pathlen = 0;
-}
-
-/*
- * return 0 if you are vold.
- */
-static int
-vol_daemon_check(void)
-{
- pid_t pid;
-
- if (volctl.ctl_daemon_pid == 0)
- return (ENXIO);
- (void) drv_getparm(PPID, &pid);
- if (volctl.ctl_daemon_pid != pid)
- 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
- * is reqeusting a response from vold for particular uni/ioctl.
- *
- * vol_ioctl_wait()
- * set argument in argp which will be used by by vold ioctl, and
- * wait for vold's response. value/ptr from vold are also returned
- * via rval/rptr arguments.
- *
- * 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
-vol_ioctl_init(struct vol_ioctl *vic)
-{
- mutex_init(&vic->mutex, NULL, MUTEX_DRIVER, NULL);
- cv_init(&vic->cv, NULL, CV_DRIVER, NULL);
- cv_init(&vic->s_cv, NULL, CV_DRIVER, NULL);
- cv_init(&vic->close_cv, NULL, CV_DRIVER, NULL);
- vic->nwait = 0;
- vic->active = 0;
- vic->closing = 0;
-}
-
-static void
-vol_ioctl_fini(struct vol_ioctl *vic)
-{
- mutex_destroy(&vic->mutex);
- cv_destroy(&vic->cv);
- cv_destroy(&vic->s_cv);
- cv_destroy(&vic->close_cv);
-}
-
-static int
-vol_ioctl_enter(struct vol_ioctl *vic)
-{
- int r;
-
- mutex_enter(&vic->mutex);
- if (vic->closing) {
- mutex_exit(&vic->mutex);
- return (ENXIO);
- }
- while (vic->active) {
- vic->nwait++;
- r = cv_wait_sig(&vic->s_cv, &vic->mutex);
- vic->nwait--;
- if (vic->closing) {
- if (vic->closewait && vic->nwait == 0) {
- /* I'm the last */
- cv_signal(&vic->close_cv);
- }
- mutex_exit(&vic->mutex);
- if (r == 0)
- return (EINTR);
- else
- return (ENXIO);
- }
- if (r == 0) {
- /* signal pending */
- mutex_exit(&vic->mutex);
- return (EINTR);
- }
- }
- vic->active = 1;
- return (0);
-}
-
-static int
-vol_ioctl_wait(struct vol_ioctl *vic, int *rvalp, void *rptrp)
-{
- int err = 0;
-
- vic->rval = -1;
- vic->rptr = NULL; /* clear return pointer in case of error */
- if (rptrp != NULL)
- vic->argp = *(uintptr_t *)rptrp;
- while (vic->rval == -1) {
- if (cv_wait_sig(&vic->cv, &vic->mutex) == 0) {
- /* we are interrupted */
- vic->rval = 0;
- err = EINTR;
- break;
- }
- }
- if (vic->closing || volctl.ctl_daemon_pid == 0) {
- /* the daemon is dying, or has already died */
- err = ENXIO;
- }
- /* return data from vold */
- if (rptrp != NULL)
- *(uintptr_t *)rptrp = vic->rptr; /* set return pointer */
- *rvalp = vic->rval;
- return (err);
-}
-
-static void
-vol_ioctl_exit(struct vol_ioctl *vic)
-{
- vic->active = 0;
- if (vic->nwait != 0) {
- cv_broadcast(&vic->s_cv);
- } else if (vic->closewait) {
- /*
- * I'm the last one. wake up the thread sleeping
- * in vol_ioctl_fail.
- */
- cv_signal(&vic->close_cv);
- }
- mutex_exit(&vic->mutex);
-}
-
-static void
-vol_ioctl_fail(struct vol_ioctl *vic)
-{
-
- mutex_enter(&vic->mutex);
- if (vic->active) {
- /*
- * 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 (vic->rval == -1) {
- vic->rval = 0;
- vic->rptr = NULL;
- }
- }
- vic->closing = 1;
- vic->closewait = 0;
- if (vic->nwait)
- cv_broadcast(&vic->s_cv);
- if (vic->active)
- cv_broadcast(&vic->cv);
- /*
- * If ioctl is active, or someone is waiting for ioctl.
- */
- while (vic->active || vic->nwait > 0) {
- vic->closewait = 1;
- cv_wait(&vic->close_cv, &vic->mutex);
- vic->closewait = 0;
- }
- mutex_exit(&vic->mutex);
-}
-
-static void
-vol_ioctl_enable(struct vol_ioctl *vic)
-{
- mutex_enter(&vic->mutex);
- vic->closing = 0;
- mutex_exit(&vic->mutex);
-}
-
-/*
- * 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
-vold_ioctl_enter(struct vol_ioctl *vic, void *rptrp)
-{
- mutex_enter(&vic->mutex);
- if (vic->closing) {
- /* should not happen, but */
- mutex_exit(&vic->mutex);
- return (ENXIO);
- }
- if (!vic->active) {
- mutex_exit(&vic->mutex);
- return (EAGAIN);
- }
- if (rptrp != NULL)
- *(uintptr_t *)rptrp = vic->argp;
- return (0);
-}
-
-static void
-vold_ioctl_respond(struct vol_ioctl *vic, int rval, void *rptr)
-{
- ASSERT(rval != -1);
- vic->rval = rval;
- vic->rptr = (uintptr_t)rptr;
- cv_signal(&vic->cv);
-}
-
-static void
-vold_ioctl_exit(struct vol_ioctl *vic)
-{
- mutex_exit(&vic->mutex);
-}
-
-/*
- * vol_tab lock/unlock functions. It used to be reader/writer lock,
- * 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,
- * increment reader count. If not yet read/write locked, set lock word,
- * 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
-vol_tab_init(struct vol_tab *tp)
-{
- vol_ioctl_init(&tp->vol_eject);
- vol_ioctl_init(&tp->vol_attr);
- mutex_init(&tp->vol_rwlck_mutex, NULL, MUTEX_DRIVER, NULL);
- cv_init(&tp->vol_incv, NULL, CV_DRIVER, NULL);
- cv_init(&tp->vol_rwlck_cv, NULL, CV_DRIVER, NULL);
- cv_init(&tp->vol_rele_cv, NULL, CV_DRIVER, NULL);
-}
-
-static void
-vol_tab_fini(struct vol_tab *tp)
-{
- vol_ioctl_fini(&tp->vol_eject);
- vol_ioctl_fini(&tp->vol_attr);
- mutex_destroy(&tp->vol_rwlck_mutex);
- cv_destroy(&tp->vol_incv);
- cv_destroy(&tp->vol_rwlck_cv);
- cv_destroy(&tp->vol_rele_cv);
-}
-
-static int
-vol_tab_rlock_unlocked(struct vol_tab *tp, boolean_t waitsig)
-{
- ASSERT(!MUTEX_HELD(&volctl.ctl_muxmutex));
- /* wait until wlock is released */
- while (tp->vol_locked == VOL_TAB_WR_LOCKED) {
- tp->vol_lckwaiter++;
- if (waitsig) {
- if (cv_wait_sig(&tp->vol_rwlck_cv,
- &tp->vol_rwlck_mutex) == 0) {
- tp->vol_lckwaiter--;
- return (EINTR);
- }
- } else {
- cv_wait(&tp->vol_rwlck_cv, &tp->vol_rwlck_mutex);
- }
- tp->vol_lckwaiter--;
- }
- tp->vol_locked = VOL_TAB_RD_LOCKED;
- tp->vol_nreader++;
- return (0);
-}
-
-static void
-vol_tab_rlock(struct vol_tab *tp)
-{
- mutex_enter(&tp->vol_rwlck_mutex);
- (void) vol_tab_rlock_unlocked(tp, B_FALSE);
- mutex_exit(&tp->vol_rwlck_mutex);
-}
-
-static int
-vol_tab_rlock_sig(struct vol_tab *tp)
-{
- int r;
-
- mutex_enter(&tp->vol_rwlck_mutex);
- r = vol_tab_rlock_unlocked(tp, B_TRUE);
- mutex_exit(&tp->vol_rwlck_mutex);
- return (r);
-}
-
-static void
-vol_tab_unlock_unlocked(struct vol_tab *tp)
-{
- ASSERT(tp->vol_locked != VOL_TAB_UNLOCKED);
-
- if (tp->vol_locked == VOL_TAB_RD_LOCKED) {
- ASSERT(tp->vol_nreader != 0);
- if (--tp->vol_nreader == 0) {
- tp->vol_locked = VOL_TAB_UNLOCKED;
- }
- } else if (tp->vol_locked == VOL_TAB_WR_LOCKED) {
- tp->vol_locked = VOL_TAB_UNLOCKED;
- }
- if (tp->vol_locked == VOL_TAB_UNLOCKED && tp->vol_lckwaiter != 0) {
- cv_broadcast(&tp->vol_rwlck_cv);
- }
-}
-
-static void
-vol_tab_unlock(struct vol_tab *tp)
-{
- mutex_enter(&tp->vol_rwlck_mutex);
- vol_tab_unlock_unlocked(tp);
- mutex_exit(&tp->vol_rwlck_mutex);
-}
-
-static int
-vol_tab_rwlock_upgrade_unlocked(struct vol_tab *tp, boolean_t waitsig)
-{
- ASSERT(!MUTEX_HELD(&volctl.ctl_muxmutex));
- ASSERT(tp->vol_locked == VOL_TAB_RD_LOCKED && tp->vol_nreader > 0);
-
- if (tp->vol_nreader == 1) {
- tp->vol_nreader = 0;
- tp->vol_locked = VOL_TAB_WR_LOCKED;
- return (0);
- }
- vol_tab_unlock_unlocked(tp); /* release READ lock */
- while (tp->vol_locked != VOL_TAB_UNLOCKED) {
- tp->vol_lckwaiter++;
- if (waitsig) {
- if (cv_wait_sig(&tp->vol_rwlck_cv,
- &tp->vol_rwlck_mutex) == 0) {
- tp->vol_lckwaiter--;
- return (EINTR);
- }
- } else {
- cv_wait(&tp->vol_rwlck_cv, &tp->vol_rwlck_mutex);
- }
- tp->vol_lckwaiter--;
- }
- ASSERT(tp->vol_nreader == 0);
- tp->vol_locked = VOL_TAB_WR_LOCKED;
- return (0);
-}
-
-static void
-vol_tab_rwlock_upgrade(struct vol_tab *tp)
-{
- mutex_enter(&tp->vol_rwlck_mutex);
- (void) vol_tab_rwlock_upgrade_unlocked(tp, B_FALSE);
- mutex_exit(&tp->vol_rwlck_mutex);
-}
-
-static int
-vol_tab_rwlock_upgrade_sig(struct vol_tab *tp)
-{
- int r;
-
- mutex_enter(&tp->vol_rwlck_mutex);
- r = vol_tab_rwlock_upgrade_unlocked(tp, B_TRUE);
- mutex_exit(&tp->vol_rwlck_mutex);
- return (r);
-}
-
-static void
-vol_tab_rele(struct vol_tab *tp)
-{
- mutex_enter(&volctl.ctl_muxmutex);
- ASSERT(tp->vol_refcnt != 0);
- tp->vol_refcnt--;
- if (tp->vol_relewait && tp->vol_refcnt == 0) {
- /* I'm the last */
- cv_broadcast(&tp->vol_rele_cv);
- }
- mutex_exit(&volctl.ctl_muxmutex);
-}
-
-static void
-vol_tab_unlock_and_rele(struct vol_tab *tp)
-{
- ASSERT(!MUTEX_HELD(&volctl.ctl_muxmutex));
- vol_tab_unlock(tp);
- vol_tab_rele(tp);
-}
-
-static void
-vol_tab_rele_wait(struct vol_tab *tp)
-{
- ASSERT(MUTEX_HELD(&volctl.ctl_muxmutex));
- vol_tab_unlock(tp);
- tp->vol_refcnt--;
- while (tp->vol_refcnt > 0) {
- tp->vol_relewait = 1;
- cv_wait(&tp->vol_rele_cv, &volctl.ctl_muxmutex);
- tp->vol_relewait = 0;
- }
-}