#include "vinumhdr.h"
#include "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 *);
jmp_buf command_fail;
int
vinumioctl(struct dev_ioctl_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
u_long cmd = ap->a_cmd;
caddr_t data = ap->a_data;
int error;
unsigned int index;
unsigned int sdno;
unsigned int objno;
struct volume *vol;
struct partinfo *dpart;
int fe;
struct _ioctl_reply *ioctl_reply;
error = 0;
switch (DEVTYPE(dev)) {
case VINUM_SUPERDEV_TYPE:
ioctl_reply = (struct _ioctl_reply *) data;
switch (cmd) {
#ifdef VINUMDEBUG
case VINUM_DEBUG:
if (((struct debuginfo *) data)->changeit)
debug = (((struct debuginfo *) data)->param);
else {
if (debug & DEBUG_REMOTEGDB)
boothowto |= RB_GDB;
else
boothowto &= ~RB_GDB;
Debugger("vinum debug");
}
ioctl_reply = (struct _ioctl_reply *) data;
ioctl_reply->error = 0;
break;
#endif
case VINUM_CREATE:
error = lock_config();
if (error)
break;
error = setjmp(command_fail);
if (error == 0)
ioctl_reply->error = parse_user_config((char *) data,
&keyword_set);
else if (ioctl_reply->error == 0) {
error = 0;
ioctl_reply->error = EINVAL;
ioctl_reply->msg[0] = '\0';
}
unlock_config();
break;
case VINUM_GETCONFIG:
bcopy(&vinum_conf, data, sizeof(vinum_conf));
break;
case VINUM_STARTCONFIG:
error = start_config(*(int *) data);
break;
case VINUM_DRIVECONFIG:
index = *(int *) data;
if (index >= (unsigned)vinum_conf.drives_allocated) {
error = ENXIO;
} else {
bcopy(&DRIVE[index], data, sizeof(struct drive));
}
break;
case VINUM_SDCONFIG:
index = *(int *) data;
if (index >= (unsigned) vinum_conf.subdisks_allocated) {
error = ENXIO;
} else {
bcopy(&SD[index], data, sizeof(struct sd));
}
break;
case VINUM_PLEXCONFIG:
index = *(int *) data;
if (index >= (unsigned) vinum_conf.plexes_allocated) {
error = ENXIO;
} else {
bcopy(&PLEX[index], data, sizeof(struct plex));
}
break;
case VINUM_VOLCONFIG:
index = *(int *) data;
if (index >= (unsigned) vinum_conf.volumes_allocated) {
error = ENXIO;
} else {
bcopy(&VOL[index], data, sizeof(struct volume));
}
break;
case VINUM_PLEXSDCONFIG:
index = ((int *)data)[0];
sdno = ((int *)data)[1];
if ((index >= (unsigned) vinum_conf.plexes_allocated)
||(sdno >= PLEX[index].subdisks)) {
error = ENXIO;
} else {
bcopy(&SD[PLEX[index].sdnos[sdno]], data, sizeof(struct sd));
}
break;
case VINUM_SAVECONFIG:
if (VFLAGS & VF_CONFIGURING) {
if (*(int *) data == 0)
finish_config(1);
else
error = EBUSY;
}
if (error == 0)
save_config();
break;
case VINUM_RELEASECONFIG:
if (VFLAGS & VF_CONFIGURING) {
finish_config(0);
save_config();
} else {
error = EINVAL;
}
break;
case VINUM_INIT:
ioctl_reply = (struct _ioctl_reply *) data;
ioctl_reply->error = 0;
break;
case VINUM_RESETCONFIG:
if (vinum_inactive(0)) {
free_vinum(1);
log(LOG_NOTICE, "vinum: CONFIGURATION OBLITERATED\n");
ioctl_reply = (struct _ioctl_reply *) data;
ioctl_reply->error = 0;
} else {
error = EBUSY;
}
case VINUM_SETSTATE:
setstate((struct vinum_ioctl_msg *) data);
break;
case VINUM_SETSTATE_FORCE:
setstate_by_force((struct vinum_ioctl_msg *) data);
break;
#ifdef VINUMDEBUG
case VINUM_MEMINFO:
vinum_meminfo(data);
break;
case VINUM_MALLOCINFO:
error = vinum_mallocinfo(data);
break;
case VINUM_RQINFO:
error = vinum_rqinfo(data);
break;
#endif
case VINUM_REMOVE:
remove((struct vinum_ioctl_msg *) data);
break;
case VINUM_GETFREELIST:
index = *(int *) data;
fe = ((int *) data)[1];
if ((index >= (unsigned) vinum_conf.drives_allocated)
||(DRIVE[index].state == drive_unallocated)) {
error = ENODEV;
} else if (fe >= DRIVE[index].freelist_entries) {
error = ENOENT;
} else {
bcopy(&DRIVE[index].freelist[fe], data,
sizeof(struct drive_freelist));
}
break;
case VINUM_RESETSTATS:
resetstats((struct vinum_ioctl_msg *) data);
break;
case VINUM_ATTACH:
attachobject((struct vinum_ioctl_msg *) data);
break;
case VINUM_DETACH:
detachobject((struct vinum_ioctl_msg *) data);
break;
case VINUM_RENAME:
renameobject((struct vinum_rename_msg *) data);
break;
case VINUM_REPLACE:
replaceobject((struct vinum_ioctl_msg *) data);
break;
case VINUM_DAEMON:
vinum_daemon();
break;
case VINUM_FINDDAEMON:
error = vinum_finddaemon();
break;
case VINUM_SETDAEMON:
error = vinum_setdaemonopts(*(int *) data);
break;
case VINUM_GETDAEMON:
*(int *) data = daemon_options;
break;
case VINUM_PARITYOP:
parityops((struct vinum_ioctl_msg *) data);
break;
case VINUM_MOVE:
moveobject((struct vinum_ioctl_msg *) data);
break;
case VINUM_LABEL:
default:
error = EINVAL;
break;
}
break;
case VINUM_DRIVE_TYPE:
case VINUM_SD_TYPE:
case VINUM_RAWSD_TYPE:
case VINUM_RAWPLEX_TYPE:
case VINUM_PLEX_TYPE:
error = EINVAL;
break;
case VINUM_VOLUME_TYPE:
objno = Volno(dev);
if (objno >= (unsigned)vinum_conf.volumes_allocated) {
error = ENXIO;
break;
}
vol = &VOL[objno];
if (vol->state != volume_up) {
error = EIO;
break;
}
switch(cmd) {
case DIOCGPART:
dpart = (void *)data;
bzero(dpart, sizeof(*dpart));
dpart->media_offset = 0;
dpart->media_size = (u_int64_t)vol->size * DEV_BSIZE;
dpart->media_blocks = vol->size;
dpart->media_blksize = DEV_BSIZE;
dpart->fstype = FS_BSDFFS;
break;
default:
error = EINVAL;
}
break;
default:
error = EINVAL;
break;
}
if (error) {
log(LOG_WARNING,
"vinumioctl: invalid ioctl from process %d (%s): %lx\n",
curproc->p_pid,
curproc->p_comm,
cmd);
}
return error;
}
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;
}
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;
drive->writes = 0;
drive->bytes_read = 0;
drive->bytes_written = 0;
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;
sd->writes = 0;
sd->bytes_read = 0;
sd->bytes_written = 0;
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;
plex->bytes_read = 0;
plex->bytes_written = 0;
plex->recovered_reads = 0;
plex->degraded_writes = 0;
plex->parityless_writes = 0;
plex->multiblock = 0;
plex->multistripe = 0;
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;
vol->bytes_written = 0;
vol->reads = 0;
vol->writes = 0;
vol->recovered_reads = 0;
reply->error = 0;
return;
}
reply->error = EINVAL;
return;
}
case invalid_object:
reply->error = EINVAL;
return;
}
}
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:
case volume_object:
case invalid_object:
reply->error = EINVAL;
reply->msg[0] = '\0';
return;
case sd_object:
sd = validsd(msg->index, reply);
if (sd == NULL)
return;
plex = validplex(msg->otherobject, reply);
if (plex) {
if ((plex->organization != plex_concat)
&&(!msg->force)) {
reply->error = EINVAL;
strcpy(reply->msg, "Can't attach to this plex organization");
return;
}
if (sd->plexno >= 0) {
reply->error = EBUSY;
reply->msg[0] = '\0';
return;
}
sd->plexoffset = msg->offset;
set_sd_state(sd->sdno, sd_stale, setstate_force);
give_sd_to_plex(plex->plexno, sd->sdno);
update_sd_config(sd->sdno, 0);
save_config();
}
if (sd->state == sd_reviving)
reply->error = EAGAIN;
else
reply->error = 0;
break;
case plex_object:
plex = validplex(msg->index, reply);
if (plex == NULL)
return;
vol = validvol(msg->otherobject, reply);
if (vol) {
if ((vol->plexes == MAXPLEX)
||(plex->volno >= 0)) {
reply->error = EINVAL;
reply->msg[0] = '\0';
return;
}
for (sdno = 0; sdno < plex->subdisks; sdno++) {
sd = &SD[plex->sdnos[sdno]];
if (sd->state > sd_down)
set_sd_state(plex->sdnos[sdno], sd_stale, setstate_force);
}
set_plex_state(plex->plexno, plex_up, setstate_none);
give_plex_to_volume(msg->otherobject, msg->index);
update_plex_config(plex->plexno, 0);
save_config();
}
}
}
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:
case volume_object:
case invalid_object:
reply->error = EINVAL;
reply->msg[0] = '\0';
return;
case sd_object:
sd = validsd(msg->index, reply);
if (sd == NULL)
return;
if (sd->plexno < 0) {
reply->error = ENOENT;
strcpy(reply->msg, "Subdisk is not attached");
return;
} else {
plex = &PLEX[sd->plexno];
if ((!msg->force)
&&((plex->state == plex_up)
||((plex->state == plex_flaky) && sd->state == sd_up))) {
reply->error = EBUSY;
reply->msg[0] = '\0';
return;
}
sd->plexno = -1;
if (plex->subdisks == 1) {
Free(plex->sdnos);
plex->sdnos = NULL;
plex->subdisks_allocated = 0;
} else {
for (sdno = 0; sdno < plex->subdisks; sdno++) {
if (plex->sdnos[sdno] == msg->index)
break;
}
if (sdno < (plex->subdisks - 1))
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))
{
bcopy(sd->name,
&sd->name[3],
imin(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))
set_plex_state(plex->plexno,
plex_down,
setstate_force | setstate_configuring);
save_config();
reply->error = 0;
}
return;
case plex_object:
plex = validplex(msg->index, reply);
if (plex == NULL)
return;
if (plex->volno >= 0) {
int volno = plex->volno;
vol = &VOL[volno];
if ((!msg->force)
&&((vol->state == volume_up)
&&(vol->plexes == 1))) {
reply->error = EBUSY;
reply->msg[0] = '\0';
return;
}
plex->volno = -1;
for (plexno = 0; plexno < vol->plexes; plexno++) {
if (vol->plex[plexno] == msg->index)
break;
}
if (plexno < (vol->plexes - 1))
bcopy(&vol->plex[plexno + 1],
&vol->plex[plexno],
(vol->plexes - 1 - plexno) * sizeof(int));
vol->plexes--;
vol->last_plex_read = 0;
if (!bcmp(vol->name, plex->name, strlen(vol->name) + 1))
{
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))
{
bcopy(sd->name,
&sd->name[3],
imin(strlen(sd->name) + 1, MAXSDNAME - 3));
bcopy("ex-", sd->name, 3);
sd->name[MAXSDNAME - 1] = '\0';
}
}
}
bcopy(plex->name,
&plex->name[3],
imin(strlen(plex->name) + 1, MAXPLEXNAME - 3));
bcopy("ex-", plex->name, 3);
plex->name[MAXPLEXNAME - 1] = '\0';
}
update_volume_config(volno, 0);
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:
if (find_drive(msg->newname, 0) >= 0) {
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:
if (find_subdisk(msg->newname, 0) >= 0) {
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:
if (find_plex(msg->newname, 0) >= 0) {
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:
if (find_volume(msg->newname, 0) >= 0) {
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, 0);
save_config();
reply->error = 0;
}
return;
case invalid_object:
reply->error = EINVAL;
reply->msg[0] = '\0';
}
}
void
replaceobject(struct vinum_ioctl_msg *msg)
{
struct _ioctl_reply *reply = (struct _ioctl_reply *) msg;
reply->error = ENODEV;
strcpy(reply->msg, "replace not implemented yet");
}
void
moveobject(struct vinum_ioctl_msg *msg)
{
struct _ioctl_reply *reply = (struct _ioctl_reply *) msg;
struct drive *drive;
struct sd *sd;
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)
return;
if (sd->state > sd_stale)
set_sd_state(sd->sdno, sd_stale, setstate_force);
else
sd->state = sd_empty;
if (sd->plexno >= 0)
update_plex_state(sd->plexno);
if ((sd->driveno >= 0)
&&(sd->sectors > 0))
return_drive_space(sd->driveno,
sd->driveoffset,
sd->sectors);
sd->driveno = msg->index;
sd->driveoffset = -1;
give_sd_to_drive(sd->sdno);
reply->error = 0;
}