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