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