vold_vol.c revision 5c43205d04ce612cd2940e062ab48365a5b3d42a
/*
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Volume Daemon primary interface to the vol driver
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <locale.h>
#include <errno.h>
#include <rpc/auth_unix.h>
#include <rpcsvc/nfs_prot.h>
#include <thread.h>
#include <synch.h>
#include "vold.h"
#include "label.h"
#include "dev.h"
#include "medium.h"
int vol_fd = -1;
extern bool_t support_nomedia;
struct volins {
void *vi_stk; /* stack for the thread */
};
/*
* will be obsolete
*/
struct q alabq;
static struct q async_taskq;
static bool_t async_taskq_run;
static void vol_event_unlocked(struct vioc_event *);
static void insert_medium(dev_t *);
static void remount_medium(dev_t *);
static void vol_forceout(vol_t *);
static void vol_insert(struct volins *);
static void vol_missing(struct ve_missing *);
static int vol_reaper(struct reap *);
/*
* thread stack size
*/
vol_init(void)
{
namebuf);
return (FALSE);
}
/* set up the driver */
/*NOTREACHED*/
}
/* set up our mutex */
return (TRUE);
}
void
{
struct async_task *as;
struct vioc_event *viep;
/*
* there may be a chance that the main thread goes into
* this route such as via floppy_check(). In such case,
* lock has been held, thus async_task_mutex is a recursive
* mutex.
*
* Any events which may create async tasks should be
* queued up, so that it's safely dispatched by the
* asyncq dispatcher.
*/
case VIE_INSERT:
case VIE_REMOUNT:
case VIE_EJECT:
break;
default:
return;
}
/*
* we aready have same event queued up. just ignore
* this event.
*/
return;
}
}
static struct devs *
{
vol_t *v;
case VIE_INSERT:
break;
case VIE_REMOUNT:
if (v != NULL)
break;
case VIE_EJECT:
if (v != NULL)
break;
}
return (dp);
}
void
vol_readevents(void)
{
int err;
struct vioc_event vie;
for (;;) {
err);
}
if (err != 0) {
if (errno == EWOULDBLOCK) {
return;
}
perror("vol_readevents");
return;
}
}
}
static char *event_names[] = {
"VIE_MISSING",
"VIE_EJECT",
"VIE_DEVERR",
"VIE_CLOSE",
"VIE_CANCEL",
"VIE_NEWLABEL",
"VIE_INSERT",
"VIE_GETATTR",
"VIE_SETATTR",
"VIE_INUSE",
"VIE_CHECK",
"VIE_REMOVED",
"VIE_SYMNAME",
"VIE_SYMDEV",
"VIE_REMOUNT",
};
static void
{
int err = 0;
vol_t *v;
char event_str[50];
} else {
}
case VIE_CHECK: {
int rval;
/*
* dev_check returns:
* 0 if it didn't find anything
* 1 if it foudn something and we already knew about it
* 2 if it found something and we generated an insert event
*/
if (rval == 0) {
} else if (rval == 1) {
}
/*
* If there was something there, a flag was set
* in the dp structure saying that a response needs to
* be made "as late as possible". In other words,
* if there are no actions to be done, do the response
* after the name space has been built. If there are
* actions to be done, respond after they have completed.
*/
break;
}
case VIE_SYMNAME: {
} else {
}
debug(11,
"vol_event: VIE_SYMNAME: (%d,%d) -> \"%s\" (len %d)\n",
break;
}
case VIE_SYMDEV: {
extern char *symname_to_dev(char *);
char *symname;
/*
* be sure symname is not NULL and symname_to_dev does not
* return NULL
*/
} else {
}
} else {
}
break;
}
case VIE_INUSE: {
extern bool_t mount_complete;
if (!mount_complete) {
debug(5,
"vol_event: VIE_INUSE: poll NOT being done yet\n");
} else {
debug(5,
"vol_event: VIE_INUSE: check for (%d,%d) use\n",
/*
* dev_inuse returns TRUE if the device
* is managed, and FALSE if it isn't
*/
}
}
}
break;
}
case VIE_INSERT: {
dp->dp_asynctask++;
/*
* If the node is a "nomedia" node, then
* remove it.
*/
}
}
if (thr_create(0, VOL_STKSIZE,
(void *(*)(void *))insert_medium,
dp->dp_asynctask--;
}
} else {
" tid %d (%d,%d) (for INSERT)\n",
}
break;
}
case VIE_MISSING:
break;
case VIE_EJECT:
/*
* Is it already gone?
*/
break;
}
v->v_ej_force = TRUE;
} else {
if (v == NULL) {
break;
}
v->v_ej_force = FALSE;
}
if (v->v_ej_inprog) {
/* ejection in progress... ignore */
break;
}
v->v_ej_inprog = TRUE;
/*
* If we're ejecting a piece of media that a new label
* has been written to -- read the new label off before
* proceeding with the ejection.
*/
dp->dp_asynctask++;
if (thr_create(0, VOL_STKSIZE,
(void *(*)(void *))vol_insert, (void *)vid,
dp->dp_asynctask--;
} else {
debug(6,
}
v->v_flags &= ~V_NEWLABEL;
} else {
}
}
break;
case VIE_DEVERR:
break;
case VIE_CLOSE: {
v = minor_getvol(mnr);
break;
}
dp->dp_asynctask++;
if (thr_create(0, VOL_STKSIZE,
(void *(*)(void *))vol_insert, (void *)vid,
dp->dp_asynctask--;
} else {
debug(6,
"vol_event: created vol_insert() tid %d (%d,%d) (for CLOSE)\n",
}
v->v_flags &= ~V_NEWLABEL;
}
break;
}
case VIE_CANCEL:
mnr);
break;
}
/* if we have a device then clean up after it */
if (v->v_confirmed == FALSE) {
vol_forceout(v);
}
#ifdef IT_ALL_WORKED
/*
* it'd be nice to "unmount" (i.e. run rmmount, the
* std eject-action handler), but when vol_reaper()
* calls dev_eject(), which calls dev_getdp(), the
* assertion that dev != NODEV in dev_getdp() pukes!
* so, for now, if the user cancels i/o (via volcancel),
* they leave the vol mounted! -- wld
*/
/* now try to run the eject action */
if (v->v_ej_inprog) {
/* ejection in progress... ignore */
break;
}
v->v_ej_inprog = TRUE;
#endif /* IT_ALL_WORKED */
break;
case VIE_NEWLABEL:
/*
* The old code seemed to interact with VIE_CLOSE
* and VIE_REMOUNT in a way that unmapped the device
* and caused VIE_REMOUNT to fail. Trying to elminate
* that failure by not setting the V_NEWLABEL flag
* in the volume object. I don't know whether that
* will work, as the old code for relabeling media
* is convoluted, but I'll give it a try.
* Henry Knapp 991015
*/
break;
case VIE_GETATTR: {
char *value;
char *props;
struct vioc_dattr vda;
mnr);
break;
}
} else {
}
}
break;
}
case VIE_SETATTR: {
char *props;
char *nprops;
struct vioc_dattr vda;
mnr);
break;
}
/* this will free "props" */
}
change_flags((obj_t *)v);
} else {
}
break;
}
case VIE_REMOVED:
vol_forceout(v); /* get rid of that baby */
}
break;
case VIE_REMOUNT:
v = minor_getvol(mnr);
if (v != NULL) {
result = dev_remount(v);
} else {
}
break;
}
*in_devicep = v->v_device;
dp->dp_asynctask++;
/*
* If the node is a "nomedia" node, then
* remove it.
*/
}
}
if (thr_create(0, VOL_STKSIZE,
(void *(*)(void *))remount_medium,
" tid %d (%d,%d) (VIE_REMOUNT)\n",
} else {
"VIE_REMOUNT:can't create thread; %m\n"));
dp->dp_asynctask--;
}
}
break;
default:
break;
}
}
/*
* Media has been removed from the drive. Do all the required
* cleanup.
*/
static void
vol_forceout(vol_t *v)
{
if (v->v_confirmed == FALSE) {
return;
}
if (v->v_ej_inprog != FALSE) {
}
v->v_ej_force = TRUE;
v->v_ej_inprog = TRUE;
}
static void
{
vol_t *v;
if (v == NULL) {
return;
}
/* it's no longer missing */
return;
}
/* ctl volume never missing. cancel it */
return;
}
/* okay, it's really missing -- create a notify event */
(void) action(ACT_NOTIFY, v);
}
static void
{
struct async_task *as;
in_device = *in_devicep;
if (medium_result == MEDIUM_SUCCESS) {
if (remount)
else
}
}
static void
{
}
static void
{
}
/*
* Something wonderful has just happened to vid->vi_dev. Either
* a new piece of media was inserted into the drive (act == INSERT),
* someone just wrote a new label over the old one (act == NEWLABEL),
* or we are being asked to read the label to confirm what was
* believed to be in the drive (act == CONFIRM).
*/
static void
{
#ifdef TWO_SIDED_DEVICE
#endif
}
#ifdef TWO_SIDED_DEVICE
/* if it's a two sided device, we only have one poller per slot */
}
#endif
}
(void) mutex_lock(&alab_mutex);
}
#ifdef TWO_SIDED_DEVICE
}
#endif
(void) mutex_unlock(&alab_mutex);
(void) mutex_lock(&vold_main_mutex);
/* we only want to send the signal while he's in a poll */
if (alab_work) {
}
(void) mutex_unlock(&vold_main_mutex);
/* this thread is all done */
}
static struct alab *
{
struct devs *);
enum laread_res res;
int fd;
/*
* have the driver specific code tell us how to get to the
* raw device.
*/
return (NULL);
}
return (NULL);
}
/*
* walk through the label types, trying to read the labels.
*/
/*
* If the label routines couldn't read the device,
* get it outta here!
*/
return (NULL);
}
return (al);
}
static vol_t *
{
const char *laread_res_to_str(enum laread_res);
static const char *read_type_to_str(enum read_type);
vol_t *v;
int nacts = 0;
/*
* go look for it in the namespace, etc. If it isn't there,
* build us a new one.
*/
switch (rres) {
default:
/*
* just drop thru here -- dev_unlabeled will take
* care of it for us.
*/
case L_UNFORMATTED:
case L_NOTUNIQUE:
case L_UNRECOG:
break;
case L_ERROR:
case L_FOUND:
debug(3,
"vol_foundlabel: node_findlabel() failed: ejecting\n");
return (NULL);
}
break;
}
dev_hangvol(dp, v);
v->v_confirmed = TRUE;
/*
* if it's not an unlabeled thing, write changes
* back to the database.
*/
}
v->v_checkresp = TRUE;
}
}
/* see if the s-enxio property needs to be cleared for this volume */
uint_t i;
/* clear s-enxio for each device on this volume */
for (i = 0; i < v->v_ndev; i++) {
struct vioc_flags vfl;
/*
* use the VOLIOCFLAGS ioctl to tell the driver to
* quit handling enxio
*/
debug(1,
"vol_foundlabel: calling VOLIOCFLAGS(0), unit %d\n",
}
}
/* clear the volume's s-enxio flag */
}
/* if someone is waiting on the check ioctl, wake them up. */
}
return (v);
}
static void
{
int nacts = 0;
struct reap *r;
/*
* device is being closed or ejected, or might be
* being mounted, but we got another mount request
* because volume was missing and missing voluem has
* been inserted.
* We just ignore this request so that we don't
* bother the ongoing tasks.
*/
if (dp->dp_checkresp &&
/*
* respond to the check request which
* was made after the volume was missing.
* If the running action was either eject
* or close, we don't need to wait those
* to be complete, but respond immediately.
*/
}
return;
}
}
v->v_checkresp = TRUE;
}
/*
* see if the s-enxio property needs to be cleared for
* this volume
*
* QUESTION: What is the s-enxio property?
*/
uint_t i;
/* clear s-enxio for each device on this volume */
for (i = 0; i < v->v_ndev; i++) {
struct vioc_flags vfl;
/*
* use the VOLIOCFLAGS ioctl to tell the driver to
* quit handling enxio
*/
debug(1,
"mount_volume: calling VOLIOCFLAGS(0), unit %d\n",
}
}
/* clear the volume's s-enxio flag */
}
/*
* If a device thread is waiting for an ioctl(), send one.
*/
}
}
const char *
{
static char res_buf[10];
switch (rres) {
case L_UNRECOG:
res = "L_UNRECOG";
break;
case L_UNFORMATTED:
res = "L_UNFORMATTED";
break;
case L_NOTUNIQUE:
res = "L_NOTUNIQUE";
break;
case L_ERROR:
res = "L_ERROR";
break;
case L_FOUND:
res = "L_FOUND";
break;
default:
break;
}
return (res);
}
static const char
{
static char res_buf[10];
switch (act) {
case INSERT:
res = "INSERT";
break;
case NEWLABEL:
res = "NEWLABEL";
break;
case CONFIRM:
res = "CONFIRM";
break;
default:
break;
}
return (res);
}
static void
{
/*
* unlabeled -> labeled
*/
if (v->v_ej_inprog) {
debug(5,
"vol_newlabel: clearing devmap using devmapfree\n");
(void) dev_devmapfree(v);
} else {
}
/* unhang the old unlabeled stuff */
/* v is gone now */
/* recognize it in the cannonical way */
/* must clear "cancel" flag on vol before unmapping */
dev_devmap(nv);
}
}
return;
}
/*
* labeled -> labeled
*/
/* just rewrote the label on a normal name */
}
return;
}
/*
* labeled -> unlabeled
*/
/* out with the old */
(void) dev_devmapfree(v);
if (v->v_ej_inprog) {
v->v_devmap = 0;
} else {
}
/* v is gone now */
/* in with the new */
if (doej) {
/*
* If we need to eject him, copy the
* eject information.
*/
}
}
/*
* must map device in case someboy is in a hurry to
* access it
*/
dev_devmap(nv);
return;
}
/*
* unlabeled -> unlabeled
*/
}
return;
}
/* shouldn't reach here */
ASSERT(0);
}
static void
dispatch_alab(void)
{
/*
* Results from async label reads.
*/
(void) mutex_lock(&alab_mutex);
case INSERT:
break;
case NEWLABEL:
break;
case REMOUNT:
break;
default:
}
/* wait for the thread */
}
}
}
(void) mutex_unlock(&alab_mutex);
}
static int
{
return (1);
case VIE_INSERT:
/*
* The intent of the logic below is that we don't want to
* block the insert when volume is missing. Otherwise,
* new volume won't be created, and the volume is missing
* forever.
*/
if (dp->dp_asynctask != 0) {
struct reap *r;
/*
* this device is being read and the volume
* is being created. not safe.
*/
return (0);
}
/*
* not scanning the medium, but volume has gone.
* That means, either device is missing, or brand
* new medium was inserted.
*/
return (1);
/*
* check the async tasks see if any references
* to the volume.
*/
/*
* missing volume. It can go through.
*/
return (1);
}
}
return (0);
}
break;
case VIE_REMOUNT:
case VIE_EJECT:
/*
* if nothing is working, then it's safe.
*/
if (dp->dp_asynctask != 0)
return (0);
break;
}
return (1);
}
static void
dispatch_async_task(void)
{
int start_over;
(void) mutex_lock(&async_taskq_mutex);
start_over = 0;
case ASACT_REAPTHR: {
if (--dp->dp_asynctask == 0)
start_over = 1;
}
}
break;
}
case ASACT_NEWEVENT: {
struct vioc_event *vie;
/*
* leave the async task int the queue and
* and go to the next event.
*/
continue;
}
break;
}
case ASACT_MOUNT:
case ASACT_REMOUNT: {
vol_t *v;
int act;
break;
}
case ASACT_DEV_CLOSE: {
break;
}
default:
break;
}
if (start_over)
goto restart;
}
(void) mutex_unlock(&async_taskq_mutex);
}
void
{
(void) mutex_lock(&async_taskq_mutex);
vold_run_run();
(void) mutex_unlock(&async_taskq_mutex);
}
/*
* remove the NEWEVENT by given devicep from async taskq.
*/
void
{
struct vioc_event *vie;
return;
(void) mutex_lock(&async_taskq_mutex);
continue;
continue;
}
(void) mutex_unlock(&async_taskq_mutex);
}
/*
* check to see if we already have REMOUNT event queued in the
* async taskq. If so, don't queue.
*/
static int
{
struct vioc_event *vie;
return (0);
(void) mutex_lock(&async_taskq_mutex);
continue;
continue;
break;
}
(void) mutex_unlock(&async_taskq_mutex);
}
static void
reap_child(void)
{
if (vol_reaper(r) != 1)
continue;
if (r->r_act != ACT_NOTIFY) {
/*
* we need r_dev here, because r_v may be
* replaced by an insertion event when volume
* was missing. Therefore, v_basedev could point
* different drive.
*/
if (--dp->dp_asynctask == 0)
vold_run_run();
}
}
/*
* child has been dead. respond to the check,
* if it's pending. We don't do it for eject, close
* or notify, because volume may have been released or
* replaced(notify) at this point. And actually volcheck
*/
}
}
/* free this thing */
free(r);
}
}
/*
* called from main vold loop to handle asynchronous events:
* - async label reads
* - reap process status (e.g. from rmmount)
* return TRUE if action is in progress.
*/
int
vol_async(void)
{
int ret;
/*
* Results from async label reads.
*/
if (alab_work) {
}
if (async_taskq_run) {
}
/*
* Results from action processes (eject specifically)
*/
reap_child();
}
return (ret);
}
static void
{
switch (r->r_act) {
case ACT_EJECT:
break;
case ACT_CLOSE:
break;
}
}
static int
vol_reaper(struct reap *r)
{
int stat;
if (r->r_pid == 0 &&
/* had an internal error -- ejecting */
eject_or_close(r, TRUE);
return (1);
}
if (r->r_pid == -1 &&
/* unsafe -- not ejecting */
"volume %s has file system mounted, "
"cannot eject\n"),
eject_or_close(r, FALSE);
}
return (1);
}
/* process is still working */
return (0);
}
if (err == -1) {
return (0);
}
} else if (WIFSIGNALED(stat)) {
debug(4,
"status for pid %d: signaled with '%s' and dumped core\n",
} else {
debug(4,
"status for pid %d: signaled with '%s'\n",
}
} else {
}
/*
* If the process was an eject action and it exited normally,
* we take this path.
*/
/*
* volume may have been released while ejecting
* the medium.
*/
return (1);
}
if (WEXITSTATUS(stat) == 0) {
/*
* don't eject until all the actions are done.
*/
eject_or_close(r, TRUE);
}
return (1);
}
/*
* If we get a no, say that we are done and
* fail the eject.
*/
eject_or_close(r, FALSE);
return (1);
}
debug(1,
gettext("eject action %s retns exit code %d, not ejecting\n"),
eject_or_close(r, FALSE);
return (1);
}
if (WIFSIGNALED(stat)) {
/*
* If the process got its brains blown out,
* don't treat that as a denial, just continue
* along.
*/
eject_or_close(r, TRUE);
}
return (1);
}
}
return (1);
}
return (0);
}
vol_t *
{
vol_t *v;
return (v);
}
void
{
return;
(void) dev_devmapfree(volumep);
}