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