summaryrefslogtreecommitdiffstats
path: root/sys/kern/subr_diskslice.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/subr_diskslice.c')
-rw-r--r--sys/kern/subr_diskslice.c997
1 files changed, 997 insertions, 0 deletions
diff --git a/sys/kern/subr_diskslice.c b/sys/kern/subr_diskslice.c
new file mode 100644
index 0000000..ec6099e
--- /dev/null
+++ b/sys/kern/subr_diskslice.c
@@ -0,0 +1,997 @@
+/*-
+ * 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 $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bio.h>
+#include <sys/conf.h>
+#include <sys/disk.h>
+#include <sys/disklabel.h>
+#include <sys/diskslice.h>
+#include <sys/fcntl.h>
+#include <sys/malloc.h>
+#include <sys/stat.h>
+#include <sys/stdint.h>
+#include <sys/syslog.h>
+#include <sys/vnode.h>
+
+#define TRACE(str) do { if (ds_debug) printf str; } while (0)
+
+typedef u_char bool_t;
+
+static volatile bool_t ds_debug;
+
+static struct disklabel *clone_label(struct disklabel *lp);
+static void dsiodone(struct bio *bp);
+static char *fixlabel(char *sname, struct diskslice *sp,
+ struct disklabel *lp, int writeflag);
+static void free_ds_label(struct diskslices *ssp, int slice);
+static void partition_info(char *sname, int part, struct partition *pp);
+static void slice_info(char *sname, struct diskslice *sp);
+static void set_ds_label(struct diskslices *ssp, int slice,
+ struct disklabel *lp);
+static void set_ds_labeldevs(dev_t dev, struct diskslices *ssp);
+static void set_ds_wlabel(struct diskslices *ssp, int slice,
+ int wlabel);
+
+/*
+ * Duplicate a label for the whole disk, and initialize defaults in the
+ * copy for fields that are not already initialized. The caller only
+ * needs to initialize d_secsize and d_secperunit, and zero the fields
+ * that are to be defaulted.
+ */
+static struct disklabel *
+clone_label(lp)
+ struct disklabel *lp;
+{
+ struct disklabel *lp1;
+
+ lp1 = malloc(sizeof *lp1, M_DEVBUF, M_WAITOK);
+ *lp1 = *lp;
+ lp = NULL;
+ if (lp1->d_typename[0] == '\0')
+ strncpy(lp1->d_typename, "amnesiac", sizeof(lp1->d_typename));
+ if (lp1->d_packname[0] == '\0')
+ strncpy(lp1->d_packname, "fictitious", sizeof(lp1->d_packname));
+ if (lp1->d_nsectors == 0)
+ lp1->d_nsectors = 32;
+ if (lp1->d_ntracks == 0)
+ lp1->d_ntracks = 64;
+ lp1->d_secpercyl = lp1->d_nsectors * lp1->d_ntracks;
+ lp1->d_ncylinders = lp1->d_secperunit / lp1->d_secpercyl;
+ if (lp1->d_rpm == 0)
+ lp1->d_rpm = 3600;
+ if (lp1->d_interleave == 0)
+ lp1->d_interleave = 1;
+ if (lp1->d_npartitions < RAW_PART + 1)
+ lp1->d_npartitions = MAXPARTITIONS;
+ if (lp1->d_bbsize == 0)
+ lp1->d_bbsize = BBSIZE;
+ lp1->d_partitions[RAW_PART].p_size = lp1->d_secperunit;
+ lp1->d_magic = DISKMAGIC;
+ lp1->d_magic2 = DISKMAGIC;
+ lp1->d_checksum = dkcksum(lp1);
+ return (lp1);
+}
+
+dev_t
+dkmodpart(dev_t dev, int part)
+{
+ return (makedev(major(dev), (minor(dev) & ~7) | part));
+}
+
+dev_t
+dkmodslice(dev_t dev, int slice)
+{
+ return (makedev(major(dev), (minor(dev) & ~0x1f0000) | (slice << 16)));
+}
+
+u_int
+dkunit(dev_t dev)
+{
+ return (((minor(dev) >> 16) & 0x1e0) | ((minor(dev) >> 3) & 0x1f));
+}
+
+/*
+ * 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 Split buffers that are too big for the device.
+ * o Check for overflow.
+ * o Finish cleaning this up.
+ */
+int
+dscheck(bp, ssp)
+ struct bio *bp;
+ struct diskslices *ssp;
+{
+ daddr_t blkno;
+ daddr_t endsecno;
+ daddr_t labelsect;
+ struct disklabel *lp;
+ char *msg;
+ long nsec;
+ struct partition *pp;
+ daddr_t secno;
+ daddr_t slicerel_secno;
+ struct diskslice *sp;
+
+ blkno = bp->bio_blkno;
+ if (blkno < 0) {
+ printf("dscheck(%s): negative bio_blkno %ld\n",
+ devtoname(bp->bio_dev), (long)blkno);
+ bp->bio_error = EINVAL;
+ goto bad;
+ }
+ sp = &ssp->dss_slices[dkslice(bp->bio_dev)];
+ lp = sp->ds_label;
+ if (ssp->dss_secmult == 1) {
+ if (bp->bio_bcount % (u_long)DEV_BSIZE)
+ goto bad_bcount;
+ secno = blkno;
+ nsec = bp->bio_bcount >> DEV_BSHIFT;
+ } else if (ssp->dss_secshift != -1) {
+ if (bp->bio_bcount & (ssp->dss_secsize - 1))
+ goto bad_bcount;
+ if (blkno & (ssp->dss_secmult - 1))
+ goto bad_blkno;
+ secno = blkno >> ssp->dss_secshift;
+ nsec = bp->bio_bcount >> (DEV_BSHIFT + ssp->dss_secshift);
+ } else {
+ if (bp->bio_bcount % ssp->dss_secsize)
+ goto bad_bcount;
+ if (blkno % ssp->dss_secmult)
+ goto bad_blkno;
+ secno = blkno / ssp->dss_secmult;
+ nsec = bp->bio_bcount / ssp->dss_secsize;
+ }
+ if (lp == NULL) {
+ labelsect = -LABELSECTOR - 1;
+ endsecno = sp->ds_size;
+ slicerel_secno = secno;
+ } else {
+ labelsect = lp->d_partitions[LABEL_PART].p_offset;
+if (labelsect != 0) Debugger("labelsect != 0 in dscheck()");
+ pp = &lp->d_partitions[dkpart(bp->bio_dev)];
+ endsecno = pp->p_size;
+ slicerel_secno = pp->p_offset + secno;
+ }
+
+ /* overwriting disk label ? */
+ /* XXX should also protect bootstrap in first 8K */
+ if (slicerel_secno <= LABELSECTOR + labelsect &&
+#if LABELSECTOR != 0
+ slicerel_secno + nsec > LABELSECTOR + labelsect &&
+#endif
+ (bp->bio_cmd == BIO_WRITE) && sp->ds_wlabel == 0) {
+ bp->bio_error = EROFS;
+ goto bad;
+ }
+
+#if defined(DOSBBSECTOR) && defined(notyet)
+ /* overwriting master boot record? */
+ if (slicerel_secno <= DOSBBSECTOR && (bp->bio_cmd == BIO_WRITE) &&
+ sp->ds_wlabel == 0) {
+ bp->bio_error = EROFS;
+ goto bad;
+ }
+#endif
+
+ /* beyond partition? */
+ if ((uintmax_t)secno + nsec > endsecno) {
+ /* if exactly at end of disk, return an EOF */
+ if (secno == endsecno) {
+ bp->bio_resid = bp->bio_bcount;
+ return (0);
+ }
+ /* or truncate if part of it fits */
+ if (secno > endsecno) {
+ bp->bio_error = EINVAL;
+ goto bad;
+ }
+ bp->bio_bcount = (endsecno - secno) * ssp->dss_secsize;
+ }
+
+ bp->bio_pblkno = sp->ds_offset + slicerel_secno;
+
+ /*
+ * Snoop on label accesses if the slice offset is nonzero. Fudge
+ * offsets in the label to keep the in-core label coherent with
+ * the on-disk one.
+ */
+ if (slicerel_secno <= LABELSECTOR + labelsect
+#if LABELSECTOR != 0
+ && slicerel_secno + nsec > LABELSECTOR + labelsect
+#endif
+ && sp->ds_offset != 0) {
+ struct iodone_chain *ic;
+
+ ic = malloc(sizeof *ic , M_DEVBUF, M_WAITOK);
+ ic->ic_prev_flags = bp->bio_flags;
+ ic->ic_prev_iodone = bp->bio_done;
+ ic->ic_prev_iodone_chain = bp->bio_done_chain;
+ ic->ic_args[0].ia_long = (LABELSECTOR + labelsect -
+ slicerel_secno) * ssp->dss_secsize;
+ ic->ic_args[1].ia_ptr = sp;
+ bp->bio_done = dsiodone;
+ bp->bio_done_chain = ic;
+ if (!(bp->bio_cmd == BIO_READ)) {
+ /*
+ * XXX even disklabel(8) writes directly so we need
+ * to adjust writes. Perhaps we should drop support
+ * for DIOCWLABEL (always write protect labels) and
+ * require the use of DIOCWDINFO.
+ *
+ * XXX probably need to copy the data to avoid even
+ * temporarily corrupting the in-core copy.
+ */
+ /* XXX need name here. */
+ msg = fixlabel((char *)NULL, sp,
+ (struct disklabel *)
+ (bp->bio_data + ic->ic_args[0].ia_long),
+ TRUE);
+ if (msg != NULL) {
+ printf("dscheck(%s): %s\n",
+ devtoname(bp->bio_dev), msg);
+ bp->bio_error = EROFS;
+ goto bad;
+ }
+ }
+ }
+ return (1);
+
+bad_bcount:
+ printf(
+ "dscheck(%s): bio_bcount %ld is not on a sector boundary (ssize %d)\n",
+ devtoname(bp->bio_dev), bp->bio_bcount, ssp->dss_secsize);
+ bp->bio_error = EINVAL;
+ goto bad;
+
+bad_blkno:
+ printf(
+ "dscheck(%s): bio_blkno %ld is not on a sector boundary (ssize %d)\n",
+ devtoname(bp->bio_dev), (long)blkno, ssp->dss_secsize);
+ bp->bio_error = EINVAL;
+ goto bad;
+
+bad:
+ bp->bio_resid = bp->bio_bcount;
+ bp->bio_flags |= BIO_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);
+ sp->ds_openmask &= ~mask;
+}
+
+void
+dsgone(sspp)
+ struct diskslices **sspp;
+{
+ int slice;
+ struct diskslice *sp;
+ struct diskslices *ssp;
+
+ for (slice = 0, ssp = *sspp; slice < ssp->dss_nslices; slice++) {
+ sp = &ssp->dss_slices[slice];
+ free_ds_label(ssp, slice);
+ }
+ free(ssp, M_DEVBUF);
+ *sspp = NULL;
+}
+
+/*
+ * For the "write" commands (DIOCSDINFO and DIOCWDINFO), this
+ * is subject to the same restriction as dsopen().
+ */
+int
+dsioctl(dev, cmd, data, flags, sspp)
+ dev_t dev;
+ u_long cmd;
+ caddr_t data;
+ int flags;
+ struct diskslices **sspp;
+{
+ int error;
+ struct disklabel *lp;
+ int old_wlabel;
+ u_char openmask;
+ int part;
+ int slice;
+ struct diskslice *sp;
+ struct diskslices *ssp;
+ struct partition *pp;
+
+ slice = dkslice(dev);
+ ssp = *sspp;
+ sp = &ssp->dss_slices[slice];
+ lp = sp->ds_label;
+ switch (cmd) {
+
+ case DIOCGDVIRGIN:
+ lp = (struct disklabel *)data;
+ if (ssp->dss_slices[WHOLE_DISK_SLICE].ds_label) {
+ *lp = *ssp->dss_slices[WHOLE_DISK_SLICE].ds_label;
+ } else {
+ bzero(lp, sizeof(struct disklabel));
+ }
+
+ lp->d_magic = DISKMAGIC;
+ lp->d_magic2 = DISKMAGIC;
+ pp = &lp->d_partitions[RAW_PART];
+ pp->p_offset = 0;
+ pp->p_size = sp->ds_size;
+
+ lp->d_npartitions = MAXPARTITIONS;
+ if (lp->d_interleave == 0)
+ lp->d_interleave = 1;
+ if (lp->d_rpm == 0)
+ lp->d_rpm = 3600;
+ if (lp->d_nsectors == 0)
+ lp->d_nsectors = 32;
+ if (lp->d_ntracks == 0)
+ lp->d_ntracks = 64;
+
+ lp->d_bbsize = BBSIZE;
+ lp->d_sbsize = 0;
+ lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
+ lp->d_ncylinders = sp->ds_size / lp->d_secpercyl;
+ lp->d_secperunit = sp->ds_size;
+ lp->d_checksum = 0;
+ lp->d_checksum = dkcksum(lp);
+ return (0);
+
+ case DIOCGDINFO:
+ if (lp == NULL)
+ return (EINVAL);
+ *(struct disklabel *)data = *lp;
+ return (0);
+
+ case DIOCGSECTORSIZE:
+ if (lp == NULL)
+ return (EINVAL);
+ *(u_int *)data = lp->d_secsize;
+ return (0);
+
+ case DIOCGMEDIASIZE:
+ if (lp == NULL)
+ return (EINVAL);
+ *(off_t *)data = (off_t)lp->d_partitions[dkpart(dev)].p_size *
+ lp->d_secsize;
+ return (0);
+
+ case DIOCGSLICEINFO:
+ bcopy(ssp, data, (char *)&ssp->dss_slices[ssp->dss_nslices] -
+ (char *)ssp);
+ return (0);
+
+ case DIOCSDINFO:
+ if (slice == WHOLE_DISK_SLICE)
+ return (ENODEV);
+ if (!(flags & FWRITE))
+ return (EBADF);
+ lp = malloc(sizeof *lp, M_DEVBUF, M_WAITOK);
+ if (sp->ds_label == NULL)
+ bzero(lp, sizeof *lp);
+ else
+ bcopy(sp->ds_label, lp, sizeof *lp);
+ if (sp->ds_label == NULL)
+ openmask = 0;
+ else {
+ openmask = sp->ds_openmask;
+ if (slice == COMPATIBILITY_SLICE)
+ openmask |= ssp->dss_slices[
+ ssp->dss_first_bsd_slice].ds_openmask;
+ else if (slice == ssp->dss_first_bsd_slice)
+ openmask |= ssp->dss_slices[
+ COMPATIBILITY_SLICE].ds_openmask;
+ }
+ error = setdisklabel(lp, (struct disklabel *)data,
+ (u_long)openmask);
+ /* XXX why doesn't setdisklabel() check this? */
+ if (error == 0 && lp->d_partitions[RAW_PART].p_offset != 0)
+ error = EXDEV;
+ if (error == 0) {
+ if (lp->d_secperunit > sp->ds_size)
+ error = ENOSPC;
+ for (part = 0; part < lp->d_npartitions; part++)
+ if (lp->d_partitions[part].p_size > sp->ds_size)
+ error = ENOSPC;
+ }
+ if (error != 0) {
+ free(lp, M_DEVBUF);
+ return (error);
+ }
+ free_ds_label(ssp, slice);
+ set_ds_label(ssp, slice, lp);
+ set_ds_labeldevs(dev, ssp);
+ return (0);
+
+ case DIOCSYNCSLICEINFO:
+ if (slice != WHOLE_DISK_SLICE || dkpart(dev) != RAW_PART)
+ return (EINVAL);
+ if (!*(int *)data)
+ for (slice = 0; slice < ssp->dss_nslices; slice++) {
+ openmask = ssp->dss_slices[slice].ds_openmask;
+ if (openmask
+ && (slice != WHOLE_DISK_SLICE
+ || openmask & ~(1 << RAW_PART)))
+ return (EBUSY);
+ }
+
+ /*
+ * Temporarily forget the current slices struct and read
+ * the current one.
+ * XXX should wait for current accesses on this disk to
+ * complete, then lock out future accesses and opens.
+ */
+ *sspp = NULL;
+ lp = malloc(sizeof *lp, M_DEVBUF, M_WAITOK);
+ *lp = *ssp->dss_slices[WHOLE_DISK_SLICE].ds_label;
+ error = dsopen(dev, S_IFCHR, ssp->dss_oflags, sspp, lp);
+ if (error != 0) {
+ free(lp, M_DEVBUF);
+ *sspp = ssp;
+ return (error);
+ }
+
+ /*
+ * Reopen everything. This is a no-op except in the "force"
+ * case and when the raw bdev and cdev are both open. Abort
+ * if anything fails.
+ */
+ for (slice = 0; slice < ssp->dss_nslices; slice++) {
+ for (openmask = ssp->dss_slices[slice].ds_openmask,
+ part = 0; openmask; openmask >>= 1, part++) {
+ if (!(openmask & 1))
+ continue;
+ error = dsopen(dkmodslice(dkmodpart(dev, part),
+ slice),
+ S_IFCHR, ssp->dss_oflags, sspp,
+ lp);
+ if (error != 0) {
+ free(lp, M_DEVBUF);
+ *sspp = ssp;
+ return (EBUSY);
+ }
+ }
+ }
+
+ free(lp, M_DEVBUF);
+ dsgone(&ssp);
+ return (0);
+
+ case DIOCWDINFO:
+ error = dsioctl(dev, DIOCSDINFO, data, flags, &ssp);
+ 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;
+ set_ds_wlabel(ssp, slice, TRUE);
+ error = writedisklabel(dev, sp->ds_label);
+ /* XXX should invalidate in-core label if write failed. */
+ set_ds_wlabel(ssp, slice, old_wlabel);
+ return (error);
+
+ case DIOCWLABEL:
+#ifndef __alpha__
+ if (slice == WHOLE_DISK_SLICE)
+ return (ENODEV);
+#endif
+ if (!(flags & FWRITE))
+ return (EBADF);
+ set_ds_wlabel(ssp, slice, *(int *)data != 0);
+ return (0);
+
+ default:
+ return (ENOIOCTL);
+ }
+}
+
+static void
+dsiodone(bp)
+ struct bio *bp;
+{
+ struct iodone_chain *ic;
+ char *msg;
+
+ ic = bp->bio_done_chain;
+ bp->bio_done = ic->ic_prev_iodone;
+ bp->bio_done_chain = ic->ic_prev_iodone_chain;
+ if (!(bp->bio_cmd == BIO_READ)
+ || (!(bp->bio_flags & BIO_ERROR) && bp->bio_error == 0)) {
+ msg = fixlabel((char *)NULL, ic->ic_args[1].ia_ptr,
+ (struct disklabel *)
+ (bp->bio_data + ic->ic_args[0].ia_long),
+ FALSE);
+ if (msg != NULL)
+ printf("%s\n", msg);
+ }
+ free(ic, M_DEVBUF);
+ biodone(bp);
+}
+
+int
+dsisopen(ssp)
+ struct diskslices *ssp;
+{
+ int slice;
+
+ if (ssp == NULL)
+ return (0);
+ for (slice = 0; slice < ssp->dss_nslices; slice++)
+ if (ssp->dss_slices[slice].ds_openmask)
+ return (1);
+ return (0);
+}
+
+/*
+ * Allocate a slices "struct" and initialize it to contain only an empty
+ * compatibility slice (pointing to itself), a whole disk slice (covering
+ * the disk as described by the label), and (nslices - BASE_SLICES) empty
+ * slices beginning at BASE_SLICE.
+ */
+struct diskslices *
+dsmakeslicestruct(nslices, lp)
+ int nslices;
+ struct disklabel *lp;
+{
+ struct diskslice *sp;
+ struct diskslices *ssp;
+
+ ssp = malloc(offsetof(struct diskslices, dss_slices) +
+ nslices * sizeof *sp, M_DEVBUF, M_WAITOK);
+ ssp->dss_first_bsd_slice = COMPATIBILITY_SLICE;
+ ssp->dss_nslices = nslices;
+ ssp->dss_oflags = 0;
+ ssp->dss_secmult = lp->d_secsize / DEV_BSIZE;
+ if (ssp->dss_secmult & (ssp->dss_secmult - 1))
+ ssp->dss_secshift = -1;
+ else
+ ssp->dss_secshift = ffs(ssp->dss_secmult) - 1;
+ ssp->dss_secsize = lp->d_secsize;
+ sp = &ssp->dss_slices[0];
+ bzero(sp, nslices * sizeof *sp);
+ sp[WHOLE_DISK_SLICE].ds_size = lp->d_secperunit;
+ return (ssp);
+}
+
+char *
+dsname(dev, unit, slice, part, partname)
+ dev_t dev;
+ int unit;
+ int slice;
+ int part;
+ char *partname;
+{
+ static char name[32];
+ const char *dname;
+
+ dname = devsw(dev)->d_name;
+ if (strlen(dname) > 16)
+ dname = "nametoolong";
+ snprintf(name, sizeof(name), "%s%d", dname, unit);
+ partname[0] = '\0';
+ if (slice != WHOLE_DISK_SLICE || part != RAW_PART) {
+ partname[0] = 'a' + part;
+ partname[1] = '\0';
+ if (slice != COMPATIBILITY_SLICE)
+ snprintf(name + strlen(name),
+ sizeof(name) - strlen(name), "s%d", slice - 1);
+ }
+ return (name);
+}
+
+/*
+ * 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(dev, mode, flags, sspp, lp)
+ dev_t dev;
+ int mode;
+ u_int flags;
+ struct diskslices **sspp;
+ struct disklabel *lp;
+{
+ dev_t dev1;
+ int error;
+ struct disklabel *lp1;
+ char *msg;
+ u_char mask;
+ int part;
+ char partname[2];
+ int slice;
+ char *sname;
+ struct diskslice *sp;
+ struct diskslices *ssp;
+ int unit;
+
+ dev->si_bsize_phys = lp->d_secsize;
+
+ unit = dkunit(dev);
+ if (lp->d_secsize % DEV_BSIZE) {
+ printf("%s: invalid sector size %lu\n", devtoname(dev),
+ (u_long)lp->d_secsize);
+ return (EINVAL);
+ }
+
+ /*
+ * XXX reinitialize the slice table unless there is an open device
+ * on the unit. This should only be done if the media has changed.
+ */
+ ssp = *sspp;
+ if (!dsisopen(ssp)) {
+ if (ssp != NULL)
+ dsgone(sspp);
+ /*
+ * Allocate a minimal slices "struct". This will become
+ * the final slices "struct" if we don't want real slices
+ * or if we can't find any real slices.
+ */
+ *sspp = dsmakeslicestruct(BASE_SLICE, lp);
+
+ if (!(flags & DSO_ONESLICE)) {
+ TRACE(("dsinit\n"));
+ error = dsinit(dev, lp, sspp);
+ if (error != 0) {
+ dsgone(sspp);
+ return (error);
+ }
+ }
+ ssp = *sspp;
+ ssp->dss_oflags = flags;
+
+ /*
+ * If there are no real slices, then make the compatiblity
+ * slice cover the whole disk.
+ */
+ if (ssp->dss_nslices == BASE_SLICE)
+ ssp->dss_slices[COMPATIBILITY_SLICE].ds_size
+ = lp->d_secperunit;
+
+ /* Point the compatibility slice at the BSD slice, if any. */
+ for (slice = BASE_SLICE; slice < ssp->dss_nslices; slice++) {
+ sp = &ssp->dss_slices[slice];
+ if (sp->ds_type == DOSPTYP_386BSD /* XXX */) {
+ ssp->dss_first_bsd_slice = slice;
+ ssp->dss_slices[COMPATIBILITY_SLICE].ds_offset
+ = sp->ds_offset;
+ ssp->dss_slices[COMPATIBILITY_SLICE].ds_size
+ = sp->ds_size;
+ ssp->dss_slices[COMPATIBILITY_SLICE].ds_type
+ = sp->ds_type;
+ break;
+ }
+ }
+
+ ssp->dss_slices[WHOLE_DISK_SLICE].ds_label = clone_label(lp);
+ ssp->dss_slices[WHOLE_DISK_SLICE].ds_wlabel = TRUE;
+ }
+
+ /* Initialize secondary info for all slices. */
+ for (slice = 0; slice < ssp->dss_nslices; slice++) {
+ sp = &ssp->dss_slices[slice];
+ if (sp->ds_label != NULL
+#ifdef __alpha__
+ && slice != WHOLE_DISK_SLICE
+#endif
+ )
+ continue;
+ dev1 = dkmodslice(dkmodpart(dev, RAW_PART), slice);
+#if 0
+ sname = dsname(dev, unit, slice, RAW_PART, partname);
+#else
+ *partname='\0';
+ sname = dev1->si_name;
+#endif
+ /*
+ * XXX this should probably only be done for the need_init
+ * case, but there may be a problem with DIOCSYNCSLICEINFO.
+ */
+ set_ds_wlabel(ssp, slice, TRUE); /* XXX invert */
+ lp1 = clone_label(lp);
+ TRACE(("readdisklabel\n"));
+ if (flags & DSO_NOLABELS)
+ msg = NULL;
+ else {
+ msg = readdisklabel(dev1, lp1);
+
+ /*
+ * readdisklabel() returns NULL for success, and an
+ * error string for failure.
+ *
+ * If there isn't a label on the disk, and if the
+ * DSO_COMPATLABEL is set, we want to use the
+ * faked-up label provided by the caller.
+ *
+ * So we set msg to NULL to indicate that there is
+ * no failure (since we have a faked-up label),
+ * free lp1, and then clone it again from lp.
+ * (In case readdisklabel() modified lp1.)
+ */
+ if (msg != NULL && (flags & DSO_COMPATLABEL)) {
+ msg = NULL;
+ free(lp1, M_DEVBUF);
+ lp1 = clone_label(lp);
+ }
+ }
+ if (msg == NULL)
+ msg = fixlabel(sname, sp, lp1, FALSE);
+ if (msg == NULL && lp1->d_secsize != ssp->dss_secsize)
+ msg = "inconsistent sector size";
+ if (msg != NULL) {
+ if (sp->ds_type == DOSPTYP_386BSD /* XXX */)
+ log(LOG_WARNING, "%s: cannot find label (%s)\n",
+ sname, msg);
+ free(lp1, M_DEVBUF);
+ continue;
+ }
+ if (lp1->d_flags & D_BADSECT) {
+ log(LOG_ERR, "%s: bad sector table not supported\n",
+ sname);
+ free(lp1, M_DEVBUF);
+ continue;
+ }
+ set_ds_label(ssp, slice, lp1);
+ set_ds_labeldevs(dev1, ssp);
+ set_ds_wlabel(ssp, slice, FALSE);
+ }
+
+ slice = dkslice(dev);
+ if (slice >= ssp->dss_nslices)
+ return (ENXIO);
+ sp = &ssp->dss_slices[slice];
+ part = dkpart(dev);
+ if (part != RAW_PART
+ && (sp->ds_label == NULL || part >= sp->ds_label->d_npartitions))
+ return (EINVAL); /* XXX needs translation */
+ mask = 1 << part;
+ sp->ds_openmask |= mask;
+ return (0);
+}
+
+int
+dssize(dev, sspp)
+ dev_t dev;
+ struct diskslices **sspp;
+{
+ struct disklabel *lp;
+ int part;
+ int slice;
+ struct diskslices *ssp;
+
+ slice = dkslice(dev);
+ part = dkpart(dev);
+ ssp = *sspp;
+ if (ssp == NULL || slice >= ssp->dss_nslices
+ || !(ssp->dss_slices[slice].ds_openmask & (1 << part))) {
+ if (devsw(dev)->d_open(dev, FREAD, S_IFCHR,
+ (struct thread *)NULL) != 0)
+ return (-1);
+ devsw(dev)->d_close(dev, FREAD, S_IFCHR, (struct thread *)NULL);
+ ssp = *sspp;
+ }
+ lp = ssp->dss_slices[slice].ds_label;
+ if (lp == NULL)
+ return (-1);
+ return ((int)lp->d_partitions[part].p_size);
+}
+
+static void
+free_ds_label(ssp, slice)
+ struct diskslices *ssp;
+ int slice;
+{
+ struct disklabel *lp;
+ struct diskslice *sp;
+
+ sp = &ssp->dss_slices[slice];
+ lp = sp->ds_label;
+ if (lp == NULL)
+ return;
+ free(lp, M_DEVBUF);
+ set_ds_label(ssp, slice, (struct disklabel *)NULL);
+}
+
+
+static char *
+fixlabel(sname, sp, lp, writeflag)
+ char *sname;
+ struct diskslice *sp;
+ struct disklabel *lp;
+ int writeflag;
+{
+ u_long end;
+ u_long offset;
+ int part;
+ struct partition *pp;
+ u_long start;
+ bool_t warned;
+
+ /* These errors "can't happen" so don't bother reporting details. */
+ if (lp->d_magic != DISKMAGIC || lp->d_magic2 != DISKMAGIC)
+ return ("fixlabel: invalid magic");
+ if (dkcksum(lp) != 0)
+ return ("fixlabel: invalid checksum");
+
+ pp = &lp->d_partitions[RAW_PART];
+ if (writeflag) {
+ start = 0;
+ offset = sp->ds_offset;
+ } else {
+ start = sp->ds_offset;
+ offset = -sp->ds_offset;
+ }
+ if (pp->p_offset != start) {
+ if (sname != NULL) {
+ printf(
+"%s: rejecting BSD label: raw partition offset != slice offset\n",
+ sname);
+ slice_info(sname, sp);
+ partition_info(sname, RAW_PART, pp);
+ }
+ return ("fixlabel: raw partition offset != slice offset");
+ }
+ if (pp->p_size != sp->ds_size) {
+ if (sname != NULL) {
+ printf("%s: raw partition size != slice size\n", sname);
+ slice_info(sname, sp);
+ partition_info(sname, RAW_PART, pp);
+ }
+ if (pp->p_size > sp->ds_size) {
+ if (sname == NULL)
+ return ("fixlabel: raw partition size > slice size");
+ printf("%s: truncating raw partition\n", sname);
+ pp->p_size = sp->ds_size;
+ }
+ }
+ end = start + sp->ds_size;
+ if (start > end)
+ return ("fixlabel: slice wraps");
+ if (lp->d_secpercyl <= 0)
+ return ("fixlabel: d_secpercyl <= 0");
+ pp -= RAW_PART;
+ warned = FALSE;
+ for (part = 0; part < lp->d_npartitions; part++, pp++) {
+ if (pp->p_offset != 0 || pp->p_size != 0) {
+ if (pp->p_offset < start
+ || pp->p_offset + pp->p_size > end
+ || pp->p_offset + pp->p_size < pp->p_offset) {
+ if (sname != NULL) {
+ printf(
+"%s: rejecting partition in BSD label: it isn't entirely within the slice\n",
+ sname);
+ if (!warned) {
+ slice_info(sname, sp);
+ warned = TRUE;
+ }
+ partition_info(sname, part, pp);
+ }
+ /* XXX else silently discard junk. */
+ bzero(pp, sizeof *pp);
+ } else
+ pp->p_offset += offset;
+ }
+ }
+ lp->d_ncylinders = sp->ds_size / lp->d_secpercyl;
+ lp->d_secperunit = sp->ds_size;
+ lp->d_checksum = 0;
+ lp->d_checksum = dkcksum(lp);
+ return (NULL);
+}
+
+static void
+partition_info(sname, part, pp)
+ char *sname;
+ int part;
+ struct partition *pp;
+{
+ printf("%s%c: start %lu, end %lu, size %lu\n", sname, 'a' + part,
+ (u_long)pp->p_offset, (u_long)(pp->p_offset + pp->p_size - 1),
+ (u_long)pp->p_size);
+}
+
+static void
+slice_info(sname, sp)
+ char *sname;
+ struct diskslice *sp;
+{
+ printf("%s: start %lu, end %lu, size %lu\n", sname,
+ sp->ds_offset, sp->ds_offset + sp->ds_size - 1, sp->ds_size);
+}
+
+static void
+set_ds_label(ssp, slice, lp)
+ struct diskslices *ssp;
+ int slice;
+ struct disklabel *lp;
+{
+ ssp->dss_slices[slice].ds_label = lp;
+ if (slice == COMPATIBILITY_SLICE)
+ ssp->dss_slices[ssp->dss_first_bsd_slice].ds_label = lp;
+ else if (slice == ssp->dss_first_bsd_slice)
+ ssp->dss_slices[COMPATIBILITY_SLICE].ds_label = lp;
+}
+
+static void
+set_ds_labeldevs(dev, ssp)
+ dev_t dev;
+ struct diskslices *ssp;
+{
+}
+
+
+static void
+set_ds_wlabel(ssp, slice, wlabel)
+ struct diskslices *ssp;
+ int slice;
+ int wlabel;
+{
+ ssp->dss_slices[slice].ds_wlabel = wlabel;
+ if (slice == COMPATIBILITY_SLICE)
+ ssp->dss_slices[ssp->dss_first_bsd_slice].ds_wlabel = wlabel;
+ else if (slice == ssp->dss_first_bsd_slice)
+ ssp->dss_slices[COMPATIBILITY_SLICE].ds_wlabel = wlabel;
+}
OpenPOWER on IntegriCloud