/* $OpenBSD: disksubr.c,v 1.3 2004/03/17 14:16:04 miod Exp $ */ /* $NetBSD: disksubr.c,v 1.21 1996/05/03 19:42:03 christos Exp $ */ /* * Copyright (c) 1996 Theo de Raadt * 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. * * @(#)ufs_disksubr.c 7.16 (Berkeley) 5/4/91 */ #include #include #include #include #include #include #include #define b_cylin b_resid void dk_establish(struct disk *dk, struct device *dev) { } int try_rdb_label(dev_t dev, void (*strat)(struct buf *), struct buf *bp, struct disklabel *lp, struct cpu_disklabel *osdep, char **pmsg, int *bsdpartoff); int try_hfs_label(dev_t dev, void (*strat)(struct buf *), struct buf *bp, struct disklabel *lp, struct cpu_disklabel *osdep, char **pmsg, int *bsdpartoff); int try_mbr_label(dev_t dev, void (*strat)(struct buf *), struct buf *bp, struct disklabel *lp, struct cpu_disklabel *osdep, char **pmsg, int *bsdpartoff); /* * Attempt to read a disk label from a device * using the indicated strategy routine. * The label must be partly set up before this: * secpercyl, secsize and anything required for a block i/o read * operation in the driver's strategy/start routines * must be filled in before calling us. * * Returns null on success and an error string on failure. */ char * readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *osdep, int spoofonly) { struct dkbad *bdp = &DKBAD(osdep); struct buf *bp; struct disklabel *dlp; char *msg = NULL; int partoff, i, found; /* minimal requirements for archtypal disk label */ if (lp->d_secsize < DEV_BSIZE) lp->d_secsize = DEV_BSIZE; if (lp->d_secperunit == 0) lp->d_secperunit = 0x1fffffff; lp->d_npartitions = RAW_PART + 1; for (i = 0; i < RAW_PART; i++) { lp->d_partitions[i].p_size = 0; lp->d_partitions[i].p_offset = 0; } if (lp->d_partitions[i].p_size == 0) lp->d_partitions[i].p_size = 0x1fffffff; lp->d_partitions[i].p_offset = 0; /* get a buffer and initialize it */ bp = geteblk((int)lp->d_secsize); bp->b_dev = dev; partoff = -1; /* try hfs */ found = try_hfs_label(dev, strat, bp, lp, osdep, &msg, &partoff); /* if no hfs or mbr, try rdb */ if (found == 0) found = try_rdb_label(dev, strat, bp, lp, osdep, &msg, &partoff); /* if no hfs, try mbr */ if (found == 0) found = try_mbr_label(dev, strat, bp, lp, osdep, &msg, &partoff); /* if no partition found, return */ if (found == 0 || partoff == -1) { /* no special partition table found try raw labeled disk. */ partoff = LABELSECTOR; } /* don't read the on-disk label if we are in spoofed-only mode */ if (spoofonly) goto done; /* next, dig out disk label */ bp->b_blkno = partoff; bp->b_cylin = partoff/lp->d_secpercyl; /* XXX */ bp->b_bcount = lp->d_secsize; bp->b_flags = B_BUSY | B_READ; (*strat)(bp); /* if successful, locate disk label within block and validate */ if (biowait(bp)) { /* XXX we return the faked label built so far */ msg = "disk label I/O error"; goto done; } for (dlp = (struct disklabel *)bp->b_data; dlp <= (struct disklabel *)(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) { #if defined(CD9660) if (iso_disklabelspoof(dev, strat, lp) == 0) msg = NULL; #endif goto done; } /* obtain bad sector table if requested and present */ if (bdp && (lp->d_flags & D_BADSECT)) { struct dkbad *db; i = 0; do { /* read a bad sector table */ bp->b_flags = B_BUSY | 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_cylin = 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_flags & B_ERROR) && (i += 2) < 10 && i < lp->d_nsectors); } done: bp->b_flags |= B_INVAL; brelse(bp); 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 *osdep) { 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); /* special case to allow disklabel to be invalidated */ if (nlp->d_magic == 0xffffffff) { *olp = *nlp; return (0); } if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC || dkcksum(nlp) != 0) return (EINVAL); /* XXX missing check if other dos partitions will be overwritten */ while (openmask != 0) { i = ffs(openmask) - 1; 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. * XXX cannot handle OpenBSD partitions in extended partitions! */ int writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *osdep) { struct dos_partition *dp = osdep->dosparts, *dp2; struct buf *bp; struct disklabel *dlp; int error, dospartoff, cyl, i; int ourpart = -1; /* get a buffer and initialize it */ bp = geteblk((int)lp->d_secsize); bp->b_dev = dev; /* try DPME partition */ if (osdep->macparts[0].pmSig == PART_ENTRY_MAGIC) { /* only write if a valid "OpenBSD" partition type exists */ if (osdep->macparts[1].pmSig == PART_ENTRY_MAGIC) { bp->b_blkno = osdep->macparts[1].pmPyPartStart; bp->b_cylin = bp->b_blkno/lp->d_secpercyl; bp->b_bcount = lp->d_secsize; bp->b_flags = B_BUSY | B_WRITE; *(struct disklabel *)bp->b_data = *lp; (*strat)(bp); error = biowait(bp); goto done; } /* SHOULD FAIL TO WRITE LABEL IF VALID HFS partition exists * and no OpenBSD partition exists */ error = 1; /* EPERM? */ goto done; } /* label RDB disk */ if (osdep->rd_bsdlbl != 0) { /* we have a location for the BSD label */ /* Really need to add some validation code here */ bp->b_blkno = osdep->rd_bsdlbl; bp->b_cylin = bp->b_blkno/lp->d_secpercyl; bp->b_bcount = lp->d_secsize; bp->b_flags = B_BUSY | B_WRITE; *(struct disklabel *)bp->b_data = *lp; (*strat)(bp); error = biowait(bp); goto done; } /* do dos partitions in the process of getting disklabel? */ dospartoff = 0; cyl = LABELSECTOR / lp->d_secpercyl; if (dp) { /* read master boot record */ bp->b_blkno = DOSBBSECTOR; bp->b_bcount = lp->d_secsize; bp->b_flags = B_BUSY | B_READ; bp->b_cylin = DOSBBSECTOR / lp->d_secpercyl; (*strat)(bp); if ((error = biowait(bp)) != 0) goto done; /* XXX how do we check veracity/bounds of this? */ bcopy(bp->b_data + DOSPARTOFF, dp, NDOSPART * sizeof(*dp)); for (dp2=dp, i=0; i < NDOSPART && ourpart == -1; i++, dp2++) if (get_le(&dp2->dp_size) && dp2->dp_typ == DOSPTYP_OPENBSD) ourpart = i; for (dp2=dp, i=0; i < NDOSPART && ourpart == -1; i++, dp2++) if (get_le(&dp2->dp_size) && dp2->dp_typ == DOSPTYP_FREEBSD) ourpart = i; for (dp2=dp, i=0; i < NDOSPART && ourpart == -1; i++, dp2++) if (get_le(&dp2->dp_size) && dp2->dp_typ == DOSPTYP_NETBSD) ourpart = i; if (ourpart != -1) { dp2 = &dp[ourpart]; /* * need sector address for SCSI/IDE, * cylinder for ESDI/ST506/RLL */ dospartoff = get_le(&dp2->dp_start); cyl = DPCYL(dp2->dp_scyl, dp2->dp_ssect); } } /* next, dig out disk label */ bp->b_blkno = dospartoff + LABELSECTOR; bp->b_cylin = cyl; bp->b_bcount = lp->d_secsize; bp->b_flags = B_BUSY | B_READ; (*strat)(bp); /* if successful, locate disk label within block and validate */ if ((error = biowait(bp)) != 0) goto done; for (dlp = (struct disklabel *)bp->b_data; dlp <= (struct disklabel *)(bp->b_data + lp->d_secsize - sizeof(*dlp)); dlp = (struct disklabel *)((char *)dlp + sizeof(long))) { if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC && dkcksum(dlp) == 0) { *dlp = *lp; bp->b_flags = B_BUSY | B_WRITE; (*strat)(bp); error = biowait(bp); goto done; } } /* Write it in the regular place. */ *(struct disklabel *)bp->b_data = *lp; bp->b_flags = B_BUSY | B_WRITE; (*strat)(bp); error = biowait(bp); goto done; done: bp->b_flags |= B_INVAL; brelse(bp); return (error); } /* * 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. */ int bounds_check_with_label(struct buf *bp, struct disklabel *lp, struct cpu_disklabel *osdep, int wlabel) { #define blockpersec(count, lp) ((count) * (((lp)->d_secsize) / DEV_BSIZE)) struct partition *p = lp->d_partitions + DISKPART(bp->b_dev); int labelsector = blockpersec(lp->d_partitions[RAW_PART].p_offset, lp) + LABELSECTOR; int sz = howmany(bp->b_bcount, DEV_BSIZE); /* avoid division by zero */ if (lp->d_secpercyl == 0) { bp->b_error = EINVAL; goto bad; } if (bp->b_blkno + sz > blockpersec(p->p_size, lp)) { sz = blockpersec(p->p_size, lp) - bp->b_blkno; if (sz == 0) { /* If exactly at end of disk, return EOF. */ bp->b_resid = bp->b_bcount; goto done; } if (sz < 0) { /* If past end of disk, return EINVAL. */ bp->b_error = EINVAL; goto bad; } /* Otherwise, truncate request. */ bp->b_bcount = sz << DEV_BSHIFT; } /* Overwriting disk label? */ if (bp->b_blkno + blockpersec(p->p_offset, lp) <= labelsector && #if LABELSECTOR != 0 bp->b_blkno + blockpersec(p->p_offset, lp) + sz > labelsector && #endif (bp->b_flags & B_READ) == 0 && !wlabel) { bp->b_error = EROFS; goto bad; } /* calculate cylinder for disksort to order transfers with */ bp->b_cylin = (bp->b_blkno + blockpersec(p->p_offset, lp)) / lp->d_secpercyl; return (1); bad: bp->b_flags |= B_ERROR; done: return (0); }