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