xref: /dragonfly/sys/dev/raid/vinum/vinumioctl.c (revision 9b5a9965)
1 /*
2  * XXX replace all the checks on object validity with
3  * calls to valid<object>
4  */
5 /*-
6  * Copyright (c) 1997, 1998, 1999
7  *	Nan Yang Computer Services Limited.  All rights reserved.
8  *
9  *  Parts copyright (c) 1997, 1998 Cybernet Corporation, NetMAX project.
10  *
11  *  Written by Greg Lehey
12  *
13  *  This software is distributed under the so-called ``Berkeley
14  *  License'':
15  *
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions
18  * are met:
19  * 1. Redistributions of source code must retain the above copyright
20  *    notice, this list of conditions and the following disclaimer.
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following disclaimer in the
23  *    documentation and/or other materials provided with the distribution.
24  * 3. All advertising materials mentioning features or use of this software
25  *    must display the following acknowledgement:
26  *	This product includes software developed by Nan Yang Computer
27  *      Services Limited.
28  * 4. Neither the name of the Company nor the names of its contributors
29  *    may be used to endorse or promote products derived from this software
30  *    without specific prior written permission.
31  *
32  * This software is provided ``as is'', and any express or implied
33  * warranties, including, but not limited to, the implied warranties of
34  * merchantability and fitness for a particular purpose are disclaimed.
35  * In no event shall the company or contributors be liable for any
36  * direct, indirect, incidental, special, exemplary, or consequential
37  * damages (including, but not limited to, procurement of substitute
38  * goods or services; loss of use, data, or profits; or business
39  * interruption) however caused and on any theory of liability, whether
40  * in contract, strict liability, or tort (including negligence or
41  * otherwise) arising in any way out of the use of this software, even if
42  * advised of the possibility of such damage.
43  *
44  * $Id: vinumioctl.c,v 1.14 2000/10/27 03:07:53 grog Exp grog $
45  * $FreeBSD: src/sys/dev/vinum/vinumioctl.c,v 1.25.2.4 2002/02/03 00:44:19 grog Exp $
46  * $DragonFly: src/sys/dev/raid/vinum/vinumioctl.c,v 1.11 2007/07/13 18:55:45 dillon Exp $
47  */
48 
49 #include "vinumhdr.h"
50 #include "request.h"
51 
52 #ifdef VINUMDEBUG
53 #include <sys/reboot.h>
54 #endif
55 
56 void attachobject(struct vinum_ioctl_msg *);
57 void detachobject(struct vinum_ioctl_msg *);
58 void renameobject(struct vinum_rename_msg *);
59 void replaceobject(struct vinum_ioctl_msg *);
60 void moveobject(struct vinum_ioctl_msg *);
61 
62 jmp_buf command_fail;					    /* return on a failed command */
63 
64 /* ioctl routine */
65 int
66 vinumioctl(struct dev_ioctl_args *ap)
67 {
68     cdev_t dev = ap->a_head.a_dev;
69     u_long cmd = ap->a_cmd;
70     caddr_t data = ap->a_data;
71     int error;
72     unsigned int index;					    /* for transferring config info */
73     unsigned int sdno;					    /* for transferring config info */
74     unsigned int objno;
75     struct volume *vol;
76     struct partinfo *dpart;
77     int fe;						    /* free list element number */
78     struct _ioctl_reply *ioctl_reply = (struct _ioctl_reply *) data; /* struct to return */
79 
80     error = 0;
81 
82     /* First, decide what we're looking at */
83     switch (DEVTYPE(dev)) {
84     case VINUM_SUPERDEV_TYPE:				    /* ordinary super device */
85 	ioctl_reply = (struct _ioctl_reply *) data;	    /* save the address to reply to */
86 	switch (cmd) {
87 #ifdef VINUMDEBUG
88 	case VINUM_DEBUG:
89 	    if (((struct debuginfo *) data)->changeit)	    /* change debug settings */
90 		debug = (((struct debuginfo *) data)->param);
91 	    else {
92 		if (debug & DEBUG_REMOTEGDB)
93 		    boothowto |= RB_GDB;		    /* serial debug line */
94 		else
95 		    boothowto &= ~RB_GDB;		    /* local ddb */
96 		Debugger("vinum debug");
97 	    }
98 	    ioctl_reply = (struct _ioctl_reply *) data;	    /* reinstate the address to reply to */
99 	    ioctl_reply->error = 0;
100 	    break;
101 #endif
102 
103 	case VINUM_CREATE:				    /* create a vinum object */
104 	    error = lock_config();			    /* get the config for us alone */
105 	    if (error)					    /* can't do it, */
106 		break;
107 	    error = setjmp(command_fail);		    /* come back here on error */
108 	    if (error == 0)				    /* first time, */
109 		ioctl_reply->error = parse_user_config((char *) data, /* update the config */
110 		    &keyword_set);
111 	    else if (ioctl_reply->error == 0) {		    /* longjmp, but no error status */
112 		error = 0;
113 		ioctl_reply->error = EINVAL;		    /* note that something's up */
114 		ioctl_reply->msg[0] = '\0';		    /* no message? */
115 	    }
116 	    unlock_config();
117 	    break;
118 
119 	case VINUM_GETCONFIG:				    /* get the configuration information */
120 	    bcopy(&vinum_conf, data, sizeof(vinum_conf));
121 	    break;
122 
123 	    /* start configuring the subsystem */
124 	case VINUM_STARTCONFIG:
125 	    error = start_config(*(int *) data);	    /* just lock it.  Parameter is 'force' */
126 	    break;
127 
128 	case VINUM_DRIVECONFIG:
129 	    /*
130 	     * Move the individual parts of the config to user space.
131 	     *
132 	     * Specify the index of the object in the first word of data,
133 	     * and return the object there
134 	     */
135 	    index = *(int *) data;
136 	    if (index >= (unsigned)vinum_conf.drives_allocated) {
137 		error = ENXIO;
138 	    } else {
139 		bcopy(&DRIVE[index], data, sizeof(struct drive));
140 	    }
141 	    break;
142 
143 	case VINUM_SDCONFIG:
144 	    index = *(int *) data;
145 	    if (index >= (unsigned) vinum_conf.subdisks_allocated) {
146 		error = ENXIO;
147 	    } else {
148 		bcopy(&SD[index], data, sizeof(struct sd));
149 	    }
150 	    break;
151 
152 	case VINUM_PLEXCONFIG:
153 	    index = *(int *) data;
154 	    if (index >= (unsigned) vinum_conf.plexes_allocated) {
155 		error = ENXIO;
156 	    } else {
157 		bcopy(&PLEX[index], data, sizeof(struct plex));
158 	    }
159 	    break;
160 
161 	case VINUM_VOLCONFIG:
162 	    index = *(int *) data;
163 	    if (index >= (unsigned) vinum_conf.volumes_allocated) {
164 		error = ENXIO;
165 	    } else {
166 		bcopy(&VOL[index], data, sizeof(struct volume));
167 	    }
168 	    break;
169 
170 	case VINUM_PLEXSDCONFIG:
171 	    index = ((int *)data)[0];			    /* get the plex index */
172 	    sdno = ((int *)data)[1];			    /* and the sd index */
173 	    if ((index >= (unsigned) vinum_conf.plexes_allocated)
174 		||(sdno >= PLEX[index].subdisks)) {
175 		error = ENXIO;
176 	    } else {
177 		bcopy(&SD[PLEX[index].sdnos[sdno]], data, sizeof(struct sd));
178 	    }
179 	    break;
180 	case VINUM_SAVECONFIG:
181 	    /*
182 	     * We get called in two places: one from the
183 	     * userland config routines, which call us
184 	     * to complete the config and save it.  This
185 	     * call supplies the value 0 as a parameter.
186 	     *
187 	     * The other place is from the user "saveconfig"
188 	     * routine, which can only work if we're *not*
189 	     * configuring.  In this case, supply parameter 1.
190 	     */
191 	    if (VFLAGS & VF_CONFIGURING) {		    /* must be us, the others are asleep */
192 		if (*(int *) data == 0)			    /* finish config */
193 		    finish_config(1);			    /* finish the configuration and update it */
194 		else
195 		    error = EBUSY;
196 	    }
197 	    if (error == 0)
198 		save_config();				    /* save configuration to disk */
199 	    break;
200 
201 	case VINUM_RELEASECONFIG:			    /* release the config */
202 	    if (VFLAGS & VF_CONFIGURING) {		    /* must be us, the others are asleep */
203 		finish_config(0);			    /* finish the configuration, don't change it */
204 		save_config();				    /* save configuration to disk */
205 	    } else {
206 		error = EINVAL;				    /* release what config? */
207 	    }
208 	    break;
209 
210 	case VINUM_INIT:
211 	    ioctl_reply = (struct _ioctl_reply *) data;	    /* reinstate the address to reply to */
212 	    ioctl_reply->error = 0;
213 	    break;
214 
215 	case VINUM_RESETCONFIG:
216 	    if (vinum_inactive(0)) {			    /* if the volumes are not active */
217 		/*
218 		 * Note the open count.  We may be called from v, so we'll be open.
219 		 * Keep the count so we don't underflow
220 		 */
221 		free_vinum(1);				    /* clean up everything */
222 		log(LOG_NOTICE, "vinum: CONFIGURATION OBLITERATED\n");
223 		ioctl_reply = (struct _ioctl_reply *) data; /* reinstate the address to reply to */
224 		ioctl_reply->error = 0;
225 	    } else {
226 		error = EBUSY;
227 	    }
228 
229 	case VINUM_SETSTATE:
230 	    setstate((struct vinum_ioctl_msg *) data);	    /* set an object state */
231 	    break;
232 
233 	    /*
234 	     * Set state by force, without changing
235 	     * anything else.
236 	     */
237 	case VINUM_SETSTATE_FORCE:
238 	    setstate_by_force((struct vinum_ioctl_msg *) data);	/* set an object state */
239 	    break;
240 
241 #ifdef VINUMDEBUG
242 	case VINUM_MEMINFO:
243 	    vinum_meminfo(data);
244 	    break;
245 
246 	case VINUM_MALLOCINFO:
247 	    error = vinum_mallocinfo(data);
248 	    break;
249 
250 	case VINUM_RQINFO:
251 	    error = vinum_rqinfo(data);
252 	    break;
253 #endif
254 
255 	case VINUM_REMOVE:
256 	    remove((struct vinum_ioctl_msg *) data);	    /* remove an object */
257 	    break;
258 
259 	case VINUM_GETFREELIST:				    /* get a drive free list element */
260 	    index = *(int *) data;			    /* get the drive index */
261 	    fe = ((int *) data)[1];			    /* and the free list element */
262 	    if ((index >= (unsigned) vinum_conf.drives_allocated) /* plex doesn't exist */
263 		||(DRIVE[index].state == drive_unallocated)) {
264 		error = ENODEV;
265 	    } else if (fe >= DRIVE[index].freelist_entries) {
266 		error = ENOENT;
267 	    } else {
268 		bcopy(&DRIVE[index].freelist[fe], data,
269 		      sizeof(struct drive_freelist));
270 	    }
271 	    break;
272 
273 	case VINUM_RESETSTATS:
274 	    resetstats((struct vinum_ioctl_msg *) data);    /* reset object stats */
275 	    break;
276 
277 	    /* attach an object to a superordinate object */
278 	case VINUM_ATTACH:
279 	    attachobject((struct vinum_ioctl_msg *) data);
280 	    break;
281 
282 	    /* detach an object from a superordinate object */
283 	case VINUM_DETACH:
284 	    detachobject((struct vinum_ioctl_msg *) data);
285 	    break;
286 
287 	    /* rename an object */
288 	case VINUM_RENAME:
289 	    renameobject((struct vinum_rename_msg *) data);
290 	    break;
291 
292 	    /* replace an object */
293 	case VINUM_REPLACE:
294 	    replaceobject((struct vinum_ioctl_msg *) data);
295 	    break;
296 
297 	case VINUM_DAEMON:
298 	    vinum_daemon();				    /* perform the daemon */
299 	    break;
300 
301 	case VINUM_FINDDAEMON:				    /* check for presence of daemon */
302 	    error = vinum_finddaemon();
303 	    break;
304 
305 	case VINUM_SETDAEMON:				    /* set daemon flags */
306 	    error = vinum_setdaemonopts(*(int *) data);
307 	    break;
308 
309 	case VINUM_GETDAEMON:				    /* get daemon flags */
310 	    *(int *) data = daemon_options;
311 	    break;
312 
313 	case VINUM_PARITYOP:				    /* check/rebuild RAID-4/5 parity */
314 	    parityops((struct vinum_ioctl_msg *) data);
315 	    break;
316 
317 	    /* move an object */
318 	case VINUM_MOVE:
319 	    moveobject((struct vinum_ioctl_msg *) data);
320 	    break;
321 	default:
322 	    error = EINVAL;
323 	    break;
324 	}
325 	break;
326     case VINUM_LABEL:
327     case VINUM_DRIVE_TYPE:
328     case VINUM_SD_TYPE:
329     case VINUM_RAWSD_TYPE:
330     case VINUM_RAWPLEX_TYPE:
331     case VINUM_PLEX_TYPE:
332 	error = EINVAL;
333 	break;
334     case VINUM_VOLUME_TYPE:
335 	objno = Volno(dev);
336 
337 	if ((unsigned)objno >= (unsigned)vinum_conf.volumes_allocated) {
338 	    error = ENXIO;
339 	    break;
340 	}
341 	vol = &VOL[objno];
342 	if (vol->state != volume_up) {
343 	    error = EIO;
344 	    break;
345 	}
346 
347 	switch(cmd) {
348 	case DIOCGPART:
349 	    dpart = (void *)data;
350 
351 	    bzero(dpart, sizeof(*dpart));
352 	    dpart->media_offset  = 0;
353 	    dpart->media_size    = (u_int64_t)vol->size * DEV_BSIZE;
354 	    dpart->media_blocks  = vol->size;
355 	    dpart->media_blksize = DEV_BSIZE;
356 	    dpart->fstype = FS_BSDFFS;
357 	    break;
358 	default:
359 	    error = EINVAL;
360 	}
361 	break;
362     default:
363 	error = EINVAL;
364 	break;
365     }
366     if (error) {
367 	log(LOG_WARNING,
368 	    "vinumioctl: invalid ioctl from process %d (%s): %lx\n",
369 	    curproc->p_pid,
370 	    curproc->p_comm,
371 	    cmd);
372     }
373     return error;
374 }
375 
376 /*
377  * The following four functions check the supplied
378  * object index and return a pointer to the object
379  * if it exists.  Otherwise they longjump out via
380  * throw_rude_remark.
381  */
382 struct drive *
383 validdrive(int driveno, struct _ioctl_reply *reply)
384 {
385     if ((driveno < vinum_conf.drives_allocated)
386 	&& (DRIVE[driveno].state > drive_referenced))
387 	return &DRIVE[driveno];
388     strcpy(reply->msg, "No such drive");
389     reply->error = ENOENT;
390     return NULL;
391 }
392 
393 struct sd *
394 validsd(int sdno, struct _ioctl_reply *reply)
395 {
396     if ((sdno < vinum_conf.subdisks_allocated)
397 	&& (SD[sdno].state > sd_referenced))
398 	return &SD[sdno];
399     strcpy(reply->msg, "No such subdisk");
400     reply->error = ENOENT;
401     return NULL;
402 }
403 
404 struct plex *
405 validplex(int plexno, struct _ioctl_reply *reply)
406 {
407     if ((plexno < vinum_conf.plexes_allocated)
408 	&& (PLEX[plexno].state > plex_referenced))
409 	return &PLEX[plexno];
410     strcpy(reply->msg, "No such plex");
411     reply->error = ENOENT;
412     return NULL;
413 }
414 
415 struct volume *
416 validvol(int volno, struct _ioctl_reply *reply)
417 {
418     if ((volno < vinum_conf.volumes_allocated)
419 	&& (VOL[volno].state > volume_uninit))
420 	return &VOL[volno];
421     strcpy(reply->msg, "No such volume");
422     reply->error = ENOENT;
423     return NULL;
424 }
425 
426 /* reset an object's stats */
427 void
428 resetstats(struct vinum_ioctl_msg *msg)
429 {
430     struct _ioctl_reply *reply = (struct _ioctl_reply *) msg;
431 
432     switch (msg->type) {
433     case drive_object:
434 	if (msg->index < vinum_conf.drives_allocated) {
435 	    struct drive *drive = &DRIVE[msg->index];
436 	    if (drive->state > drive_referenced) {
437 		drive->reads = 0;			    /* number of reads on this drive */
438 		drive->writes = 0;			    /* number of writes on this drive */
439 		drive->bytes_read = 0;			    /* number of bytes read */
440 		drive->bytes_written = 0;		    /* number of bytes written */
441 		reply->error = 0;
442 		return;
443 	    }
444 	    reply->error = EINVAL;
445 	    return;
446 	}
447     case sd_object:
448 	if (msg->index < vinum_conf.subdisks_allocated) {
449 	    struct sd *sd = &SD[msg->index];
450 	    if (sd->state > sd_referenced) {
451 		sd->reads = 0;				    /* number of reads on this subdisk */
452 		sd->writes = 0;				    /* number of writes on this subdisk */
453 		sd->bytes_read = 0;			    /* number of bytes read */
454 		sd->bytes_written = 0;			    /* number of bytes written */
455 		reply->error = 0;
456 		return;
457 	    }
458 	    reply->error = EINVAL;
459 	    return;
460 	}
461 	break;
462 
463     case plex_object:
464 	if (msg->index < vinum_conf.plexes_allocated) {
465 	    struct plex *plex = &PLEX[msg->index];
466 	    if (plex->state > plex_referenced) {
467 		plex->reads = 0;
468 		plex->writes = 0;			    /* number of writes on this plex */
469 		plex->bytes_read = 0;			    /* number of bytes read */
470 		plex->bytes_written = 0;		    /* number of bytes written */
471 		plex->recovered_reads = 0;		    /* number of recovered read operations */
472 		plex->degraded_writes = 0;		    /* number of degraded writes */
473 		plex->parityless_writes = 0;		    /* number of parityless writes */
474 		plex->multiblock = 0;			    /* requests that needed more than one block */
475 		plex->multistripe = 0;			    /* requests that needed more than one stripe */
476 		reply->error = 0;
477 		return;
478 	    }
479 	    reply->error = EINVAL;
480 	    return;
481 	}
482 	break;
483 
484     case volume_object:
485 	if (msg->index < vinum_conf.volumes_allocated) {
486 	    struct volume *vol = &VOL[msg->index];
487 	    if (vol->state > volume_uninit) {
488 		vol->bytes_read = 0;			    /* number of bytes read */
489 		vol->bytes_written = 0;			    /* number of bytes written */
490 		vol->reads = 0;				    /* number of reads on this volume */
491 		vol->writes = 0;			    /* number of writes on this volume */
492 		vol->recovered_reads = 0;		    /* reads recovered from another plex */
493 		reply->error = 0;
494 		return;
495 	    }
496 	    reply->error = EINVAL;
497 	    return;
498 	}
499     case invalid_object:				    /* can't get this */
500 	reply->error = EINVAL;
501 	return;
502     }
503 }
504 
505 /* attach an object to a superior object */
506 void
507 attachobject(struct vinum_ioctl_msg *msg)
508 {
509     struct _ioctl_reply *reply = (struct _ioctl_reply *) msg;
510     int sdno;
511     struct sd *sd;
512     struct plex *plex;
513     struct volume *vol;
514 
515     switch (msg->type) {
516     case drive_object:					    /* you can't attach a drive to anything */
517     case volume_object:					    /* nor a volume */
518     case invalid_object:				    /* "this can't happen" */
519 	reply->error = EINVAL;
520 	reply->msg[0] = '\0';				    /* vinum(8) doesn't do this */
521 	return;
522 
523     case sd_object:
524 	sd = validsd(msg->index, reply);
525 	if (sd == NULL)					    /* not a valid subdisk  */
526 	    return;
527 	plex = validplex(msg->otherobject, reply);
528 	if (plex) {
529 	    /*
530 	     * We should be more intelligent about this.
531 	     * We should be able to reattach a dead
532 	     * subdisk, but if we want to increase the total
533 	     * number of subdisks, we have a lot of reshuffling
534 	     * to do. XXX
535 	     */
536 	    if ((plex->organization != plex_concat)	    /* can't attach to striped and RAID-4/5 */
537 	    &&(!msg->force)) {				    /* without using force */
538 		reply->error = EINVAL;			    /* no message, the user should check */
539 		strcpy(reply->msg, "Can't attach to this plex organization");
540 		return;
541 	    }
542 	    if (sd->plexno >= 0) {			    /* already belong to a plex */
543 		reply->error = EBUSY;			    /* no message, the user should check */
544 		reply->msg[0] = '\0';
545 		return;
546 	    }
547 	    sd->plexoffset = msg->offset;		    /* this is where we want it */
548 	    set_sd_state(sd->sdno, sd_stale, setstate_force); /* make sure it's stale */
549 	    give_sd_to_plex(plex->plexno, sd->sdno);	    /* and give it to the plex */
550 	    update_sd_config(sd->sdno, 0);
551 	    save_config();
552 	}
553 	if (sd->state == sd_reviving)
554 	    reply->error = EAGAIN;			    /* need to revive it */
555 	else
556 	    reply->error = 0;
557 	break;
558 
559     case plex_object:
560 	plex = validplex(msg->index, reply);		    /* get plex */
561 	if (plex == NULL)
562 	    return;
563 	vol = validvol(msg->otherobject, reply);	    /* and volume information */
564 	if (vol) {
565 	    if ((vol->plexes == MAXPLEX)		    /* we have too many already */
566 	    ||(plex->volno >= 0)) {			    /* or the plex has an owner */
567 		reply->error = EINVAL;			    /* no message, the user should check */
568 		reply->msg[0] = '\0';
569 		return;
570 	    }
571 	    for (sdno = 0; sdno < plex->subdisks; sdno++) {
572 		sd = &SD[plex->sdnos[sdno]];
573 
574 		if (sd->state > sd_down)		    /* real subdisk, vaguely accessible */
575 		    set_sd_state(plex->sdnos[sdno], sd_stale, setstate_force); /* make it stale */
576 	    }
577 	    set_plex_state(plex->plexno, plex_up, setstate_none); /* update plex state */
578 	    give_plex_to_volume(msg->otherobject, msg->index); /* and give it to the volume */
579 	    update_plex_config(plex->plexno, 0);
580 	    save_config();
581 	}
582     }
583 }
584 
585 /* detach an object from a superior object */
586 void
587 detachobject(struct vinum_ioctl_msg *msg)
588 {
589     struct _ioctl_reply *reply = (struct _ioctl_reply *) msg;
590     struct sd *sd;
591     struct plex *plex;
592     struct volume *vol;
593     int sdno;
594     int plexno;
595 
596     switch (msg->type) {
597     case drive_object:					    /* you can't detach a drive from anything */
598     case volume_object:					    /* nor a volume */
599     case invalid_object:				    /* "this can't happen" */
600 	reply->error = EINVAL;
601 	reply->msg[0] = '\0';				    /* vinum(8) doesn't do this */
602 	return;
603 
604     case sd_object:
605 	sd = validsd(msg->index, reply);
606 	if (sd == NULL)
607 	    return;
608 	if (sd->plexno < 0) {				    /* doesn't belong to a plex */
609 	    reply->error = ENOENT;
610 	    strcpy(reply->msg, "Subdisk is not attached");
611 	    return;
612 	} else {					    /* valid plex number */
613 	    plex = &PLEX[sd->plexno];
614 	    if ((!msg->force)				    /* don't force things */
615 	    &&((plex->state == plex_up)			    /* and the plex is up */
616 	    ||((plex->state == plex_flaky) && sd->state == sd_up))) { /* or flaky with this sd up */
617 		reply->error = EBUSY;			    /* we need this sd */
618 		reply->msg[0] = '\0';
619 		return;
620 	    }
621 	    sd->plexno = -1;				    /* anonymous sd */
622 	    if (plex->subdisks == 1) {			    /* this was the only subdisk */
623 		Free(plex->sdnos);			    /* free the subdisk array */
624 		plex->sdnos = NULL;			    /* and note the fact */
625 		plex->subdisks_allocated = 0;		    /* no subdisk space */
626 	    } else {
627 		for (sdno = 0; sdno < plex->subdisks; sdno++) {
628 		    if (plex->sdnos[sdno] == msg->index)    /* found our subdisk */
629 			break;
630 		}
631 		if (sdno < (plex->subdisks - 1))	    /* not the last one, compact */
632 		    bcopy(&plex->sdnos[sdno + 1],
633 			&plex->sdnos[sdno],
634 			(plex->subdisks - 1 - sdno) * sizeof(int));
635 	    }
636 	    plex->subdisks--;
637 	    if (!bcmp(plex->name, sd->name, strlen(plex->name) + 1))
638 		/* this subdisk is named after the plex */
639 	    {
640 		bcopy(sd->name,
641 		    &sd->name[3],
642 		    min(strlen(sd->name) + 1, MAXSDNAME - 3));
643 		bcopy("ex-", sd->name, 3);
644 		sd->name[MAXSDNAME - 1] = '\0';
645 	    }
646 	    update_plex_config(plex->plexno, 0);
647 	    if (isstriped(plex))			    /* we've just mutilated our plex, */
648 		set_plex_state(plex->plexno,
649 		    plex_down,
650 		    setstate_force | setstate_configuring);
651 	    save_config();
652 	    reply->error = 0;
653 	}
654 	return;
655 
656     case plex_object:
657 	plex = validplex(msg->index, reply);		    /* get plex */
658 	if (plex == NULL)
659 	    return;
660 	if (plex->volno >= 0) {
661 	    int volno = plex->volno;
662 
663 	    vol = &VOL[volno];
664 	    if ((!msg->force)				    /* don't force things */
665 	    &&((vol->state == volume_up)		    /* and the volume is up */
666 	    &&(vol->plexes == 1))) {			    /* and this is the last plex */
667 		/*
668 		   * XXX As elsewhere, check whether we will lose
669 		   * mapping by removing this plex
670 		 */
671 		reply->error = EBUSY;			    /* we need this plex */
672 		reply->msg[0] = '\0';
673 		return;
674 	    }
675 	    plex->volno = -1;				    /* anonymous plex */
676 	    for (plexno = 0; plexno < vol->plexes; plexno++) {
677 		if (vol->plex[plexno] == msg->index)	    /* found our plex */
678 		    break;
679 	    }
680 	    if (plexno < (vol->plexes - 1))		    /* not the last one, compact */
681 		bcopy(&vol->plex[plexno + 1],
682 		    &vol->plex[plexno],
683 		    (vol->plexes - 1 - plexno) * sizeof(int));
684 	    vol->plexes--;
685 	    vol->last_plex_read = 0;			    /* don't go beyond the end */
686 	    if (!bcmp(vol->name, plex->name, strlen(vol->name) + 1))
687 		/* this plex is named after the volume */
688 	    {
689 		/* First, check if the subdisks are the same */
690 		if (msg->recurse) {
691 		    int sdno;
692 
693 		    for (sdno = 0; sdno < plex->subdisks; sdno++) {
694 			struct sd *sd = &SD[plex->sdnos[sdno]];
695 
696 			if (!bcmp(plex->name, sd->name, strlen(plex->name) + 1))
697 							    /* subdisk is named after the plex */
698 			{
699 			    bcopy(sd->name,
700 				&sd->name[3],
701 				min(strlen(sd->name) + 1, MAXSDNAME - 3));
702 			    bcopy("ex-", sd->name, 3);
703 			    sd->name[MAXSDNAME - 1] = '\0';
704 			}
705 		    }
706 		}
707 		bcopy(plex->name,
708 		    &plex->name[3],
709 		    min(strlen(plex->name) + 1, MAXPLEXNAME - 3));
710 		bcopy("ex-", plex->name, 3);
711 		plex->name[MAXPLEXNAME - 1] = '\0';
712 	    }
713 	    update_volume_config(volno, 0);
714 	    save_config();
715 	    reply->error = 0;
716 	} else {
717 	    reply->error = ENOENT;
718 	    strcpy(reply->msg, "Plex is not attached");
719 	}
720     }
721 }
722 
723 void
724 renameobject(struct vinum_rename_msg *msg)
725 {
726     struct _ioctl_reply *reply = (struct _ioctl_reply *) msg;
727     struct drive *drive;
728     struct sd *sd;
729     struct plex *plex;
730     struct volume *vol;
731 
732     switch (msg->type) {
733     case drive_object:					    /* you can't attach a drive to anything */
734 	if (find_drive(msg->newname, 0) >= 0) {		    /* we have that name already, */
735 	    reply->error = EEXIST;
736 	    reply->msg[0] = '\0';
737 	    return;
738 	}
739 	drive = validdrive(msg->index, reply);
740 	if (drive) {
741 	    bcopy(msg->newname, drive->label.name, MAXDRIVENAME);
742 	    save_config();
743 	    reply->error = 0;
744 	}
745 	return;
746 
747     case sd_object:					    /* you can't attach a subdisk to anything */
748 	if (find_subdisk(msg->newname, 0) >= 0) {	    /* we have that name already, */
749 	    reply->error = EEXIST;
750 	    reply->msg[0] = '\0';
751 	    return;
752 	}
753 	sd = validsd(msg->index, reply);
754 	if (sd) {
755 	    bcopy(msg->newname, sd->name, MAXSDNAME);
756 	    update_sd_config(sd->sdno, 0);
757 	    save_config();
758 	    reply->error = 0;
759 	}
760 	return;
761 
762     case plex_object:					    /* you can't attach a plex to anything */
763 	if (find_plex(msg->newname, 0) >= 0) {		    /* we have that name already, */
764 	    reply->error = EEXIST;
765 	    reply->msg[0] = '\0';
766 	    return;
767 	}
768 	plex = validplex(msg->index, reply);
769 	if (plex) {
770 	    bcopy(msg->newname, plex->name, MAXPLEXNAME);
771 	    update_plex_config(plex->plexno, 0);
772 	    save_config();
773 	    reply->error = 0;
774 	}
775 	return;
776 
777     case volume_object:					    /* you can't attach a volume to anything */
778 	if (find_volume(msg->newname, 0) >= 0) {	    /* we have that name already, */
779 	    reply->error = EEXIST;
780 	    reply->msg[0] = '\0';
781 	    return;
782 	}
783 	vol = validvol(msg->index, reply);
784 	if (vol) {
785 	    bcopy(msg->newname, vol->name, MAXVOLNAME);
786 	    update_volume_config(msg->index, 0);
787 	    save_config();
788 	    reply->error = 0;
789 	}
790 	return;
791 
792     case invalid_object:
793 	reply->error = EINVAL;
794 	reply->msg[0] = '\0';
795     }
796 }
797 
798 /*
799  * Replace one object with another.
800  * Currently only for drives.
801  * message->index is the drive number of the old drive
802  * message->otherobject is the drive number of the new drive
803  */
804 void
805 replaceobject(struct vinum_ioctl_msg *msg)
806 {
807     struct _ioctl_reply *reply = (struct _ioctl_reply *) msg;
808 
809     reply->error = ENODEV;				    /* until I know how to do this */
810     strcpy(reply->msg, "replace not implemented yet");
811 /*      save_config (); */
812 }
813 
814 void
815 moveobject(struct vinum_ioctl_msg *msg)
816 {
817     struct _ioctl_reply *reply = (struct _ioctl_reply *) msg;
818     struct drive *drive;
819     struct sd *sd;
820 
821     /* Check that our objects are valid (i.e. they exist) */
822     drive = validdrive(msg->index, (struct _ioctl_reply *) msg);
823     if (drive == NULL)
824 	return;
825     sd = validsd(msg->otherobject, (struct _ioctl_reply *) msg);
826     if (sd == NULL)
827 	return;
828     if (sd->driveno == msg->index)			    /* sd already belongs to drive */
829 	return;
830 
831     if (sd->state > sd_stale)
832 	set_sd_state(sd->sdno, sd_stale, setstate_force);   /* make the subdisk stale */
833     else
834 	sd->state = sd_empty;
835     if (sd->plexno >= 0)				    /* part of a plex, */
836 	update_plex_state(sd->plexno);			    /* update its state */
837 
838     /* Return the space on the old drive */
839     if ((sd->driveno >= 0)				    /* we have a drive, */
840     &&(sd->sectors > 0))				    /* and some space on it */
841 	return_drive_space(sd->driveno,			    /* return the space */
842 	    sd->driveoffset,
843 	    sd->sectors);
844 
845     /* Reassign the old subdisk */
846     sd->driveno = msg->index;
847     sd->driveoffset = -1;				    /* let the drive decide where to put us */
848     give_sd_to_drive(sd->sdno);
849     reply->error = 0;
850 }
851 
852 /* Local Variables: */
853 /* fill-column: 50 */
854 /* End: */
855