diff options
Diffstat (limited to 'sys/dev/slice/disklabel.c')
| -rw-r--r-- | sys/dev/slice/disklabel.c | 827 |
1 files changed, 827 insertions, 0 deletions
diff --git a/sys/dev/slice/disklabel.c b/sys/dev/slice/disklabel.c new file mode 100644 index 000000000000..cb2119b2f189 --- /dev/null +++ b/sys/dev/slice/disklabel.c @@ -0,0 +1,827 @@ +/*- + * Copyright (C) 1997,1998 Julian Elischer. All rights reserved. + * julian@freebsd.org + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: $ + */ + + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/fcntl.h> +#include <sys/disklabel.h> +#include <sys/diskslice.h> +#include <sys/dkstat.h> +#include <sys/malloc.h> +#include <dev/slice/slice.h> + +#include <sys/conf.h> +#include <sys/sliceio.h> +#include <sys/syslog.h> + + +struct private_data { + u_int32_t flags; + u_int8_t rflags; + u_int8_t wflags; + int savedoflags; + struct slice *slice_down; + struct disklabel disklabel; + struct subdev { + int part; + struct slice *slice; + struct slicelimits limit; + struct private_data *pd; + u_int32_t offset; /* all disklabel supports */ + } subdevs[MAXPARTITIONS]; +}; + +static sl_h_constructor_t dkl_constructor; /* constructor (from device) */ +static sl_h_IO_req_t dkl_IOreq; /* IO req downward (to device) */ +static sl_h_ioctl_t dkl_ioctl; /* ioctl req downward (to device) */ +static sl_h_open_t dkl_open; /* downwards travelling open */ +static sl_h_close_t dkl_close; /* downwards travelling close */ +static sl_h_claim_t dkl_claim; /* upwards travelling claim */ +static sl_h_revoke_t dkl_revoke;/* upwards travelling revokation */ +static sl_h_verify_t dkl_verify;/* things changed, are we stil valid? */ +static sl_h_upconfig_t dkl_upconfig;/* config requests from below */ + +static struct slice_handler slicetype = { + "disklabel", + 0, + NULL, + 0, + &dkl_constructor, /* constructor */ + &dkl_IOreq, + &dkl_ioctl, + &dkl_open, + &dkl_close, + &dkl_revoke, /* revoke */ + &dkl_claim, /* claim */ + &dkl_verify, /* verify */ + &dkl_upconfig /* subslice manipulation */ +}; + +static void +sd_drvinit(void *unused) +{ + sl_newtype(&slicetype); +} + +SYSINIT(sddev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sd_drvinit, NULL); + +/*- + * Given a slice, extract out our table of information + */ +/*- + * Attempt to read a disk label from a slice. + * The label must be partly set up before this: secpercyl, secsize + * and anything required in the strategy routine (e.g., dummy bounds for the + * partition containing the label) must be filled in before calling us. + * Returns NULL on success and an error string on failure. + */ +static int +dkl_extract_table(sl_p slice, struct disklabel * lp) +{ + int error = EINVAL; + struct buf *bp; + struct disklabel *dlp; + struct partition *dp; + int part; + int slice_offset; /* XXX */ + + RR; + /* start off with a known result */ + bzero(lp, sizeof(*lp)); + if (error = slice_readblock(slice, LABELSECTOR, &bp)) + return (error); + /* + * Step through the block looking for the label. + * It may not be at the front (Though I have never seen this). + * When found, copy it to the destination supplied. + */ + error = EINVAL; + for (dlp = (struct disklabel *) bp->b_data; + dlp <= (struct disklabel *) ((char *) bp->b_data + + slice->limits.blksize + - sizeof(*dlp)); + dlp = (struct disklabel *) ((char *) dlp + sizeof(long))) { + if ((dlp->d_magic != DISKMAGIC) || + (dlp->d_magic2 != DISKMAGIC) || + (dlp->d_npartitions > MAXPARTITIONS) || + dkcksum(dlp)) + continue; + error = 0; + bcopy(dlp, lp, sizeof(*lp)); + /* + * disklabels are done relative to the base of the disk, + * rather than the local partition, (DUH!) + * so use partition 2 (c) to get the base, + * and subtract it from all non-0 offsets. + */ + dp = lp->d_partitions; + slice_offset = dp[2].p_offset; + for (part = 0; part < MAXPARTITIONS; part++, dp++) { + /* + * We could be reloading, in which case skip + * entries already set up. + */ + if (dp->p_size == 0) + continue; + if( dp->p_offset < slice_offset ) { + printf("slice before 'c'\n"); + dp->p_size = 0; + continue; + } + dp->p_offset -= slice_offset; + } + break; + } + +done: + bp->b_flags |= B_INVAL | B_AGE; + brelse(bp); + return (error); +} +/* + * given a table, write it to disk. + */ +static int +dkl_insert_table(sl_p slice, struct disklabel * lp) +{ + int error = EINVAL; + struct buf *bp; + struct disklabel *dlp; + struct partition *dp; + int part; + int slice_offset; /* XXX */ + + RR; + /* start off with a known result */ + if (error = slice_readblock(slice, LABELSECTOR, &bp)) + return (error); + /* + * Step through the block looking for the label. + * It may not be at the front (Though I have never seen this). + * When found, replace it witht he new one. + */ + error = EINVAL; + for (dlp = (struct disklabel *) bp->b_data; + dlp <= (struct disklabel *) ((char *) bp->b_data + + slice->limits.blksize + - sizeof(*dlp)); + dlp = (struct disklabel *) ((char *) dlp + sizeof(long))) { + if ((dlp->d_magic != DISKMAGIC) || + (dlp->d_magic2 != DISKMAGIC) || + (dlp->d_npartitions > MAXPARTITIONS) || + dkcksum(dlp)) + continue; + error = 0; + } + if (error) { + /* + * We didn't find one.. + * so clear the block and place the new disklabel + * at the start. + */ + bzero(bp->b_data, slice->limits.blksize); + dlp = (struct disklabel *) bp->b_data; + } + /* + * old disklabels are done relative to the base of the disk, + * rather than the local partition, (DUH!) + * so use partition 2 (c) to get the base, + * and subtract it from all non-0 offsets. + */ + dp = dlp->d_partitions; + slice_offset = dp[2].p_offset; + bcopy(lp, dlp, sizeof(*lp)); + slice_offset -= dp[2].p_offset; /* size we adjust by? */ + for (part = 0; part < MAXPARTITIONS; part++, dp++) { + if (dp->p_size == 0) + continue; + dp->p_offset += slice_offset; + } + error = slice_writeblock(slice, LABELSECTOR, bp); +quit: + bp->b_flags |= B_INVAL | B_AGE; + brelse(bp); + return (error); +} + + + +/*- + * look at a slice and figure out if we should be interested in it. (Is it + * ours?) + */ +static int +dkl_claim(struct slice * slice, struct slice * lower, void *ID) +{ + struct disklabel disklabel; + struct disklabel *dl, *dl0; + int error; + RR; + + /*- + * Try load a valid disklabel table. + * This is 90% of what we need to check. + */ + if ((error = dkl_extract_table(slice, &disklabel)) != 0) { + return (error); + } + /*- + * If there is no geometry info, extract it from the label + * as some drivers need this. + */ + /* XXX */ + + /*- + * well, it looks like one of ours. + */ + return (0); +} + +/*- + * This is a special HACK function for the IDE driver. + * It is here because everything it need is in scope here, + * but it is not really part of the SLICE code. + * Because old ESDI drives could not tell their geometry, They need + * to get it from the MBR or the disklabel. This is the disklabel bit. + */ +int +dkl_geom_hack(struct slice * slice, struct ide_geom *geom) +{ + struct disklabel disklabel; + struct disklabel *dl, *dl0; + int error; + RR; + + /* first check it's a disklabel*/ + if ((error = dkl_claim (slice, NULL, 0))) + return (error); + /*- + * Try load a valid disklabel table. + * This is wasteful but never called on new (< 5 YO ) drives. + */ + if ((error = dkl_extract_table(slice, &disklabel)) != 0) { + return (error); + } + geom->secpertrack = disklabel. d_nsectors; + geom->trackpercyl = disklabel.d_ntracks; + geom->cyls = disklabel.d_ncylinders; + return (0); +} + +/*- + * look at a slice we know to be ours and decide what the #$%^ to do with it. + */ +static int +dkl_constructor(sl_p slice) +{ + int i; + u_int64_t disksize = slice->limits.slicesize; + struct private_data *pd; + struct partition *dp, *dp0; + struct disklabel *dl; + sh_p tp; + char name[64]; + + int part; + int error = 0; + u_long dkl_offset; + + RR; + /*- + * If we are being called to re-load a slice, + * then don't reallocate resources. + */ + if ((pd = slice->private_up) == NULL) { + if (slice->name == NULL) { + printf("name is NULL\n"); + return (EINVAL); + } + if (strlen(slice->name) > 58) { + printf("slice: name %s too long\n", slice->name); + return (ENAMETOOLONG); + } + pd = malloc(sizeof(*pd), M_DEVBUF, M_NOWAIT); + if (pd == NULL) { + printf("fdisk: failed malloc\n"); + return (ENOMEM); + } + bzero(pd, sizeof(*pd)); + pd->slice_down = slice; + if ((error = dkl_extract_table(slice, &pd->disklabel)) != 0) { + struct partinfo data; + /* + * If it's just that there is no disklabel there, + * Then we fake one up and write it. if this were + * not ok, then we would have not been called. + * (as probe will have failed). If it's + * a physical error, then that's reason to fail. + */ + if (error != EINVAL) { + free(pd, M_DEVBUF); + return (error); + } + dkl_dummy_ioctl(slice, DIOCGPART, + (caddr_t) &data, 0, NULL); + bcopy(data.disklab, &pd->disklabel, + sizeof(pd->disklabel)); + if ((error = dkl_insert_table(slice, &pd->disklabel))) { + free(pd, M_DEVBUF); + return (error); + } + } + slice->refs++; + slice->handler_up = &slicetype; + slice->private_up = pd; + slicetype.refs++; + } + dl = &pd->disklabel; + dp0 = dl->d_partitions; + + /*- + * Handle each of the partitions. + * We should check that each makes sence and is legal. + * 1/ it should not already have a slice. + * 2/ should not be 0 length. + * 3/ should not go past end of our slice. + * 4/ should not overlap other slices. + * It can include sector 0 (unfortunatly) + */ + dp = dp0; + for (part = 0; part < MAXPARTITIONS; part++, dp++) { + int i; + if ( part == 2 ) + continue; /* XXX skip the 'c' partition */ + /* + * We could be reloading, in which case skip + * entries already set up. + */ + if (pd->subdevs[part].slice != NULL) + breakout: continue; + /* + * also skip partitions not present + */ + if (dp->p_size == 0) + continue; +printf(" part %d, start=%d, size=%d\n", part, dp->p_offset, dp->p_size); + + if ((dp->p_offset + dp->p_size) > + (slice->limits.slicesize / slice->limits.blksize)) { + printf("dkl: slice %d too big ", part); + printf("(%x > %x:%x )\n", + (dp->p_offset + dp->p_size), + (slice->limits.slicesize / slice->limits.blksize) ); + continue; + } + /* check for overlaps with existing slices */ + for (i = 0; i < MAXPARTITIONS; i++) { + /* + * Don't bother if that slice was not made. + * This handles the (i == part) case. + */ + if (pd->subdevs[i].slice == NULL) + continue; + if ((dp0[i].p_offset < (dp->p_offset + dp->p_size)) + && ((dp0[i].p_offset + dp0[i].p_size) > dp->p_offset)) { + printf("dkl: slice %d overlaps slice %d\n", + part, i); + goto breakout; + } + } + /*- + * the slice seems to make sense. Use it. + */ + pd->subdevs[part].part = part; + pd->subdevs[part].pd = pd; + pd->subdevs[part].offset = dp->p_offset; + pd->subdevs[part].limit.blksize + = slice->limits.blksize; + pd->subdevs[part].limit.slicesize + = (slice->limits.blksize * (u_int64_t)dp->p_size); + + sprintf(name, "%s%c", slice->name, (char )('a' + part)); + sl_make_slice(&slicetype, + &pd->subdevs[part], + &pd->subdevs[part].limit, + &pd->subdevs[part].slice, + NULL, + name); + pd->subdevs[part].slice->probeinfo.typespecific = &dp->p_fstype; + switch (dp->p_fstype) { + case FS_UNUSED: + /* allow unuseed to be further split */ + pd->subdevs[part].slice->probeinfo.type = NULL; + break; + case FS_V6: + case FS_V7: + case FS_SYSV: + case FS_V71K: + case FS_V8: + case FS_MSDOS: + case FS_BSDLFS: + case FS_OTHER: + case FS_HPFS: + case FS_ISO9660: + case FS_BOOT : +#if 0 + printf("%s: type %d. Leaving\n", + pd->subdevs[part].slice->name, + (u_int)dp->p_fstype); +#endif + case FS_SWAP: + case FS_BSDFFS: + pd->subdevs[part].slice->probeinfo.type = NO_SUBPART; + break; + default: + pd->subdevs[part].slice->probeinfo.type = NULL; + } + /* + * Dont allow further breakup of slices that + * cover our disklabel + */ + if (dp->p_offset < 16) { +#if 0 + printf("%s: covers disklabel. Leaving\n", + pd->subdevs[part].slice->name); +#endif + pd->subdevs[part].slice->probeinfo.type = NO_SUBPART; + } + if ((tp = slice_probeall(pd->subdevs[part].slice)) != NULL) { + (*tp->constructor)(pd->subdevs[part].slice); + } + } + return (error); +} + +/*- + * look at a slice that USED to be ours. + * decide if any sub-slices need to be revoked. + * If not then at least ask them to verify themselves. + */ +static int +dkl_verify(sl_p slice) +{ + register struct private_data *pd; + struct disklabel label; + struct partition *dp, *dp2; + struct disklabel *dl; + int part; + int error; + /* register struct slice *slice; */ + + RR; + pd = slice->private_up; + /* slice = pd->slice_down; */ + bzero(&label, sizeof(label)); + /* + * Try load a valid disklabel. This is 90% of what we need to check. + */ + if (((error = dkl_extract_table(slice, &label)) != 0) + || (slice->limits.blksize != 512)) { + /*- + * Oh oh, we need to invalidate all the subslices. + * and relinquish this slice. + */ + return (dkl_revoke(pd)); + } + dl = &(pd->disklabel); + dp = dl->d_partitions; + dp2 = label.d_partitions; + for (part = 0; part < MAXPARTITIONS; part++, dp++, dp2++) { + if (pd->subdevs[part].slice) { + if ((dp2->p_offset != dp->p_offset) + || (dp2->p_size != dp->p_size)) { + sl_rmslice(pd->subdevs[part].slice); + pd->subdevs[part].slice = NULL; + } else if (pd->subdevs[part].slice->handler_up) { + (*pd->subdevs[part].slice->handler_up->verify) + (pd->subdevs[part].slice); + } + } + } + /*- having got rid of changing slices, replace + * the old table with the new one, and + * handle any new slices by calling the constructor. + */ + bcopy(&label, dl, sizeof(label)); + error = dkl_constructor(slice); +done: + return (error); +} + +/*- + * Invalidate all subslices, and free resources for this handler instance. + */ +static int +dkl_revoke(void *private) +{ + register struct private_data *pd; + register struct slice *slice; + int part; + + RR; + pd = private; + slice = pd->slice_down; + for (part = 0; part < MAXPARTITIONS; part++) { + if (pd->subdevs[part].slice) { + sl_rmslice(pd->subdevs[part].slice); + } + } + /*- + * remove ourself as a handler + */ + slice->handler_up = NULL; + slice->private_up = NULL; + slicetype.refs--; + free(pd, M_DEVBUF); + sl_unref(slice); + return (0); +} + +/*- + * shift the appropriate IO by the offset for that slice. + */ +static void +dkl_IOreq(void *private, struct buf * bp) +{ + register struct private_data *pd; + struct subdev *sdp; + register struct slice *slice; + +RR; + sdp = private; + pd = sdp->pd; + slice = pd->slice_down; + bp->b_pblkno += sdp->offset; /* add the offset for that slice */ + sliceio(slice, bp, SLW_ABOVE); +} + +/* + * shift the appropriate IO by the offset for that slice. + */ +static void +mbr_IOreq(void *private, struct buf * bp) +{ + register struct private_data *pd; + struct subdev *sdp; + register struct slice *slice; + +RR; + sdp = private; + pd = sdp->pd; + slice = pd->slice_down; + bp->b_pblkno += sdp->offset; /* add the offset for that slice */ + sliceio(slice, bp, SLW_ABOVE); +} + +static int +dkl_open(void *private, int flags, int mode, struct proc * p) +{ + register struct private_data *pd; + struct subdev *sdp; + register struct slice *slice; + int error; + u_int8_t newrflags = 0; + u_int8_t newwflags = 0; + int newoflags; + int part; + u_int8_t partbit; + +RR; + sdp = private; + part = sdp->part; + partbit = (1 << part); + pd = sdp->pd; + slice = pd->slice_down; + + /* + * Calculate the change to to over-all picture here. + * Notice that this might result in LESS open bits + * if that was what was passed from above. + * (Prelude to 'mode-change' instead of open/close.) + */ + /* work out what our stored flags will be if this succeeds */ + newwflags &= ~ (partbit); + newrflags &= ~ (partbit); + newwflags |= (flags & FWRITE) ? (partbit) : 0; + newrflags |= (flags & FREAD) ? (partbit) : 0; + + /* work out what we want to pass down this time */ + newoflags = newwflags ? FWRITE : 0; + newoflags |= newrflags ? FREAD : 0; + + /* + * If the agregate flags we used last time are the same as + * the agregate flags we would use this time, then don't + * bother re-doing the command. + */ + if (newoflags != pd->savedoflags) { + if (error = sliceopen(slice, newoflags, mode, p, SLW_ABOVE)) { + return (error); + } + } + + /* + * Now that we know it succeeded, commit, by replacing the old + * flags with the new ones. + */ + pd->rflags = newrflags; + pd->wflags = newwflags; + pd->savedoflags = newoflags; + return (0); +} + +static void +dkl_close(void *private, int flags, int mode, struct proc * p) +{ + register struct private_data *pd; + struct subdev *sdp; + register struct slice *slice; + u_int8_t newrflags = 0; + u_int8_t newwflags = 0; + int newoflags; + int part; + u_int8_t partbit; + +RR; + sdp = private; + part = sdp->part; + partbit = (1 << part); + pd = sdp->pd; + slice = pd->slice_down; + + if ((pd->rflags == 0) && (pd->wflags == 0)) + return; + + /* work out what our stored flags will be if this succeeds */ + newwflags &= ~ (partbit); + newrflags &= ~ (partbit); + newwflags |= (flags & FWRITE) ? (partbit) : 0; + newrflags |= (flags & FREAD) ? (partbit) : 0; + + /* work out what we want to pass down this time */ + newoflags = newwflags ? FWRITE : 0; + newoflags |= newrflags ? FREAD : 0; + + /* + * If this was the last open slice above, then release our own open + */ + if ((pd->rflags == 0) && (pd->wflags == 0)) { + sliceclose(slice, newoflags, mode, p, SLW_ABOVE); + } + pd->rflags = newrflags; + pd->wflags = newwflags; + pd->savedoflags = newoflags; + return ; +} + +static int +dkl_ioctl(void *private, int cmd, caddr_t addr, int flag, struct proc * p) +{ + register struct private_data *pd; + struct subdev *sdp; + register struct slice *slice; + struct disklabel *lp; + int error; + + RR; + sdp = private; + pd = sdp->pd; + slice = pd->slice_down; + lp = &pd->disklabel; + switch (cmd) { + case DIOCGDINFO: + *(struct disklabel *)addr = *lp; + return (0); + + case DIOCGPART: + if (lp == NULL) + return (EINVAL); + ((struct partinfo *)addr)->disklab = lp; + ((struct partinfo *)addr)->part = lp->d_partitions + sdp->part; + return (0); + +/* These don't really make sense. keep the headers for a reminder */ + case DIOCSDINFO: + case DIOCSYNCSLICEINFO: + case DIOCWDINFO: + case DIOCWLABEL: + return (ENOIOCTL); + } + + return ((*slice->handler_down->ioctl) (slice->private_down, + cmd, addr, flag, p)); +} + +static int +dkl_upconfig(struct slice *slice, int cmd, caddr_t addr, int flag, struct proc * p) +{ + RR; + switch (cmd) { + case SLCIOCRESET: + return (0); + +/* These don't really make sense. keep the headers for a reminder */ + default: + return (ENOIOCTL); + } + return (0); +} + +static struct disklabel static_label; +/* + * This is a hack routine called from the slice generic code to produce a dummy + * disklabel when given a slice descriptor. It's in here because this code + * knows about disklabels. + */ +int +dkl_dummy_ioctl(struct slice *slice, int cmd, caddr_t addr, + int flag, struct proc * p) +{ + struct disklabel *lp = &static_label; + + switch (cmd) { + case DIOCGDINFO: + case DIOCGPART: + bzero(lp, sizeof(static_label)); + lp->d_magic = DISKMAGIC; + lp->d_magic2 = DISKMAGIC; + lp->d_secsize = slice->limits.blksize; + lp->d_nsectors = 1; + lp->d_ntracks = 1; + lp->d_secpercyl = 1; + lp->d_ncylinders = + lp->d_secperunit = slice->limits.slicesize + / slice->limits.blksize; + lp->d_npartitions = RAW_PART + 1; + lp->d_partitions[RAW_PART].p_size = lp->d_secperunit; + lp->d_partitions[RAW_PART].p_offset = 0; + break; + default: + return (ENOIOCTL); + } + lp->d_checksum = dkcksum(lp); + + switch (cmd) { + case DIOCGDINFO: + *(struct disklabel *)addr = *lp; + break; + case DIOCGPART: + /* XXX hack alert. + * This is a hack as this information is consumed immediatly + * otherwise the use of a static buffer would be dangerous. + */ + ((struct partinfo *)addr)->disklab = lp; + ((struct partinfo *)addr)->part = lp->d_partitions + RAW_PART; + } + + return (0); + +} + +#if 0 /* use the existing one for now */ +/*- + * Compute checksum for disk label. + */ +u_int +dkcksum(lp) + register struct disklabel *lp; +{ + register u_short *start, *end; + register u_short sum = 0; + + start = (u_short *) lp; + end = (u_short *) & lp->d_partitions[lp->d_npartitions]; + while (start < end) + sum ^= *start++; + return (sum); +} +#endif /* 0 */ + |
