2 * XXX replace all the checks on object validity with
3 * calls to valid<object>
6 * Copyright (c) 1997, 1998, 1999
7 * Nan Yang Computer Services Limited. All rights reserved.
9 * Parts copyright (c) 1997, 1998 Cybernet Corporation, NetMAX project.
11 * Written by Greg Lehey
13 * This software is distributed under the so-called ``Berkeley
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
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
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.
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.
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.8 2007/05/15 17:50:56 dillon Exp $
53 #include <sys/reboot.h>
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 *);
62 jmp_buf command_fail; /* return on a failed command */
66 vinumioctl(struct dev_ioctl_args *ap)
68 cdev_t dev = ap->a_head.a_dev;
69 u_long cmd = ap->a_cmd;
70 caddr_t data = ap->a_data;
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 */
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 */
88 if (((struct debuginfo *) data)->changeit) /* change debug settings */
89 debug = (((struct debuginfo *) data)->param);
91 if (debug & DEBUG_REMOTEGDB)
92 boothowto |= RB_GDB; /* serial debug line */
94 boothowto &= ~RB_GDB; /* local ddb */
95 Debugger("vinum debug");
97 ioctl_reply = (struct _ioctl_reply *) data; /* reinstate the address to reply to */
98 ioctl_reply->error = 0;
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 */
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? */
115 return 0; /* must be 0 to return the real error info */
117 case VINUM_GETCONFIG: /* get the configuration information */
118 bcopy(&vinum_conf, data, sizeof(vinum_conf));
121 /* start configuring the subsystem */
122 case VINUM_STARTCONFIG:
123 return start_config(*(int *) data); /* just lock it. Parameter is 'force' */
126 * Move the individual parts of the config to user space.
128 * Specify the index of the object in the first word of data,
129 * and return the object there
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 */
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 */
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 */
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 */
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 */
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.
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.
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 */
185 return EBUSY; /* can't do it now */
187 save_config(); /* save configuration to disk */
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 */
195 error = EINVAL; /* release what config? */
199 ioctl_reply = (struct _ioctl_reply *) data; /* reinstate the address to reply to */
200 ioctl_reply->error = 0;
203 case VINUM_RESETCONFIG:
204 if (vinum_inactive(0)) { /* if the volumes are not active */
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
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;
218 setstate((struct vinum_ioctl_msg *) data); /* set an object state */
222 * Set state by force, without changing
225 case VINUM_SETSTATE_FORCE:
226 setstate_by_force((struct vinum_ioctl_msg *) data); /* set an object state */
234 case VINUM_MALLOCINFO:
235 return vinum_mallocinfo(data);
238 return vinum_rqinfo(data);
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 */
247 remove((struct vinum_ioctl_msg *) data); /* remove an object */
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))
256 if (fe >= DRIVE[index].freelist_entries) /* no such entry */
258 bcopy(&DRIVE[index].freelist[fe],
260 sizeof(struct drive_freelist));
263 case VINUM_RESETSTATS:
264 resetstats((struct vinum_ioctl_msg *) data); /* reset object stats */
267 /* attach an object to a superordinate object */
269 attachobject((struct vinum_ioctl_msg *) data);
272 /* detach an object from a superordinate object */
274 detachobject((struct vinum_ioctl_msg *) data);
277 /* rename an object */
279 renameobject((struct vinum_rename_msg *) data);
282 /* replace an object */
284 replaceobject((struct vinum_ioctl_msg *) data);
288 vinum_daemon(); /* perform the daemon */
291 case VINUM_FINDDAEMON: /* check for presence of daemon */
292 return vinum_finddaemon();
295 case VINUM_SETDAEMON: /* set daemon flags */
296 return vinum_setdaemonopts(*(int *) data);
298 case VINUM_GETDAEMON: /* get daemon flags */
299 *(int *) data = daemon_options;
302 case VINUM_PARITYOP: /* check/rebuild RAID-4/5 parity */
303 parityops((struct vinum_ioctl_msg *) data);
308 moveobject((struct vinum_ioctl_msg *) data);
315 case VINUM_DRIVE_TYPE:
318 "vinumioctl: invalid ioctl from process %d (%s): %lx\n",
325 case VINUM_RAWSD_TYPE:
331 case DIOCGDINFO: /* get disk label */
332 get_volume_label(sd->name, 1, sd->sectors, (struct disklabel *) data);
336 * We don't have this stuff on hardware,
337 * so just pretend to do it so that
338 * utilities don't get upset.
340 case DIOCWDINFO: /* write partition info */
341 case DIOCSDINFO: /* set partition info */
342 return 0; /* not a titty */
345 return ENOTTY; /* not my kind of ioctl */
348 return 0; /* pretend we did it */
350 case VINUM_RAWPLEX_TYPE:
351 case VINUM_PLEX_TYPE:
357 case DIOCGDINFO: /* get disk label */
358 get_volume_label(plex->name, 1, plex->length, (struct disklabel *) data);
362 * We don't have this stuff on hardware,
363 * so just pretend to do it so that
364 * utilities don't get upset.
366 case DIOCWDINFO: /* write partition info */
367 case DIOCSDINFO: /* set partition info */
368 return 0; /* not a titty */
371 return ENOTTY; /* not my kind of ioctl */
374 return 0; /* pretend we did it */
376 case VINUM_VOLUME_TYPE:
379 if ((unsigned) objno >= (unsigned) vinum_conf.volumes_allocated) /* not a valid volume */
382 if (vol->state != volume_up) /* not up, */
383 return EIO; /* I/O error */
386 case DIOCGDINFO: /* get disk label */
387 get_volume_label(vol->name, vol->plexes, vol->size, (struct disklabel *) data);
390 case DIOCGPART: /* get partition information */
392 struct partinfo *dpart = (void *)data;
394 bzero(dpart, sizeof(*dpart));
395 dpart->media_offset = 0;
396 dpart->media_size = (u_int64_t)vol->size * DEV_BSIZE;
397 dpart->media_blocks = vol->size;
398 dpart->media_blksize = DEV_BSIZE;
399 dpart->fstype = FS_BSDFFS;
404 * We don't have this stuff on hardware,
405 * so just pretend to do it so that
406 * utilities don't get upset.
408 case DIOCWDINFO: /* write partition info */
409 case DIOCSDINFO: /* set partition info */
410 return 0; /* not a titty */
412 case DIOCWLABEL: /* set or reset label writeable */
413 if ((ap->a_fflag & FWRITE) == 0) /* not writeable? */
414 return EACCES; /* no, die */
415 if (*(int *) data != 0) /* set it? */
416 vol->flags |= VF_WLABEL; /* yes */
418 vol->flags &= ~VF_WLABEL; /* no, reset */
422 return ENOTTY; /* not my kind of ioctl */
430 * The following four functions check the supplied
431 * object index and return a pointer to the object
432 * if it exists. Otherwise they longjump out via
436 validdrive(int driveno, struct _ioctl_reply *reply)
438 if ((driveno < vinum_conf.drives_allocated)
439 && (DRIVE[driveno].state > drive_referenced))
440 return &DRIVE[driveno];
441 strcpy(reply->msg, "No such drive");
442 reply->error = ENOENT;
447 validsd(int sdno, struct _ioctl_reply *reply)
449 if ((sdno < vinum_conf.subdisks_allocated)
450 && (SD[sdno].state > sd_referenced))
452 strcpy(reply->msg, "No such subdisk");
453 reply->error = ENOENT;
458 validplex(int plexno, struct _ioctl_reply *reply)
460 if ((plexno < vinum_conf.plexes_allocated)
461 && (PLEX[plexno].state > plex_referenced))
462 return &PLEX[plexno];
463 strcpy(reply->msg, "No such plex");
464 reply->error = ENOENT;
469 validvol(int volno, struct _ioctl_reply *reply)
471 if ((volno < vinum_conf.volumes_allocated)
472 && (VOL[volno].state > volume_uninit))
474 strcpy(reply->msg, "No such volume");
475 reply->error = ENOENT;
479 /* reset an object's stats */
481 resetstats(struct vinum_ioctl_msg *msg)
483 struct _ioctl_reply *reply = (struct _ioctl_reply *) msg;
487 if (msg->index < vinum_conf.drives_allocated) {
488 struct drive *drive = &DRIVE[msg->index];
489 if (drive->state > drive_referenced) {
490 drive->reads = 0; /* number of reads on this drive */
491 drive->writes = 0; /* number of writes on this drive */
492 drive->bytes_read = 0; /* number of bytes read */
493 drive->bytes_written = 0; /* number of bytes written */
497 reply->error = EINVAL;
501 if (msg->index < vinum_conf.subdisks_allocated) {
502 struct sd *sd = &SD[msg->index];
503 if (sd->state > sd_referenced) {
504 sd->reads = 0; /* number of reads on this subdisk */
505 sd->writes = 0; /* number of writes on this subdisk */
506 sd->bytes_read = 0; /* number of bytes read */
507 sd->bytes_written = 0; /* number of bytes written */
511 reply->error = EINVAL;
517 if (msg->index < vinum_conf.plexes_allocated) {
518 struct plex *plex = &PLEX[msg->index];
519 if (plex->state > plex_referenced) {
521 plex->writes = 0; /* number of writes on this plex */
522 plex->bytes_read = 0; /* number of bytes read */
523 plex->bytes_written = 0; /* number of bytes written */
524 plex->recovered_reads = 0; /* number of recovered read operations */
525 plex->degraded_writes = 0; /* number of degraded writes */
526 plex->parityless_writes = 0; /* number of parityless writes */
527 plex->multiblock = 0; /* requests that needed more than one block */
528 plex->multistripe = 0; /* requests that needed more than one stripe */
532 reply->error = EINVAL;
538 if (msg->index < vinum_conf.volumes_allocated) {
539 struct volume *vol = &VOL[msg->index];
540 if (vol->state > volume_uninit) {
541 vol->bytes_read = 0; /* number of bytes read */
542 vol->bytes_written = 0; /* number of bytes written */
543 vol->reads = 0; /* number of reads on this volume */
544 vol->writes = 0; /* number of writes on this volume */
545 vol->recovered_reads = 0; /* reads recovered from another plex */
549 reply->error = EINVAL;
552 case invalid_object: /* can't get this */
553 reply->error = EINVAL;
558 /* attach an object to a superior object */
560 attachobject(struct vinum_ioctl_msg *msg)
562 struct _ioctl_reply *reply = (struct _ioctl_reply *) msg;
569 case drive_object: /* you can't attach a drive to anything */
570 case volume_object: /* nor a volume */
571 case invalid_object: /* "this can't happen" */
572 reply->error = EINVAL;
573 reply->msg[0] = '\0'; /* vinum(8) doesn't do this */
577 sd = validsd(msg->index, reply);
578 if (sd == NULL) /* not a valid subdisk */
580 plex = validplex(msg->otherobject, reply);
583 * We should be more intelligent about this.
584 * We should be able to reattach a dead
585 * subdisk, but if we want to increase the total
586 * number of subdisks, we have a lot of reshuffling
589 if ((plex->organization != plex_concat) /* can't attach to striped and RAID-4/5 */
590 &&(!msg->force)) { /* without using force */
591 reply->error = EINVAL; /* no message, the user should check */
592 strcpy(reply->msg, "Can't attach to this plex organization");
595 if (sd->plexno >= 0) { /* already belong to a plex */
596 reply->error = EBUSY; /* no message, the user should check */
597 reply->msg[0] = '\0';
600 sd->plexoffset = msg->offset; /* this is where we want it */
601 set_sd_state(sd->sdno, sd_stale, setstate_force); /* make sure it's stale */
602 give_sd_to_plex(plex->plexno, sd->sdno); /* and give it to the plex */
603 update_sd_config(sd->sdno, 0);
606 if (sd->state == sd_reviving)
607 reply->error = EAGAIN; /* need to revive it */
613 plex = validplex(msg->index, reply); /* get plex */
616 vol = validvol(msg->otherobject, reply); /* and volume information */
618 if ((vol->plexes == MAXPLEX) /* we have too many already */
619 ||(plex->volno >= 0)) { /* or the plex has an owner */
620 reply->error = EINVAL; /* no message, the user should check */
621 reply->msg[0] = '\0';
624 for (sdno = 0; sdno < plex->subdisks; sdno++) {
625 sd = &SD[plex->sdnos[sdno]];
627 if (sd->state > sd_down) /* real subdisk, vaguely accessible */
628 set_sd_state(plex->sdnos[sdno], sd_stale, setstate_force); /* make it stale */
630 set_plex_state(plex->plexno, plex_up, setstate_none); /* update plex state */
631 give_plex_to_volume(msg->otherobject, msg->index); /* and give it to the volume */
632 update_plex_config(plex->plexno, 0);
638 /* detach an object from a superior object */
640 detachobject(struct vinum_ioctl_msg *msg)
642 struct _ioctl_reply *reply = (struct _ioctl_reply *) msg;
650 case drive_object: /* you can't detach a drive from anything */
651 case volume_object: /* nor a volume */
652 case invalid_object: /* "this can't happen" */
653 reply->error = EINVAL;
654 reply->msg[0] = '\0'; /* vinum(8) doesn't do this */
658 sd = validsd(msg->index, reply);
661 if (sd->plexno < 0) { /* doesn't belong to a plex */
662 reply->error = ENOENT;
663 strcpy(reply->msg, "Subdisk is not attached");
665 } else { /* valid plex number */
666 plex = &PLEX[sd->plexno];
667 if ((!msg->force) /* don't force things */
668 &&((plex->state == plex_up) /* and the plex is up */
669 ||((plex->state == plex_flaky) && sd->state == sd_up))) { /* or flaky with this sd up */
670 reply->error = EBUSY; /* we need this sd */
671 reply->msg[0] = '\0';
674 sd->plexno = -1; /* anonymous sd */
675 if (plex->subdisks == 1) { /* this was the only subdisk */
676 Free(plex->sdnos); /* free the subdisk array */
677 plex->sdnos = NULL; /* and note the fact */
678 plex->subdisks_allocated = 0; /* no subdisk space */
680 for (sdno = 0; sdno < plex->subdisks; sdno++) {
681 if (plex->sdnos[sdno] == msg->index) /* found our subdisk */
684 if (sdno < (plex->subdisks - 1)) /* not the last one, compact */
685 bcopy(&plex->sdnos[sdno + 1],
687 (plex->subdisks - 1 - sdno) * sizeof(int));
690 if (!bcmp(plex->name, sd->name, strlen(plex->name) + 1))
691 /* this subdisk is named after the plex */
695 min(strlen(sd->name) + 1, MAXSDNAME - 3));
696 bcopy("ex-", sd->name, 3);
697 sd->name[MAXSDNAME - 1] = '\0';
699 update_plex_config(plex->plexno, 0);
700 if (isstriped(plex)) /* we've just mutilated our plex, */
701 set_plex_state(plex->plexno,
703 setstate_force | setstate_configuring);
710 plex = validplex(msg->index, reply); /* get plex */
713 if (plex->volno >= 0) {
714 int volno = plex->volno;
717 if ((!msg->force) /* don't force things */
718 &&((vol->state == volume_up) /* and the volume is up */
719 &&(vol->plexes == 1))) { /* and this is the last plex */
721 * XXX As elsewhere, check whether we will lose
722 * mapping by removing this plex
724 reply->error = EBUSY; /* we need this plex */
725 reply->msg[0] = '\0';
728 plex->volno = -1; /* anonymous plex */
729 for (plexno = 0; plexno < vol->plexes; plexno++) {
730 if (vol->plex[plexno] == msg->index) /* found our plex */
733 if (plexno < (vol->plexes - 1)) /* not the last one, compact */
734 bcopy(&vol->plex[plexno + 1],
736 (vol->plexes - 1 - plexno) * sizeof(int));
738 vol->last_plex_read = 0; /* don't go beyond the end */
739 if (!bcmp(vol->name, plex->name, strlen(vol->name) + 1))
740 /* this plex is named after the volume */
742 /* First, check if the subdisks are the same */
746 for (sdno = 0; sdno < plex->subdisks; sdno++) {
747 struct sd *sd = &SD[plex->sdnos[sdno]];
749 if (!bcmp(plex->name, sd->name, strlen(plex->name) + 1))
750 /* subdisk is named after the plex */
754 min(strlen(sd->name) + 1, MAXSDNAME - 3));
755 bcopy("ex-", sd->name, 3);
756 sd->name[MAXSDNAME - 1] = '\0';
762 min(strlen(plex->name) + 1, MAXPLEXNAME - 3));
763 bcopy("ex-", plex->name, 3);
764 plex->name[MAXPLEXNAME - 1] = '\0';
766 update_volume_config(volno, 0);
770 reply->error = ENOENT;
771 strcpy(reply->msg, "Plex is not attached");
777 renameobject(struct vinum_rename_msg *msg)
779 struct _ioctl_reply *reply = (struct _ioctl_reply *) msg;
786 case drive_object: /* you can't attach a drive to anything */
787 if (find_drive(msg->newname, 0) >= 0) { /* we have that name already, */
788 reply->error = EEXIST;
789 reply->msg[0] = '\0';
792 drive = validdrive(msg->index, reply);
794 bcopy(msg->newname, drive->label.name, MAXDRIVENAME);
800 case sd_object: /* you can't attach a subdisk to anything */
801 if (find_subdisk(msg->newname, 0) >= 0) { /* we have that name already, */
802 reply->error = EEXIST;
803 reply->msg[0] = '\0';
806 sd = validsd(msg->index, reply);
808 bcopy(msg->newname, sd->name, MAXSDNAME);
809 update_sd_config(sd->sdno, 0);
815 case plex_object: /* you can't attach a plex to anything */
816 if (find_plex(msg->newname, 0) >= 0) { /* we have that name already, */
817 reply->error = EEXIST;
818 reply->msg[0] = '\0';
821 plex = validplex(msg->index, reply);
823 bcopy(msg->newname, plex->name, MAXPLEXNAME);
824 update_plex_config(plex->plexno, 0);
830 case volume_object: /* you can't attach a volume to anything */
831 if (find_volume(msg->newname, 0) >= 0) { /* we have that name already, */
832 reply->error = EEXIST;
833 reply->msg[0] = '\0';
836 vol = validvol(msg->index, reply);
838 bcopy(msg->newname, vol->name, MAXVOLNAME);
839 update_volume_config(msg->index, 0);
846 reply->error = EINVAL;
847 reply->msg[0] = '\0';
852 * Replace one object with another.
853 * Currently only for drives.
854 * message->index is the drive number of the old drive
855 * message->otherobject is the drive number of the new drive
858 replaceobject(struct vinum_ioctl_msg *msg)
860 struct _ioctl_reply *reply = (struct _ioctl_reply *) msg;
862 reply->error = ENODEV; /* until I know how to do this */
863 strcpy(reply->msg, "replace not implemented yet");
864 /* save_config (); */
868 moveobject(struct vinum_ioctl_msg *msg)
870 struct _ioctl_reply *reply = (struct _ioctl_reply *) msg;
874 /* Check that our objects are valid (i.e. they exist) */
875 drive = validdrive(msg->index, (struct _ioctl_reply *) msg);
878 sd = validsd(msg->otherobject, (struct _ioctl_reply *) msg);
881 if (sd->driveno == msg->index) /* sd already belongs to drive */
884 if (sd->state > sd_stale)
885 set_sd_state(sd->sdno, sd_stale, setstate_force); /* make the subdisk stale */
887 sd->state = sd_empty;
888 if (sd->plexno >= 0) /* part of a plex, */
889 update_plex_state(sd->plexno); /* update its state */
891 /* Return the space on the old drive */
892 if ((sd->driveno >= 0) /* we have a drive, */
893 &&(sd->sectors > 0)) /* and some space on it */
894 return_drive_space(sd->driveno, /* return the space */
898 /* Reassign the old subdisk */
899 sd->driveno = msg->index;
900 sd->driveoffset = -1; /* let the drive decide where to put us */
901 give_sd_to_drive(sd->sdno);
905 /* Local Variables: */
906 /* fill-column: 50 */