From d1f35d579f86de9750391d056e22b9dedea4da36 Mon Sep 17 00:00:00 2001 From: Bruce Evans Date: Mon, 12 Dec 1994 00:20:34 +0000 Subject: [PATCH] subr_diskslice.c implements everything related to slices and labels except reading and writing the slice tables and labels. subr_dkbad.c implements everything related to bad sector remapping using the bad144 format. --- sys/kern/subr_diskslice.c | 522 ++++++++++++++++++++++++++++++++++++++ sys/kern/subr_dkbad.c | 158 ++++++++++++ 2 files changed, 680 insertions(+) create mode 100644 sys/kern/subr_diskslice.c create mode 100644 sys/kern/subr_dkbad.c diff --git a/sys/kern/subr_diskslice.c b/sys/kern/subr_diskslice.c new file mode 100644 index 000000000000..882771692759 --- /dev/null +++ b/sys/kern/subr_diskslice.c @@ -0,0 +1,522 @@ +/*- + * Copyright (c) 1994 Bruce D. Evans. + * All rights reserved. + * + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz. + * + * Copyright (c) 1982, 1986, 1988 Regents of the University of California. + * All rights reserved. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS 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. + * + * from: @(#)wd.c 7.2 (Berkeley) 5/9/91 + * from: wd.c,v 1.55 1994/10/22 01:57:12 phk Exp $ + * from: @(#)ufs_disksubr.c 7.16 (Berkeley) 5/4/91 + * from: ufs_disksubr.c,v 1.8 1994/06/07 01:21:39 phk Exp $ + * $Id: + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define b_cylinder b_resid + +#define FALSE 0 +#define TRUE 1 +typedef u_char bool_t; + +static char *fixlabel __P((char *dname, int unit, int slice, + struct diskslice *sp, struct disklabel *lp)); +static void partition_info __P((char *dname, int unit, int slice, + int part, struct partition *pp)); +static void slice_info __P((char *dname, int unit, int slice, + struct diskslice *sp)); + +/* + * Determine the size of the transfer, and make sure it is + * within the boundaries of the partition. Adjust transfer + * if needed, and signal errors or early completion. + * + * XXX TODO: + * o Do bad sector remapping. May need to split buffer. + * o Split buffers that are too big for the device. + * o Check for overflow. + * o Finish cleaning this up. + */ +int +dscheck(bp, ssp) + struct buf *bp; + struct diskslices *ssp; +{ + daddr_t blkno; + daddr_t labelsect; + struct disklabel *lp; + u_long maxsz; + struct partition *pp; + struct diskslice *sp; + long sz; + + sp = &ssp->dss_slices[dkslice(bp->b_dev)]; + lp = sp->ds_label; + sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT; + if (lp == NULL) { + blkno = bp->b_blkno; + labelsect = -LABELSECTOR - 1; + maxsz = sp->ds_size; + } else { + labelsect = lp->d_partitions[LABEL_PART].p_offset; +if (labelsect != 0) Debugger(""); + pp = &lp->d_partitions[dkpart(bp->b_dev)]; + blkno = pp->p_offset + bp->b_blkno; + maxsz = pp->p_size; + if (sp->ds_bad != NULL) { + daddr_t newblkno; + + newblkno = transbad144(sp->ds_bad, blkno); + if (newblkno != blkno) + printf("bad block %lu -> %lu\n", + blkno, newblkno); + } + } + + /* overwriting disk label ? */ + /* XXX should also protect bootstrap in first 8K */ + if (blkno <= LABELSECTOR + labelsect && +#if LABELSECTOR != 0 + bp->b_blkno + sz > LABELSECTOR + labelsect && +#endif + (bp->b_flags & B_READ) == 0 && sp->ds_wlabel == 0) { + bp->b_error = EROFS; + goto bad; + } + +#if defined(DOSBBSECTOR) && defined(notyet) + /* overwriting master boot record? */ + if (blkno <= DOSBBSECTOR && (bp->b_flags & B_READ) == 0 && + sp->ds_wlabel == 0) { + bp->b_error = EROFS; + goto bad; + } +#endif + + /* beyond partition? */ + if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) { + /* if exactly at end of disk, return an EOF */ + if (bp->b_blkno == maxsz) { + bp->b_resid = bp->b_bcount; + return (0); + } + /* or truncate if part of it fits */ + sz = maxsz - bp->b_blkno; + if (sz <= 0) { + bp->b_error = EINVAL; + goto bad; + } + bp->b_bcount = sz << DEV_BSHIFT; + } + + /* calculate cylinder for disksort to order transfers with */ + bp->b_pblkno = blkno + sp->ds_offset; + + if (lp == NULL) + bp->b_cylinder = 0; /* XXX all 0 would be better */ + else + bp->b_cylinder = bp->b_pblkno / lp->d_secpercyl; + return (1); + +bad: + bp->b_flags |= B_ERROR; + return (-1); +} + +void +dsclose(dev, mode, ssp) + dev_t dev; + int mode; + struct diskslices *ssp; +{ + u_char mask; + struct diskslice *sp; + + sp = &ssp->dss_slices[dkslice(dev)]; + mask = 1 << dkpart(dev); + switch (mode) { + case S_IFBLK: + sp->ds_bopenmask &= ~mask; + break; + case S_IFCHR: + sp->ds_copenmask &= ~mask; + break; + } + sp->ds_openmask = sp->ds_bopenmask | sp->ds_copenmask; +} + +/* + * For the "write" commands (DIOCSBAD, DIOCSDINFO and DIOCWDINFO), this + * is subject to the same restriction as dsopen(). + */ +int +dsioctl(dev, cmd, data, flags, ssp, strat, setgeom) + dev_t dev; + int cmd; + caddr_t data; + int flags; + struct diskslices *ssp; + d_strategy_t *strat; + ds_setgeom_t *setgeom; +{ + int error; + struct disklabel *lp; + int old_wlabel; + struct diskslice *sp; + + sp = &ssp->dss_slices[dkslice(dev)]; + lp = sp->ds_label; + switch (cmd) { + + case DIOCGDINFO: + if (lp == NULL) + return (EINVAL); + *(struct disklabel *)data = *lp; + return (0); + +#ifdef notyet + case DIOCGDINFOP: + if (lp == NULL) + return (EINVAL); + *(struct disklabel **)data = lp; + return (0); +#endif + + case DIOCGPART: + if (lp == NULL) + return (EINVAL); + ((struct partinfo *)data)->disklab = lp; + ((struct partinfo *)data)->part + = &lp->d_partitions[dkpart(dev)]; + return (0); + + case DIOCSBAD: + if (!(flags & FWRITE)) + return (EBADF); + if (lp == NULL) + return (EINVAL); + if (sp->ds_bad != NULL) + free(sp->ds_bad, M_DEVBUF); + sp->ds_bad = internbad144((struct dkbad *)data, lp); + return (0); + + case DIOCSDINFO: + if (!(flags & FWRITE)) + return (EBADF); + lp = malloc(sizeof *lp, M_DEVBUF, M_WAITOK); + error = setdisklabel(lp, (struct disklabel *)data, + sp->ds_label != NULL + ? sp->ds_openmask : (u_long)0); +#if 0 /* XXX */ + if (error != 0 && setgeom != NULL) + error = setgeom(lp); +#endif + if (error != 0) { + free(lp, M_DEVBUF); + return (error); + } + if (sp->ds_label != NULL) + free(sp->ds_label, M_DEVBUF); + sp->ds_label = lp; + return (0); + + case DIOCWDINFO: + error = dsioctl(dev, DIOCSDINFO, data, flags, ssp, strat, + setgeom); + if (error != 0) + return (error); + /* + * XXX this used to hack on dk_openpart to fake opening + * partition 0 in case that is used instead of dkpart(dev). + */ + old_wlabel = sp->ds_wlabel; + sp->ds_wlabel = TRUE; + error = writedisklabel(dev, strat, sp->ds_label); + sp->ds_wlabel = old_wlabel; + return (error); + + case DIOCWLABEL: + if (!(flags & FWRITE)) + return (EBADF); + sp->ds_wlabel = (*(int *)data != 0); + return (0); + + default: + return (-1); + } +} + +/* + * This should only be called when the unit is inactive and the strategy + * routine should not allow it to become active unless we call it. Our + * strategy routine must be special to allow activity. + */ +int +dsopen(dname, dev, mode, sspp, lp, strat, setgeom) + char *dname; + dev_t dev; + int mode; + struct diskslices **sspp; + struct disklabel *lp; + d_strategy_t *strat; + ds_setgeom_t *setgeom; +{ + int error; + char *msg; + u_char mask; + bool_t need_init; + int part; + int slice; + struct diskslice *sp; + struct diskslices *ssp; + int unit; + + need_init = TRUE; + if (*sspp != NULL) { + /* + * XXX reinitialize the slice table unless there is an open + * device on the unit. This should only be done if the media + * has changed. + */ + for (slice = 0, ssp = *sspp; slice < ssp->dss_nslices; slice++) + if (ssp->dss_slices[slice].ds_openmask) { + need_init = FALSE; + break; + } + if (need_init) + for (slice = 0, ssp = *sspp; slice < ssp->dss_nslices; + slice++) { + sp = &ssp->dss_slices[slice]; + if (sp->ds_bad != NULL) { + free(sp->ds_bad, M_DEVBUF); + sp->ds_bad = NULL; + } + if (sp->ds_label != NULL) { + free(sp->ds_label, M_DEVBUF); + sp->ds_label = NULL; + } + } + } + if (need_init) { + printf("dsinit\n"); + error = dsinit(dname, dev, strat, lp, sspp); + if (error != 0) + return (error); + if (setgeom != NULL) { + error = setgeom(lp); + if (error != 0) + /* XXX clean up? */ + return (error); + } + } + ssp = *sspp; + slice = dkslice(dev); + if (slice >= ssp->dss_nslices) + return (ENXIO); + sp = &ssp->dss_slices[slice]; + part = dkpart(dev); + unit = dkunit(dev); + if (/* slice != WHOLE_DISK_SLICE && */ sp->ds_label == NULL) { + struct disklabel *lp1; + + lp1 = malloc(sizeof *lp1, M_DEVBUF, M_WAITOK); + *lp1 = *lp; + lp = lp1; + printf("readdisklabel\n"); + msg = correct_readdisklabel(dkmodpart(dev, RAW_PART), strat, lp); +#if 0 /* XXX */ + if (msg == NULL && setgeom != NULL && setgeom(lp) != 0) + msg = "setgeom failed"; +#endif + if (msg == NULL) + msg = fixlabel(dname, unit, slice, sp, lp); + if (msg != NULL) { + free(lp, M_DEVBUF); + log(LOG_WARNING, "%s%ds%d: cannot find label (%s)\n", + dname, unit, slice, msg); + if (part == RAW_PART) + goto out; + return (EINVAL); /* XXX needs translation */ + } + if (lp->d_flags & D_BADSECT) { + struct dkbad *btp; + + btp = malloc(sizeof *btp, M_DEVBUF, M_WAITOK); + printf("readbad144\n"); + msg = readbad144(dev, strat, lp, btp); + if (msg != NULL) { + log(LOG_WARNING, + "%s%ds%d: cannot find bad sector table (%s)\n", + dname, unit, slice, msg); + free(btp, M_DEVBUF); + free(lp, M_DEVBUF); + if (part == RAW_PART) + goto out; + return (EINVAL); /* XXX needs translation */ + } + sp->ds_bad = internbad144(btp, lp); + free(btp, M_DEVBUF); + if (sp->ds_bad == NULL) { + free(lp, M_DEVBUF); + if (part == RAW_PART) + goto out; + return (EINVAL); /* XXX needs translation */ + } + } + sp->ds_label = lp; + sp->ds_wlabel = TRUE; + } + if (part != RAW_PART + && (sp->ds_label == NULL || part >= sp->ds_label->d_npartitions)) + return (EINVAL); /* XXX needs translation */ +out: + mask = 1 << part; + switch (mode) { + case S_IFBLK: + sp->ds_bopenmask |= mask; + break; + case S_IFCHR: + sp->ds_copenmask |= mask; + break; + } + sp->ds_openmask = sp->ds_bopenmask | sp->ds_copenmask; + return (0); +} + +static char * +fixlabel(dname, unit, slice, sp, lp) + char *dname; + int unit; + int slice; + struct diskslice *sp; + struct disklabel *lp; +{ + bool_t adjust; + int bsdpart; + struct partition *pp; + bool_t warned; + + pp = &lp->d_partitions[LABEL_PART]; + if (pp->p_offset != 0 && pp->p_offset != sp->ds_offset) { + printf( +"%s%ds%d: rejecting BSD label: label partition start != 0 and != slice start\n", + dname, unit, slice); + slice_info(dname, unit, slice, sp); + partition_info(dname, unit, slice, LABEL_PART, pp); + return ("invalid partition table"); + } + if (pp->p_size != sp->ds_size) { + printf("%s%ds%d: label partition size != slice size\n", + dname, unit, slice); + slice_info(dname, unit, slice, sp); + partition_info(dname, unit, slice, LABEL_PART, pp); + if (pp->p_size > sp->ds_size) { + printf("%s%ds%d: truncating label partition\n", + dname, unit, slice); + pp->p_size = sp->ds_size; + } + } + adjust = FALSE; + warned = FALSE; + if (pp->p_offset != 0) { + printf("%s%ds%d: adjusting offsets in BSD label\n", + dname, unit, slice); + slice_info(dname, unit, slice, sp); + partition_info(dname, unit, slice, LABEL_PART, pp); + adjust = TRUE; + warned = TRUE; + } + pp -= LABEL_PART; + for (bsdpart = 0; bsdpart < lp->d_npartitions; bsdpart++, pp++) { + if (adjust && (pp->p_offset != 0 || pp->p_size != 0)) + pp->p_offset -= sp->ds_offset; + if (pp->p_offset + pp->p_size > sp->ds_size + || pp->p_offset + pp->p_size < pp->p_offset) { + printf( +"%s%ds%d: rejecting partition in BSD label: it isn't entirely within the slice\n", + dname, unit, slice); + if (!warned) { + slice_info(dname, unit, slice, sp); + warned = TRUE; + } + if (adjust) + pp->p_offset += sp->ds_offset; + partition_info(dname, unit, slice, bsdpart, pp); + bzero(pp, sizeof *pp); + } + } + + /* XXX TODO: fix general params in *lp? */ + + return (NULL); +} + +static void +partition_info(dname, unit, slice, part, pp) + char *dname; + int unit; + int slice; + int part; + struct partition *pp; +{ + printf("%s%ds%d%c: start %lu, end %lu, size %lu\n", + dname, unit, slice, 'a' + part, + pp->p_offset, pp->p_offset + pp->p_size - 1, pp->p_size); +} + +static void +slice_info(dname, unit, slice, sp) + char *dname; + int unit; + int slice; + struct diskslice *sp; +{ + printf("%s%ds%d: start %lu, end %lu, size %lu\n", + dname, unit, slice, + sp->ds_offset, sp->ds_offset + sp->ds_size - 1, sp->ds_size); +} diff --git a/sys/kern/subr_dkbad.c b/sys/kern/subr_dkbad.c new file mode 100644 index 000000000000..e685abf7e162 --- /dev/null +++ b/sys/kern/subr_dkbad.c @@ -0,0 +1,158 @@ +/*- + * Copyright (c) 1994 Bruce D. Evans. + * All rights reserved. + * + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz. + * + * Copyright (c) 1982, 1986, 1988 Regents of the University of California. + * All rights reserved. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS 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. + * + * from: @(#)wd.c 7.2 (Berkeley) 5/9/91 + * from: wd.c,v 1.55 1994/10/22 01:57:12 phk Exp $ + * from: @(#)ufs_disksubr.c 7.16 (Berkeley) 5/4/91 + * from: ufs_disksubr.c,v 1.8 1994/06/07 01:21:39 phk Exp $ + * $Id: + */ + +#include +#include +#include +#include +#include + +/* + * Internalize the bad sector table. + * TODO: + * o Fix types. + * Type long should be daddr_t since we compare with blkno's. + * Sentinel -1 should be ((daddr_t)-1). + * o Can remove explicit test for sentinel if it is a positive + * (unsigned or not) value larger than all possible blkno's. + * o Check that the table is sorted. + * o Use faster searches. + * o Use the internal table in wddump(). + * o Don't duplicate so much code. + * o Do all bad block handing in a driver-independent file. + * o Remove limit of 126 spare sectors. + */ +struct dkbad_intern * +internbad144(btp, lp) + struct dkbad *btp; + struct disklabel *lp; +{ + struct dkbad_intern *bip; + int i; + + bip = malloc(sizeof *bip, M_DEVBUF, M_WAITOK); + /* + * Spare sectors are allocated beginning with the last sector of + * the second last track of the disk (the last track is used for + * the bad sector list). + */ + bip->bi_maxspare = lp->d_secperunit - lp->d_nsectors - 1; + bip->bi_nbad = DKBAD_MAXBAD; + i = 0; + for (; i < DKBAD_MAXBAD && btp->bt_bad[i].bt_cyl != DKBAD_NOCYL; i++) + bip->bi_bad[i] = btp->bt_bad[i].bt_cyl * lp->d_secpercyl + + (btp->bt_bad[i].bt_trksec >> 8) + * lp->d_nsectors + + (btp->bt_bad[i].bt_trksec & 0x00ff); + bip->bi_bad[i] = -1; + return (bip); +} + +char * +readbad144(dev, strat, lp, bdp) + dev_t dev; + d_strategy_t *strat; + struct disklabel *lp; + struct dkbad *bdp; +{ + struct buf *bp; + struct dkbad *db; + int i; + char *msg; + + bp = geteblk((int)lp->d_secsize); + i = 0; + do { + /* Read a bad sector table. */ + bp->b_dev = dev; + bp->b_blkno = lp->d_secperunit - lp->d_nsectors + i; + if (lp->d_secsize > DEV_BSIZE) + bp->b_blkno *= lp->d_secsize / DEV_BSIZE; + else + bp->b_blkno /= DEV_BSIZE / lp->d_secsize; + bp->b_bcount = lp->d_secsize; + bp->b_flags = B_BUSY | B_READ; + (*strat)(bp); + + /* If successful, validate, otherwise try another. */ + if (biowait(bp) == 0) { + db = (struct dkbad *)(bp->b_un.b_addr); + if (db->bt_mbz == 0 && db->bt_flag == DKBAD_MAGIC) { + msg = NULL; + *bdp = *db; + break; + } + msg = "bad sector table corrupted"; + } else + msg = "bad sector table I/O error"; + } while ((bp->b_flags & B_ERROR) && (i += 2) < 10 && + i < lp->d_nsectors); + bp->b_flags = B_INVAL | B_AGE; + brelse(bp); + return (msg); +} + +daddr_t +transbad144(bip, blkno) + struct dkbad_intern *bip; + daddr_t blkno; +{ + int i; + + /* + * List is sorted, so the search can terminate when it is past our + * sector. + */ + for (i = 0; bip->bi_bad[i] != -1 && bip->bi_bad[i] <= blkno; i++) + if (bip->bi_bad[i] == blkno) + /* + * Spare sectors are allocated in decreasing order. + */ + return (bip->bi_maxspare - i); + return (blkno); +}