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