Merge from vendor branch SENDMAIL:
[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  * $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: */