FreeBSD-5.3/sys/dev/vinum/vinumioctl.c

/*
 * XXX replace all the checks on object validity with
 * calls to valid<object>
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD: src/sys/dev/vinum/vinumioctl.c,v 1.51 2004/07/10 21:17:04 marcel Exp $");
/*-
 * Copyright (c) 1997, 1998, 1999
 *	Nan Yang Computer Services Limited.  All rights reserved.
 *
 *  Parts copyright (c) 1997, 1998 Cybernet Corporation, NetMAX project.
 *
 *  Written by Greg Lehey
 *
 *  This software is distributed under the so-called ``Berkeley
 *  License'':
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Nan Yang Computer
 *      Services Limited.
 * 4. Neither the name of the Company nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * This software is provided ``as is'', and any express or implied
 * warranties, including, but not limited to, the implied warranties of
 * merchantability and fitness for a particular purpose are disclaimed.
 * In no event shall the company or contributors be liable for any
 * direct, indirect, incidental, special, exemplary, or consequential
 * damages (including, but not limited to, procurement of substitute
 * goods or services; loss of use, data, or profits; or business
 * interruption) however caused and on any theory of liability, whether
 * in contract, strict liability, or tort (including negligence or
 * otherwise) arising in any way out of the use of this software, even if
 * advised of the possibility of such damage.
 *
 * $Id: vinumioctl.c,v 1.23 2003/05/23 01:02:22 grog Exp grog $
 */

#include <dev/vinum/vinumhdr.h>
#include <dev/vinum/request.h>

#ifdef VINUMDEBUG
#include <sys/reboot.h>
#endif

void attachobject(struct vinum_ioctl_msg *);
void detachobject(struct vinum_ioctl_msg *);
void renameobject(struct vinum_rename_msg *);
void replaceobject(struct vinum_ioctl_msg *);
void moveobject(struct vinum_ioctl_msg *);
void setreadpol(struct vinum_ioctl_msg *);

jmp_buf command_fail;					    /* return on a failed command */

/* ioctl routine */
int
vinumioctl(struct cdev *dev,
    u_long cmd,
    caddr_t data,
    int flag,
    struct thread *td)
{
    unsigned int objno;
    struct sd *sd;
    struct plex *plex;
    struct volume *vol;

    /* First, decide what we're looking at */
    if ((minor(dev) == VINUM_SUPERDEV_MINOR)
	|| (minor(dev) == VINUM_DAEMON_MINOR))
	return vinum_super_ioctl(dev, cmd, data);
    else						    /* real device */
	switch (DEVTYPE(dev)) {
	case VINUM_SD_TYPE:
	case VINUM_SD2_TYPE:				    /* second half of sd namespace */
	    objno = Sdno(dev);

	    sd = &SD[objno];

	    switch (cmd) {
	    case DIOCGSECTORSIZE:
		*(u_int *) data = sd->sectorsize;
		return 0;

	    case DIOCGMEDIASIZE:
		*(u_int64_t *) data = sd->sectors * sd->sectorsize;
		return 0;

		/*
		 * We don't have this stuff on hardware,
		 * so just pretend to do it so that
		 * utilities don't get upset.
		 */
	    case DIOCWDINFO:				    /* write partition info */
	    case DIOCSDINFO:				    /* set partition info */
		return 0;				    /* not a titty */

	    default:
		return ENOTTY;				    /* not my kind of ioctl */
	    }

	    return 0;					    /* pretend we did it */

	case VINUM_PLEX_TYPE:
	    objno = Plexno(dev);

	    plex = &PLEX[objno];

	    switch (cmd) {
	    case DIOCGSECTORSIZE:
		*(u_int64_t *) data = plex->sectorsize;
		return 0;

	    case DIOCGMEDIASIZE:
		*(u_int64_t *) data = plex->length * plex->sectorsize;
		return 0;

		/*
		 * We don't have this stuff on hardware,
		 * so just pretend to do it so that
		 * utilities don't get upset.
		 */
	    case DIOCWDINFO:				    /* write partition info */
	    case DIOCSDINFO:				    /* set partition info */
		return 0;				    /* not a titty */

	    default:
		return ENOTTY;				    /* not my kind of ioctl */
	    }

	    return 0;					    /* pretend we did it */

	case VINUM_VOLUME_TYPE:
	    objno = Volno(dev);

	    if ((unsigned) objno >= (unsigned) vinum_conf.volumes_allocated) /* not a valid volume */
		return ENXIO;
	    vol = &VOL[objno];
	    if (vol->state != volume_up)		    /* not up, */
		return EIO;				    /* I/O error */

	    switch (cmd) {
	    case DIOCGSECTORSIZE:
		*(u_int *) data = vol->sectorsize;
		return 0;

	    case DIOCGMEDIASIZE:
		*(u_int64_t *) data = vol->size * vol->sectorsize;
		return 0;

		/*
		 * We don't have this stuff on hardware,
		 * so just pretend to do it so that
		 * utilities don't get upset.
		 */
	    case DIOCWDINFO:				    /* write partition info */
	    case DIOCSDINFO:				    /* set partition info */
		return 0;				    /* not a titty */

	    default:
		return ENOTTY;				    /* not my kind of ioctl */
	    }
	    break;
	}
    return 0;						    /* XXX */
}

/* Handle ioctls for the super device */
int
vinum_super_ioctl(struct cdev *dev,
    u_long cmd,
    caddr_t data)
{
    int error = 0;
    unsigned int index;					    /* for transferring config info */
    unsigned int sdno;					    /* for transferring config info */
    int fe;						    /* free list element number */
    struct _ioctl_reply *ioctl_reply = (struct _ioctl_reply *) data; /* struct to return */

    ioctl_reply = (struct _ioctl_reply *) data;		    /* save the address to reply to */
    if (error)						    /* bombed out */
	return 0;					    /* the reply will contain meaningful info */
    switch (cmd) {
#ifdef VINUMDEBUG
    case VINUM_DEBUG:
	if (((struct debuginfo *) data)->changeit)	    /* change debug settings */
	    debug = (((struct debuginfo *) data)->param);
	else {
	    if (debug & DEBUG_REMOTEGDB)
		boothowto |= RB_GDB;			    /* serial debug line */
	    else
		boothowto &= ~RB_GDB;			    /* local ddb */
	    kdb_enter("vinum debug");
	}
	ioctl_reply = (struct _ioctl_reply *) data;	    /* reinstate the address to reply to */
	ioctl_reply->error = 0;
	return 0;
#endif

    case VINUM_CREATE:					    /* create a vinum object */
	error = lock_config();				    /* get the config for us alone */
	if (error)					    /* can't do it, */
	    return error;				    /* give up */
	error = setjmp(command_fail);			    /* come back here on error */
	if (error == 0)					    /* first time, */
	    ioctl_reply->error = parse_user_config((char *) data, /* update the config */
		&keyword_set);
	else if (ioctl_reply->error == 0) {		    /* longjmp, but no error status */
	    ioctl_reply->error = EINVAL;		    /* note that something's up */
	    ioctl_reply->msg[0] = '\0';			    /* no message? */
	}
	unlock_config();
	return 0;					    /* must be 0 to return the real error info */

    case VINUM_GETCONFIG:				    /* get the configuration information */
	bcopy(&vinum_conf, data, sizeof(vinum_conf));
	return 0;

	/* start configuring the subsystem */
    case VINUM_STARTCONFIG:
	return start_config(*(int *) data);		    /* just lock it.  Parameter is 'force' */

	/*
	 * Move the individual parts of the config to user space.
	 *
	 * Specify the index of the object in the first word of data,
	 * and return the object there
	 */
    case VINUM_DRIVECONFIG:
	index = *(int *) data;				    /* get the index */
	if (index >= (unsigned) vinum_conf.drives_allocated) /* can't do it */
	    return ENXIO;				    /* bang */
	bcopy(&DRIVE[index], data, sizeof(struct _drive));  /* copy the config item out */
	return 0;

    case VINUM_SDCONFIG:
	index = *(int *) data;				    /* get the index */
	if (index >= (unsigned) vinum_conf.subdisks_allocated) /* can't do it */
	    return ENXIO;				    /* bang */
	bcopy(&SD[index], data, sizeof(struct _sd));	    /* copy the config item out */
	return 0;

    case VINUM_PLEXCONFIG:
	index = *(int *) data;				    /* get the index */
	if (index >= (unsigned) vinum_conf.plexes_allocated) /* can't do it */
	    return ENXIO;				    /* bang */
	bcopy(&PLEX[index], data, sizeof(struct _plex));    /* copy the config item out */
	return 0;

    case VINUM_VOLCONFIG:
	index = *(int *) data;				    /* get the index */
	if (index >= (unsigned) vinum_conf.volumes_allocated) /* can't do it */
	    return ENXIO;				    /* bang */
	bcopy(&VOL[index], data, sizeof(struct _volume));   /* copy the config item out */
	return 0;

    case VINUM_PLEXSDCONFIG:
	index = *(int *) data;				    /* get the plex index */
	sdno = ((int *) data)[1];			    /* and the sd index */
	if ((index >= (unsigned) vinum_conf.plexes_allocated) /* plex doesn't exist */
	||(sdno >= PLEX[index].subdisks))		    /* or it doesn't have this many subdisks */
	    return ENXIO;				    /* bang */
	bcopy(&SD[PLEX[index].sdnos[sdno]],		    /* copy the config item out */
	    data,
	    sizeof(struct _sd));
	return 0;

	/*
	 * We get called in two places: one from the
	 * userland config routines, which call us
	 * to complete the config and save it.  This
	 * call supplies the value 0 as a parameter.
	 *
	 * The other place is from the user "saveconfig"
	 * routine, which can only work if we're *not*
	 * configuring.  In this case, supply parameter 1.
	 */
    case VINUM_SAVECONFIG:
	if (VFLAGS & VF_CONFIGURING) {			    /* must be us, the others are asleep */
	    if (*(int *) data == 0)			    /* finish config */
		finish_config(1);			    /* finish the configuration and update it */
	    else
		return EBUSY;				    /* can't do it now */
	}
	save_config();					    /* save configuration to disk */
	return 0;

    case VINUM_RELEASECONFIG:				    /* release the config */
	if (VFLAGS & VF_CONFIGURING) {			    /* must be us, the others are asleep */
	    finish_config(0);				    /* finish the configuration, don't change it */
	    save_config();				    /* save configuration to disk */
	} else
	    error = EINVAL;				    /* release what config? */
	return error;

    case VINUM_READCONFIG:
	if (((char *) data)[0] == '\0')
	    ioctl_reply->error = vinum_scandisk(NULL);	    /* built your own list */
	else
	    ioctl_reply->error = vinum_scandisk((char *) data);
	if (ioctl_reply->error == ENOENT) {
	    if (vinum_conf.drives_used > 0)
		strcpy(ioctl_reply->msg, "no additional drives found");
	    else
		strcpy(ioctl_reply->msg, "no drives found");
	} else if (ioctl_reply->error)
	    strcpy(ioctl_reply->msg, "can't read configuration information, see log file");
	return 0;					    /* must be 0 to return the real error info */

    case VINUM_INIT:
	ioctl_reply = (struct _ioctl_reply *) data;	    /* reinstate the address to reply to */
	ioctl_reply->error = 0;
	return 0;

    case VINUM_RESETCONFIG:
	if (vinum_inactive(0)) {			    /* if the volumes are not active */
	    /*
	     * Note the open count.  We may be called from v, so we'll be open.
	     * Keep the count so we don't underflow
	     */
	    free_vinum(1);				    /* clean up everything */
	    log(LOG_NOTICE, "vinum: CONFIGURATION OBLITERATED\n");
	    ioctl_reply = (struct _ioctl_reply *) data;	    /* reinstate the address to reply to */
	    ioctl_reply->error = 0;
	    return 0;
	}
	return EBUSY;

    case VINUM_SETSTATE:
	setstate((struct vinum_ioctl_msg *) data);	    /* set an object state */
	return 0;

	/*
	 * Set state by force, without changing
	 * anything else.
	 */
    case VINUM_SETSTATE_FORCE:
	setstate_by_force((struct vinum_ioctl_msg *) data); /* set an object state */
	return 0;

#ifdef VINUMDEBUG
    case VINUM_MEMINFO:
	vinum_meminfo(data);
	return 0;

    case VINUM_MALLOCINFO:
	return vinum_mallocinfo(data);

    case VINUM_RQINFO:
	return vinum_rqinfo(data);
#endif

    case VINUM_REMOVE:
	remove((struct vinum_ioctl_msg *) data);	    /* remove an object */
	return 0;

    case VINUM_GETFREELIST:				    /* get a drive free list element */
	index = *(int *) data;				    /* get the drive index */
	fe = ((int *) data)[1];				    /* and the free list element */
	if ((index >= (unsigned) vinum_conf.drives_allocated) /* plex doesn't exist */
	||(DRIVE[index].state == drive_unallocated))
	    return ENODEV;
	if (fe >= DRIVE[index].freelist_entries)	    /* no such entry */
	    return ENOENT;
	bcopy(&DRIVE[index].freelist[fe],
	    data,
	    sizeof(struct drive_freelist));
	return 0;

    case VINUM_RESETSTATS:
	resetstats((struct vinum_ioctl_msg *) data);	    /* reset object stats */
	return 0;

	/* attach an object to a superordinate object */
    case VINUM_ATTACH:
	attachobject((struct vinum_ioctl_msg *) data);
	return 0;

	/* detach an object from a superordinate object */
    case VINUM_DETACH:
	detachobject((struct vinum_ioctl_msg *) data);
	return 0;

	/* rename an object */
    case VINUM_RENAME:
	renameobject((struct vinum_rename_msg *) data);
	return 0;

	/* replace an object */
    case VINUM_REPLACE:
	replaceobject((struct vinum_ioctl_msg *) data);
	return 0;

    case VINUM_DAEMON:
	vinum_daemon();					    /* perform the daemon */
	return 0;

    case VINUM_FINDDAEMON:				    /* check for presence of daemon */
	return vinum_finddaemon();
	return 0;

    case VINUM_SETDAEMON:				    /* set daemon flags */
	return vinum_setdaemonopts(*(int *) data);

    case VINUM_GETDAEMON:				    /* get daemon flags */
	*(int *) data = daemon_options;
	return 0;

    case VINUM_PARITYOP:				    /* check/rebuild RAID-4/5 parity */
	parityops((struct vinum_ioctl_msg *) data);
	return 0;

	/* move an object */
    case VINUM_MOVE:
	moveobject((struct vinum_ioctl_msg *) data);
	return 0;

    case VINUM_READPOL:
	setreadpol((struct vinum_ioctl_msg *) data);
	return 0;

    default:
	/* FALLTHROUGH */
	break;
    }
    return 0;						    /* to keep the compiler happy */
}

/*
 * The following four functions check the supplied
 * object index and return a pointer to the object
 * if it exists.  Otherwise they longjump out via
 * throw_rude_remark.
 */
struct drive *
validdrive(int driveno, struct _ioctl_reply *reply)
{
    if ((driveno < vinum_conf.drives_allocated)
	&& (DRIVE[driveno].state > drive_referenced))
	return &DRIVE[driveno];
    strcpy(reply->msg, "No such drive");
    reply->error = ENOENT;
    return NULL;
}

struct sd *
validsd(int sdno, struct _ioctl_reply *reply)
{
    if ((sdno < vinum_conf.subdisks_allocated)
	&& (SD[sdno].state > sd_referenced))
	return &SD[sdno];
    strcpy(reply->msg, "No such subdisk");
    reply->error = ENOENT;
    return NULL;
}

struct plex *
validplex(int plexno, struct _ioctl_reply *reply)
{
    if ((plexno < vinum_conf.plexes_allocated)
	&& (PLEX[plexno].state > plex_referenced))
	return &PLEX[plexno];
    strcpy(reply->msg, "No such plex");
    reply->error = ENOENT;
    return NULL;
}

struct volume *
validvol(int volno, struct _ioctl_reply *reply)
{
    if ((volno < vinum_conf.volumes_allocated)
	&& (VOL[volno].state > volume_uninit))
	return &VOL[volno];
    strcpy(reply->msg, "No such volume");
    reply->error = ENOENT;
    return NULL;
}

/* reset an object's stats */
void
resetstats(struct vinum_ioctl_msg *msg)
{
    struct _ioctl_reply *reply = (struct _ioctl_reply *) msg;

    switch (msg->type) {
    case drive_object:
	if (msg->index < vinum_conf.drives_allocated) {
	    struct drive *drive = &DRIVE[msg->index];
	    if (drive->state > drive_referenced) {
		drive->reads = 0;			    /* number of reads on this drive */
		drive->writes = 0;			    /* number of writes on this drive */
		drive->bytes_read = 0;			    /* number of bytes read */
		drive->bytes_written = 0;		    /* number of bytes written */
		reply->error = 0;
		return;
	    }
	    reply->error = EINVAL;
	    return;
	}
    case sd_object:
	if (msg->index < vinum_conf.subdisks_allocated) {
	    struct sd *sd = &SD[msg->index];
	    if (sd->state > sd_referenced) {
		sd->reads = 0;				    /* number of reads on this subdisk */
		sd->writes = 0;				    /* number of writes on this subdisk */
		sd->bytes_read = 0;			    /* number of bytes read */
		sd->bytes_written = 0;			    /* number of bytes written */
		reply->error = 0;
		return;
	    }
	    reply->error = EINVAL;
	    return;
	}
	break;

    case plex_object:
	if (msg->index < vinum_conf.plexes_allocated) {
	    struct plex *plex = &PLEX[msg->index];
	    if (plex->state > plex_referenced) {
		plex->reads = 0;
		plex->writes = 0;			    /* number of writes on this plex */
		plex->bytes_read = 0;			    /* number of bytes read */
		plex->bytes_written = 0;		    /* number of bytes written */
		plex->recovered_reads = 0;		    /* number of recovered read operations */
		plex->degraded_writes = 0;		    /* number of degraded writes */
		plex->parityless_writes = 0;		    /* number of parityless writes */
		plex->multiblock = 0;			    /* requests that needed more than one block */
		plex->multistripe = 0;			    /* requests that needed more than one stripe */
		reply->error = 0;
		return;
	    }
	    reply->error = EINVAL;
	    return;
	}
	break;

    case volume_object:
	if (msg->index < vinum_conf.volumes_allocated) {
	    struct volume *vol = &VOL[msg->index];
	    if (vol->state > volume_uninit) {
		vol->bytes_read = 0;			    /* number of bytes read */
		vol->bytes_written = 0;			    /* number of bytes written */
		vol->reads = 0;				    /* number of reads on this volume */
		vol->writes = 0;			    /* number of writes on this volume */
		vol->recovered_reads = 0;		    /* reads recovered from another plex */
		reply->error = 0;
		return;
	    }
	    reply->error = EINVAL;
	    return;
	}
    case invalid_object:				    /* can't get this */
	reply->error = EINVAL;
	return;
    }
}

/* attach an object to a superior object */
void
attachobject(struct vinum_ioctl_msg *msg)
{
    struct _ioctl_reply *reply = (struct _ioctl_reply *) msg;
    int sdno;
    struct sd *sd;
    struct plex *plex;
    struct volume *vol;

    switch (msg->type) {
    case drive_object:					    /* you can't attach a drive to anything */
    case volume_object:					    /* nor a volume */
    case invalid_object:				    /* "this can't happen" */
	reply->error = EINVAL;
	reply->msg[0] = '\0';				    /* vinum(8) doesn't do this */
	return;

    case sd_object:
	sd = validsd(msg->index, reply);
	if (sd == NULL)					    /* not a valid subdisk  */
	    return;
	plex = validplex(msg->otherobject, reply);
	if (plex) {
	    /*
	     * We should be more intelligent about this.
	     * We should be able to reattach a dead
	     * subdisk, but if we want to increase the total
	     * number of subdisks, we have a lot of reshuffling
	     * to do. XXX
	     */
	    if ((plex->organization != plex_concat)	    /* can't attach to striped and RAID-4/5 */
	    &&(!msg->force)) {				    /* without using force */
		reply->error = EINVAL;			    /* no message, the user should check */
		strcpy(reply->msg, "Can't attach to this plex organization");
	    } else if (sd->plexno >= 0) {		    /* already belong to a plex */
		reply->error = EBUSY;			    /* no message, the user should check */
		sprintf(reply->msg, "%s is already attached to %s",
		    sd->name,
		    sd[sd->plexno].name);
		reply->msg[0] = '\0';
	    } else {
		sd->plexoffset = msg->offset;		    /* this is where we want it */
		set_sd_state(sd->sdno, sd_stale, setstate_force); /* make sure it's stale */
		give_sd_to_plex(plex->plexno, sd->sdno);    /* and give it to the plex */
		update_sd_config(sd->sdno, 0);
		save_config();
		if (sd->state == sd_reviving)
		    reply->error = EAGAIN;		    /* need to revive it */
		else
		    reply->error = 0;
	    }
	}
	break;

    case plex_object:
	plex = validplex(msg->index, reply);		    /* get plex */
	if (plex == NULL)
	    return;
	vol = validvol(msg->otherobject, reply);	    /* and volume information */
	if (vol) {
	    if (vol->plexes == MAXPLEX) {		    /* we have too many already */
		reply->error = ENOSPC;			    /* nowhere to put it */
		strcpy(reply->msg, "Too many plexes");
	    } else if (plex->volno >= 0) {		    /* the plex has an owner */
		reply->error = EBUSY;			    /* no message, the user should check */
		sprintf(reply->msg, "%s is already attached to %s",
		    plex->name,
		    VOL[plex->volno].name);
	    } else {
		for (sdno = 0; sdno < plex->subdisks; sdno++) {
		    sd = &SD[plex->sdnos[sdno]];

		    if (sd->state > sd_down)		    /* real subdisk, vaguely accessible */
			set_sd_state(plex->sdnos[sdno], sd_stale, setstate_force); /* make it stale */
		}
		set_plex_state(plex->plexno, plex_up, setstate_none); /* update plex state */
		give_plex_to_volume(msg->otherobject, msg->index, 0); /* and give it to the volume */
		update_plex_config(plex->plexno, 0);
		save_config();
		reply->error = 0;			    /* all went well */
	    }
	}
    }
}

/* detach an object from a superior object */
void
detachobject(struct vinum_ioctl_msg *msg)
{
    struct _ioctl_reply *reply = (struct _ioctl_reply *) msg;
    struct sd *sd;
    struct plex *plex;
    struct volume *vol;
    int sdno;
    int plexno;

    switch (msg->type) {
    case drive_object:					    /* you can't detach a drive from anything */
    case volume_object:					    /* nor a volume */
    case invalid_object:				    /* "this can't happen" */
	reply->error = EINVAL;
	reply->msg[0] = '\0';				    /* vinum(8) doesn't do this */
	return;

    case sd_object:
	sd = validsd(msg->index, reply);
	if (sd == NULL)
	    return;
	if (sd->plexno < 0) {				    /* doesn't belong to a plex */
	    reply->error = ENOENT;
	    strcpy(reply->msg, "Subdisk is not attached");
	    return;
	} else {					    /* valid plex number */
	    plex = &PLEX[sd->plexno];
	    if ((!msg->force)				    /* don't force things */
	    &&((plex->state == plex_up)			    /* and the plex is up */
	    ||((plex->state == plex_flaky) && sd->state == sd_up))) { /* or flaky with this sd up */
		reply->error = EBUSY;			    /* we need this sd */
		reply->msg[0] = '\0';
		return;
	    }
	    sd->plexno = -1;				    /* anonymous sd */
	    if (plex->subdisks == 1) {			    /* this was the only subdisk */
		Free(plex->sdnos);			    /* free the subdisk array */
		plex->sdnos = NULL;			    /* and note the fact */
		plex->subdisks_allocated = 0;		    /* no subdisk space */
	    } else {
		for (sdno = 0; sdno < plex->subdisks; sdno++) {
		    if (plex->sdnos[sdno] == msg->index)    /* found our subdisk */
			break;
		}
		if (sdno < (plex->subdisks - 1))	    /* not the last one, compact */
		    bcopy(&plex->sdnos[sdno + 1],
			&plex->sdnos[sdno],
			(plex->subdisks - 1 - sdno) * sizeof(int));
	    }
	    plex->subdisks--;
	    if (!bcmp(plex->name, sd->name, strlen(plex->name) + 1))
		/* this subdisk is named after the plex */
	    {
		bcopy(sd->name,
		    &sd->name[3],
		    min(strlen(sd->name) + 1, MAXSDNAME - 3));
		bcopy("ex-", sd->name, 3);
		sd->name[MAXSDNAME - 1] = '\0';
	    }
	    update_plex_config(plex->plexno, 0);
	    if (isstriped(plex))			    /* we've just mutilated our plex, */
		set_plex_state(plex->plexno,
		    plex_down,
		    setstate_force | setstate_configuring);
	    if (plex->volno >= 0)			    /* plex attached to volume, */
		update_volume_config(plex->volno);
	    save_config();
	    reply->error = 0;
	}
	return;

    case plex_object:
	plex = validplex(msg->index, reply);		    /* get plex */
	if (plex == NULL)
	    return;
	if (plex->volno >= 0) {
	    int volno = plex->volno;

	    vol = &VOL[volno];
	    if ((!msg->force)				    /* don't force things */
	    &&((vol->state == volume_up)		    /* and the volume is up */
	    &&(vol->plexes == 1))) {			    /* and this is the last plex */
		/*
		   * XXX As elsewhere, check whether we will lose
		   * mapping by removing this plex
		 */
		reply->error = EBUSY;			    /* we need this plex */
		reply->msg[0] = '\0';
		return;
	    }
	    plex->volno = -1;				    /* anonymous plex */
	    for (plexno = 0; plexno < vol->plexes; plexno++) {
		if (vol->plex[plexno] == msg->index)	    /* found our plex */
		    break;
	    }
	    if (plexno < (vol->plexes - 1))		    /* not the last one, compact */
		bcopy(&vol->plex[plexno + 1],
		    &vol->plex[plexno],
		    (vol->plexes - 1 - plexno) * sizeof(int));
	    vol->plexes--;
	    vol->last_plex_read = 0;			    /* don't go beyond the end */
	    if (!bcmp(vol->name, plex->name, strlen(vol->name) + 1))
		/* this plex is named after the volume */
	    {
		/* First, check if the subdisks are the same */
		if (msg->recurse) {
		    int sdno;

		    for (sdno = 0; sdno < plex->subdisks; sdno++) {
			struct sd *sd = &SD[plex->sdnos[sdno]];

			if (!bcmp(plex->name, sd->name, strlen(plex->name) + 1))
							    /* subdisk is named after the plex */
			{
			    bcopy(sd->name,
				&sd->name[3],
				min(strlen(sd->name) + 1, MAXSDNAME - 3));
			    bcopy("ex-", sd->name, 3);
			    sd->name[MAXSDNAME - 1] = '\0';
			}
		    }
		}
		bcopy(plex->name,
		    &plex->name[3],
		    min(strlen(plex->name) + 1, MAXPLEXNAME - 3));
		bcopy("ex-", plex->name, 3);
		plex->name[MAXPLEXNAME - 1] = '\0';
	    }
	    update_volume_config(volno);
	    save_config();
	    reply->error = 0;
	} else {
	    reply->error = ENOENT;
	    strcpy(reply->msg, "Plex is not attached");
	}
    }
}

void
renameobject(struct vinum_rename_msg *msg)
{
    struct _ioctl_reply *reply = (struct _ioctl_reply *) msg;
    struct drive *drive;
    struct sd *sd;
    struct plex *plex;
    struct volume *vol;

    switch (msg->type) {
    case drive_object:					    /* you can't attach a drive to anything */
	if (find_drive(msg->newname, 0) >= 0) {		    /* we have that name already, */
	    reply->error = EEXIST;
	    reply->msg[0] = '\0';
	    return;
	}
	drive = validdrive(msg->index, reply);
	if (drive) {
	    bcopy(msg->newname, drive->label.name, MAXDRIVENAME);
	    save_config();
	    reply->error = 0;
	}
	return;

    case sd_object:					    /* you can't attach a subdisk to anything */
	if (find_subdisk(msg->newname, 0) >= 0) {	    /* we have that name already, */
	    reply->error = EEXIST;
	    reply->msg[0] = '\0';
	    return;
	}
	sd = validsd(msg->index, reply);
	if (sd) {
	    bcopy(msg->newname, sd->name, MAXSDNAME);
	    update_sd_config(sd->sdno, 0);
	    save_config();
	    reply->error = 0;
	}
	return;

    case plex_object:					    /* you can't attach a plex to anything */
	if (find_plex(msg->newname, 0) >= 0) {		    /* we have that name already, */
	    reply->error = EEXIST;
	    reply->msg[0] = '\0';
	    return;
	}
	plex = validplex(msg->index, reply);
	if (plex) {
	    bcopy(msg->newname, plex->name, MAXPLEXNAME);
	    update_plex_config(plex->plexno, 0);
	    save_config();
	    reply->error = 0;
	}
	return;

    case volume_object:					    /* you can't attach a volume to anything */
	if (find_volume(msg->newname, 0) >= 0) {	    /* we have that name already, */
	    reply->error = EEXIST;
	    reply->msg[0] = '\0';
	    return;
	}
	vol = validvol(msg->index, reply);
	if (vol) {
	    bcopy(msg->newname, vol->name, MAXVOLNAME);
	    update_volume_config(msg->index);
	    save_config();
	    reply->error = 0;
	}
	return;

    case invalid_object:
	reply->error = EINVAL;
	reply->msg[0] = '\0';
    }
}

/*
 * Replace one object with another.
 * Currently only for drives.
 * message->index is the drive number of the old drive
 * message->otherobject is the drive number of the new drive
 */
void
replaceobject(struct vinum_ioctl_msg *msg)
{
    struct _ioctl_reply *reply = (struct _ioctl_reply *) msg;

    reply->error = ENODEV;				    /* until I know how to do this */
    strcpy(reply->msg, "replace not implemented yet");
/*      save_config (); */
}

void
moveobject(struct vinum_ioctl_msg *msg)
{
    struct _ioctl_reply *reply = (struct _ioctl_reply *) msg;
    struct drive *drive;
    struct sd *sd;

    /* Check that our objects are valid (i.e. they exist) */
    drive = validdrive(msg->index, (struct _ioctl_reply *) msg);
    if (drive == NULL)
	return;
    sd = validsd(msg->otherobject, (struct _ioctl_reply *) msg);
    if (sd == NULL)
	return;
    if (sd->driveno == msg->index)			    /* sd already belongs to drive */
	return;

    if (sd->state > sd_stale)
	set_sd_state(sd->sdno, sd_stale, setstate_force);   /* make the subdisk stale */
    else
	sd->state = sd_empty;
    if (sd->plexno >= 0)				    /* part of a plex, */
	update_plex_state(sd->plexno);			    /* update its state */

    /* Return the space on the old drive */
    if ((sd->driveno >= 0)				    /* we have a drive, */
    &&(sd->sectors > 0))				    /* and some space on it */
	return_drive_space(sd->driveno,			    /* return the space */
	    sd->driveoffset,
	    sd->sectors);

    /* Reassign the old subdisk */
    sd->driveno = msg->index;
    sd->driveoffset = -1;				    /* let the drive decide where to put us */
    give_sd_to_drive(sd->sdno);
    reply->error = 0;
}

void
setreadpol(struct vinum_ioctl_msg *msg)
{
    struct _ioctl_reply *reply = (struct _ioctl_reply *) msg;
    struct volume *vol;
    struct plex *plex;
    int myplexno = -1;

    /* Check that our objects are valid (i.e. they exist) */
    vol = validvol(msg->index, reply);
    if (vol == NULL)
	return;

    /* If a plex was specified, check that is is valid */
    if (msg->otherobject >= 0) {
	plex = validplex(msg->otherobject, reply);
	if (vol == NULL)
	    return;

	/* Is it attached to this volume? */
	myplexno = my_plex(msg->index, msg->otherobject);
	if (myplexno < 0) {
	    strcpy(reply->msg, "Plex is not attached to volume");
	    reply->error = ENOENT;
	    return;
	}
    }
    lock_config();
    vol->preferred_plex = myplexno;
    save_config();
    unlock_config();
    reply->error = 0;
}

/* Local Variables: */
/* fill-column: 50 */
/* End: */