/* $NetBSD: disksubr.c,v 1.17 2008/01/02 11:48:24 ad Exp $ */ /* * 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. 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. */ #include __KERNEL_RCSID(0, "$NetBSD: disksubr.c,v 1.17 2008/01/02 11:48:24 ad Exp $"); #include #include #include #include #include /* * Attempt to read a disk label from a device * using the indicated strategy routine. * The label must be partly set up before this: * secpercyl and anything required in the strategy routine * (e.g., sector size) must be filled in before calling us. * Returns null on success and an error string on failure. */ const char * readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *clp) { struct buf *bp; struct disklabel *dlp; struct dkbad *bdp; const char *msg = NULL; uint32_t secperunit; int i; /* minimal requirements for archtypal disk label */ if (lp->d_secsize == 0) lp->d_secsize = DEV_BSIZE; if (lp->d_secperunit == 0) lp->d_secperunit = 0x1fffffff; lp->d_npartitions = RAW_PART + 1; if (lp->d_partitions[RAW_PART].p_size == 0) lp->d_partitions[RAW_PART].p_size = lp->d_secperunit; lp->d_partitions[RAW_PART].p_offset = 0; secperunit = lp->d_secperunit; /* obtain buffer to probe drive with */ bp = geteblk((int)lp->d_secsize); bp->b_dev = dev; /* Next, dig out disk label. If successful, locate disk * label within block and validate. */ if (disk_read_sectors(strat, lp, bp, LABELSECTOR, 1) != 0) { msg = "disk label read error"; goto done; } for (dlp = (struct disklabel *)bp->b_data; dlp <= (struct disklabel *)((char *)bp->b_data + lp->d_secsize - sizeof(*dlp)); dlp = (struct disklabel *)((char *)dlp + sizeof(long))) { if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC) { if (msg == NULL) msg = "no disk label"; } else if (dlp->d_npartitions > MAXPARTITIONS || dkcksum(dlp) != 0) msg = "disk label corrupted"; else { *lp = *dlp; msg = NULL; break; } } if (msg) goto done; if ((msg = convertdisklabel(lp, strat, bp, secperunit)) != NULL) goto done; /* obtain bad sector table if requested and present */ if (clp && (bdp = &clp->bad) != NULL && (lp->d_flags & D_BADSECT)) { struct dkbad *db; i = 0; do { /* read a bad sector table */ bp->b_oflags &= ~(BO_DONE); bp->b_flags |= B_READ; 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_cylinder = lp->d_ncylinders - 1; (*strat)(bp); /* if successful, validate, otherwise try another */ if (biowait(bp)) { msg = "bad sector table I/O error"; } else { db = (struct dkbad *)(bp->b_data); #define DKBAD_MAGIC 0x4321 if (db->bt_mbz == 0 && db->bt_flag == DKBAD_MAGIC) { msg = NULL; *bdp = *db; break; } else msg = "bad sector table corrupted"; } } while (bp->b_error != 0 && (i += 2) < 10 && i < lp->d_nsectors); } done: brelse(bp, 0); return (msg); } /* * Check new disk label for sensibility before setting it. */ int setdisklabel(struct disklabel *olp, struct disklabel *nlp, u_long openmask, struct cpu_disklabel *clp) { int i; struct partition *opp, *npp; /* sanity clause */ if (nlp->d_secpercyl == 0 || nlp->d_secsize == 0 || (nlp->d_secsize % DEV_BSIZE) != 0) return(EINVAL); if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC || dkcksum(nlp) != 0) return (EINVAL); while ((i = ffs(openmask)) != 0) { i--; openmask &= ~(1 << i); if (nlp->d_npartitions <= i) return (EBUSY); opp = &olp->d_partitions[i]; npp = &nlp->d_partitions[i]; if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size) return (EBUSY); /* * Copy internally-set partition information * if new label doesn't include it. XXX */ if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) { npp->p_fstype = opp->p_fstype; npp->p_fsize = opp->p_fsize; npp->p_frag = opp->p_frag; npp->p_cpg = opp->p_cpg; } } nlp->d_checksum = 0; nlp->d_checksum = dkcksum(nlp); *olp = *nlp; return (0); } /* * Write disk label back to device after modification. * this means write out the Rigid disk blocks to represent the * label. Hope the user was carefull. */ int writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *clp) { struct buf *bp; struct disklabel *dlp; int error = 0; bp = geteblk((int)lp->d_secsize); bp->b_dev = dev; bp->b_blkno = LABELSECTOR; bp->b_cylinder = 0; bp->b_bcount = lp->d_secsize; bp->b_flags |= B_READ; /* get current label */ (*strat)(bp); if ((error = biowait(bp)) != 0) goto done; dlp = (struct disklabel *)((char *)bp->b_data + LABELOFFSET); *dlp = *lp; /* struct assignment */ bp->b_oflags &= ~(BO_DONE); bp->b_flags &= ~(B_READ); bp->b_flags |= B_WRITE; (*strat)(bp); error = biowait(bp); done: brelse(bp, 0); return (error); }