root/sys/arch/macppc/macppc/disksubr.c
/*      $NetBSD: disksubr.c,v 1.50 2022/08/13 09:34:47 martin 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.
 *
 *      @(#)ufs_disksubr.c      7.16 (Berkeley) 5/4/91
 */
/*-
 * Copyright (C) 1993   Allen K. Briggs, Chris P. Caputo,
 *                      Michael L. Finch, Bradley A. Grantham, and
 *                      Lawrence A. Kesteloot
 * 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 Alice Group.
 * 4. The names of the Alice Group or any of its members may not be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE ALICE GROUP ``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 ALICE GROUP 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.
 *
 */
/*
 * Copyright (C) 1996 Wolfgang Solfrank.
 * Copyright (C) 1996 TooLs GmbH.
 * 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 TooLs GmbH.
 * 4. The name of TooLs GmbH may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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.
 */

/* rewritten, 2-5-93 MLF */
/* its alot cleaner now, and adding support for new partition types
 * is possible without causing serious brain-damage
 * known bugs:
 * 1) when only an HFS_PART part exists on a drive it gets assigned to "B"
 * this is because of line 623 of sd.c, I think this line should go.
 * 2) /sbin/disklabel expects the whole disk to be in "D", we put it in
 * "C" (I think) and we don't set that position in the disklabel structure
 * as used.  Again, not my fault.
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: disksubr.c,v 1.50 2022/08/13 09:34:47 martin Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/disk.h>
#include <sys/disklabel.h>
#include <sys/bootblock.h>
#include <sys/syslog.h>

#include <sys/bswap.h>

#define NUM_PARTS 32

#define ROOT_PART 1
#define UFS_PART 2
#define SWAP_PART 3
#define HFS_PART 4
#define SCRATCH_PART 5

static int getFreeLabelEntry(struct disklabel *);
static int whichType(struct part_map_entry *, u_int8_t *, int *);
static void setpartition(struct part_map_entry *,
                struct partition *, int);
static int getNamedType(struct part_map_entry *, int,
                struct disklabel *, int, int, int *);
static const char *read_mac_label(dev_t, void (*)(struct buf *),
                struct disklabel *, struct cpu_disklabel *);
static const char *read_dos_label(dev_t, void (*)(struct buf *),
                struct disklabel *, struct cpu_disklabel *);
static const char *read_bsd_label(dev_t, void (*)(struct buf *),
                struct disklabel *, struct cpu_disklabel *);
static int get_netbsd_label(dev_t, void (*)(struct buf *),
                struct disklabel *, struct cpu_disklabel *);

/*
 * Find an entry in the disk label that is unused and return it
 * or -1 if no entry
 */
static int
getFreeLabelEntry(struct disklabel *lp)
{
        int i = 0;

        for (i = 0; i < MAXPARTITIONS; i++) {
                if ((i != RAW_PART)
                    && (lp->d_partitions[i].p_fstype == FS_UNUSED))
                        return i;
        }

        return -1;
}

/*
 * figure out what the type of the given part is and return it
 */
static int
whichType(struct part_map_entry *part, u_int8_t *fstype, int *clust)
{
        struct blockzeroblock *bzb;
        char typestr[32], *s;
        int type;

        /* Set default unix partition type. Certain partition types can
         * specify a different partition type. */
        *fstype = FS_OTHER;
        *clust = 0;     /* only A/UX partitions not in cluster 0 */

        if (part->pmSig != PART_ENTRY_MAGIC || part->pmPartType[0] == '\0')
                return 0;

        strncpy(typestr, (char *)part->pmPartType, sizeof(typestr));
        typestr[sizeof(typestr) - 1] = '\0';
        for (s = typestr; *s; s++)
                if ((*s >= 'a') && (*s <= 'z'))
                        *s = (*s - 'a' + 'A');

        if (strncmp(PART_TYPE_DRIVER, typestr, strlen(PART_TYPE_DRIVER)) == 0 ||
            strcmp(PART_TYPE_DRIVER43, typestr) == 0 ||
            strcmp(PART_TYPE_DRIVERATA, typestr) == 0 ||
            strcmp(PART_TYPE_DRIVERIOKIT, typestr) == 0 ||
            strcmp(PART_TYPE_FWDRIVER, typestr) == 0 ||
            strcmp(PART_TYPE_FWB_COMPONENT, typestr) == 0 ||
            strcmp(PART_TYPE_PARTMAP, typestr) == 0 ||
            strcmp(PART_TYPE_PATCHES, typestr) == 0)
                type = 0;
        else if (strcmp(PART_TYPE_NBSD_PPCBOOT, typestr) == 0) {
                type = ROOT_PART;
                bzb = (struct blockzeroblock *)(&part->pmBootArgs);
                if ((bzb->bzbMagic == BZB_MAGIC) &&
                    (bzb->bzbType < FSMAXTYPES))
                        *fstype = bzb->bzbType;
                else
                        *fstype = FS_BSDFFS;
        } else if (strcmp(PART_TYPE_NETBSD, typestr) == 0 ||
                 strcmp(PART_TYPE_NBSD_68KBOOT, typestr) == 0) {
                type = UFS_PART;
                bzb = (struct blockzeroblock *)(&part->pmBootArgs);
                if ((bzb->bzbMagic == BZB_MAGIC) &&
                    (bzb->bzbType < FSMAXTYPES))
                        *fstype = bzb->bzbType;
                else
                        *fstype = FS_BSDFFS;
        } else if (strcmp(PART_TYPE_UNIX, typestr) == 0) {
                /* unix part, swap, root, usr */
                bzb = (struct blockzeroblock *)(&part->pmBootArgs);
                *clust = bzb->bzbCluster;
                if (bzb->bzbMagic != BZB_MAGIC) {
                        type = 0;
                } else if (bzb->bzbFlags & BZB_ROOTFS) {
                        type = ROOT_PART;
                        *fstype = FS_BSDFFS;
                } else if (bzb->bzbFlags & (BZB_USRFS | BZB_USRFS_NEW)) {
                        type = UFS_PART;
                        *fstype = FS_BSDFFS;
                } else if (bzb->bzbType == BZB_TYPESWAP) {
                        type = SWAP_PART;
                        *fstype = FS_SWAP;
                } else {
                        type = SCRATCH_PART;
                        *fstype = FS_OTHER;
                }
        } else if (strcmp(PART_TYPE_MAC, typestr) == 0) {
                type = HFS_PART;
                *fstype = FS_HFS;
        } else if (strcmp(PART_TYPE_APPLEUFS, typestr) == 0) {
                type = SCRATCH_PART;
                *fstype = FS_APPLEUFS;
        } else if (strcmp(PART_TYPE_LINUX, typestr) == 0) {
                type = SCRATCH_PART;
                *fstype = FS_OTHER;
        } else if (strcmp(PART_TYPE_LINUX_SWAP, typestr) == 0) {
                type = SCRATCH_PART;
                *fstype = FS_OTHER;
        } else {
                type = SCRATCH_PART;    /* no known type */
                *fstype = FS_OTHER;
        }

        return type;
}

static void
setpartition(struct part_map_entry *part, struct partition *pp, int fstype)
{
        pp->p_size = part->pmPartBlkCnt;
        pp->p_offset = part->pmPyPartStart;
        pp->p_fstype = fstype;

        part->pmPartType[0] = '\0';
}

static int
getNamedType(struct part_map_entry *part, int num_parts, struct disklabel *lp, int type, int alt, int *maxslot)
{
        int i = 0, clust;
        u_int8_t realtype;

        for (i = 0; i < num_parts; i++) {
                if (whichType(part + i, &realtype, &clust) != type)
                        continue;

                if (type == ROOT_PART) {
                        if (alt >= 0 && alt != clust)
                                continue;
                        setpartition(part + i, &lp->d_partitions[0], realtype);
                } else if (type == UFS_PART) {
                        if (alt >= 0 && alt != clust)
                                continue;
                        setpartition(part + i, &lp->d_partitions[6], realtype);
                        if (*maxslot < 6)
                                *maxslot = 6;
                } else if (type == SWAP_PART) {
                        setpartition(part + i, &lp->d_partitions[1], realtype);
                        if (*maxslot < 1)
                                *maxslot = 1;
                } else if (type == HFS_PART) {
                        setpartition(part + i, &lp->d_partitions[3], realtype);
                        if (*maxslot < 3)
                                *maxslot = 3;
                } else
                        printf("disksubr.c: can't do type %d\n", type);

                return 0;
        }

        return -1;
}

/*
 * MF --
 * here's what i'm gonna do:
 * read in the entire diskpartition table, it may be bigger or smaller
 * than NUM_PARTS but read that many entries.  Each entry has a magic
 * number so we'll know if an entry is crap.
 * next fill in the disklabel with info like this
 * next fill in the root, usr, and swap parts.
 * then look for anything else and fit it in.
 *      A: root
 *      B: Swap
 *      C: Whole disk
 *      G: Usr
 *
 *
 * I'm not entirely sure what netbsd386 wants in c & d
 * 386bsd wants other stuff, so i'll leave them alone
 *
 * AKB -- I added to Mike's original algorithm by searching for a bzbCluster
 *      of zero for root, first.  This allows A/UX to live on cluster 1 and
 *      NetBSD to live on cluster 0--regardless of the actual order on the
 *      disk.  This whole algorithm should probably be changed in the future.
 */
static const char *
read_mac_label(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *osdep)
{
        struct part_map_entry *part;
        struct partition *pp;
        struct buf *bp;
        const char *msg = NULL;
        int i, slot, maxslot = 0, clust;
        u_int8_t realtype;

        /* get buffer and initialize it */
        bp = geteblk((int)lp->d_secsize * NUM_PARTS);
        bp->b_dev = dev;

        /* read partition map */
        bp->b_blkno = 1;        /* partition map starts at blk 1 */
        bp->b_bcount = lp->d_secsize * NUM_PARTS;
        bp->b_flags |= B_READ;
        bp->b_cylinder = 1 / lp->d_secpercyl;
        (*strat)(bp);

        if (biowait(bp)) {
                msg = "Macintosh partition map I/O error";
                goto done;
        }

        part = (struct part_map_entry *)bp->b_data;

        /* Fill in standard partitions */
        lp->d_npartitions = RAW_PART + 1;
        if (getNamedType(part, NUM_PARTS, lp, ROOT_PART, 0, &maxslot))
                getNamedType(part, NUM_PARTS, lp, ROOT_PART, -1, &maxslot);
        if (getNamedType(part, NUM_PARTS, lp, UFS_PART, 0, &maxslot))
                getNamedType(part, NUM_PARTS, lp, UFS_PART, -1, &maxslot);
        getNamedType(part, NUM_PARTS, lp, SWAP_PART, -1, &maxslot);
        getNamedType(part, NUM_PARTS, lp, HFS_PART, -1, &maxslot);

        /* Now get as many of the rest of the partitions as we can */
        for (i = 0; i < NUM_PARTS; i++) {
                slot = getFreeLabelEntry(lp);
                if (slot < 0)
                        break;

                pp = &lp->d_partitions[slot];

                /*
                 * Additional ROOT_PART will turn into a plain old
                 * UFS_PART partition, live with it.
                 */

                if (whichType(part + i, &realtype, &clust)) {
                        setpartition(part + i, pp, realtype);
                } else {
                        slot = 0;
                }
                if (slot > maxslot)
                        maxslot = slot;
        }
        lp->d_npartitions = ((maxslot >= RAW_PART) ? maxslot : RAW_PART) + 1;

done:
        brelse(bp, 0);
        return msg;
}

/*
 * Scan the disk buffer in four byte strides for a native BSD
 * disklabel (different ports have variably-sized bootcode before
 * the label)
 */
static const char *
read_bsd_label(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
    struct cpu_disklabel *osdep)
{
        struct disklabel *dlp;
        struct buf *bp;
        const char *msg;
        struct disklabel *blk_start, *blk_end;
        int size, match;
        
        msg = NULL;

        /* 
         * Read in the first #(NUM_PARTS + 1) blocks of the disk.
         * The native Macintosh partition table starts at 
         * sector #1, but we want #0 too for the BSD label.
         */
        size = roundup((NUM_PARTS + 1) << DEV_BSHIFT, lp->d_secsize);
        bp = geteblk(size);

        bp->b_dev = dev;
        bp->b_blkno = 0;
        bp->b_resid = 0;
        bp->b_bcount = size;
        bp->b_flags |= B_READ;
        bp->b_cylinder = 1 / lp->d_secpercyl;
        (*strat)(bp);

        match = 0;
        
        if (biowait(bp)) {
                msg = "I/O error reading BSD disklabel";
        } else {
                /*
                 * Hunt the label, starting at the beginning of the disk.
                 * When we find an inconsistent label, report and continue.
                 */
                blk_start = (struct disklabel *)bp->b_data;
                blk_end = (struct disklabel *)((char *)bp->b_data +
                    (NUM_PARTS << DEV_BSHIFT) - sizeof(struct disklabel));

                for (dlp = blk_start; dlp <= blk_end; 
                     dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
                        if (dlp->d_magic == DISKMAGIC &&
                            dlp->d_magic2 == DISKMAGIC) {
                                /* Sanity check */
                                if (dlp->d_npartitions <= MAXPARTITIONS && 
                                    dkcksum(dlp) == 0) {
                                        *lp = *dlp;
                                        match = -1;
                                        break;
#ifdef DIAGNOSTIC
                                } else {
                                        printf("read_bsd_label() found "
                                            "damaged disklabel starting at "
                                            "0x0%p, ignore\n", dlp);
#endif /* DIAGNOSTIC */
                                }
                        }
                }
                if (!match)
                        msg = "BSD disklabel not found";
        }
        brelse(bp, 0);
        return msg;
}

/* Read MS-DOS partition table.
 *
 * XXX -
 * Since FFS is endian sensitive, we pay no effort in attempting to
 * dig up *BSD/i386 disk labels that may be present on the disk.
 * Hence anything but DOS partitions is treated as unknown FS type, but
 * this should suffice to mount_msdos Zip and other removable media.
 */
static const char *
read_dos_label(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *osdep)
{
        struct mbr_partition *dp;
        struct buf *bp;
        const char *msg = NULL;
        int i, slot, maxslot = 0;
        u_int32_t bsdpartoff;
        struct mbr_partition *bsdp;

        /* get a buffer and initialize it */
        bp = geteblk((int)lp->d_secsize);
        bp->b_dev = dev;

        /* read master boot record */
        bp->b_blkno = MBR_BBSECTOR;
        bp->b_bcount = lp->d_secsize;
        bp->b_flags |= B_READ;
        bp->b_cylinder = MBR_BBSECTOR / lp->d_secpercyl;
        (*strat)(bp);

        bsdpartoff = 0;

        /* if successful, wander through dos partition table */
        if (biowait(bp)) {
                msg = "dos partition I/O error";
                goto done;
        }
        /* XXX */
        dp = (struct mbr_partition *)((char *)bp->b_data + MBR_PART_OFFSET);
        bsdp = NULL;
        for (i = 0; i < MBR_PART_COUNT; i++, dp++) {
                switch (dp->mbrp_type) {
                case MBR_PTYPE_PMBR:
                        goto done;      /* do not fake anything for GPT disks */
                case MBR_PTYPE_NETBSD:
                        bsdp = dp;
                        break;
                case MBR_PTYPE_OPENBSD:
                case MBR_PTYPE_386BSD:
                        if (!bsdp)
                                bsdp = dp;
                        break;
                }
        }
        if (!bsdp) {
                /* generate fake disklabel */
                dp = (struct mbr_partition *)((char *)bp->b_data +
                    MBR_PART_OFFSET);
                for (i = 0; i < MBR_PART_COUNT; i++, dp++) {
                        if (!dp->mbrp_type)
                                continue;
                        slot = getFreeLabelEntry(lp);
                        if (slot < 0)
                                break;
                        if (slot > maxslot)
                                maxslot = slot;

                        lp->d_partitions[slot].p_offset = bswap32(dp->mbrp_start);
                        lp->d_partitions[slot].p_size = bswap32(dp->mbrp_size);

                        switch (dp->mbrp_type) {
                        case MBR_PTYPE_FAT12:
                        case MBR_PTYPE_FAT16S:
                        case MBR_PTYPE_FAT16B:
                        case MBR_PTYPE_FAT32:
                        case MBR_PTYPE_FAT32L:
                        case MBR_PTYPE_FAT16L:
                                lp->d_partitions[slot].p_fstype = FS_MSDOS;
                                break;
                        default:
                                lp->d_partitions[slot].p_fstype = FS_OTHER;
                                break;
                        }
                }
                msg = "no NetBSD disk label";
        } else {
                /* NetBSD partition on MBR */
                bsdpartoff = bswap32(bsdp->mbrp_start);

                lp->d_partitions[2].p_size = bswap32(bsdp->mbrp_size);
                lp->d_partitions[2].p_offset = bswap32(bsdp->mbrp_start);
                if (2 > maxslot)
                        maxslot = 2;
                /* read in disklabel, blkno + 1 for DOS disklabel offset */
                osdep->cd_labelsector = bsdpartoff + MBR_LABELSECTOR;
                osdep->cd_labeloffset = MBR_LABELOFFSET;
                if (get_netbsd_label(dev, strat, lp, osdep))
                        goto done;
                msg = "no NetBSD disk label";
        }

        lp->d_npartitions = ((maxslot >= RAW_PART) ? maxslot : RAW_PART) + 1;

 done:
        brelse(bp, 0);
        return (msg);
}

/*
 * Get real NetBSD disk label
 */
static int
get_netbsd_label(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *osdep)
{
        struct buf *bp;
        struct disklabel *dlp;

        /* get a buffer and initialize it */
        bp = geteblk((int)lp->d_secsize);
        bp->b_dev = dev;

        /* Now get the label block */
        bp->b_blkno = osdep->cd_labelsector;
        bp->b_bcount = lp->d_secsize;
        bp->b_flags |= B_READ;
        bp->b_cylinder = bp->b_blkno / (lp->d_secsize / DEV_BSIZE) / lp->d_secpercyl;
        (*strat)(bp);

        if (biowait(bp))
                goto done;

        for (dlp = (struct disklabel *)((char *)bp->b_data +
                 osdep->cd_labeloffset);
             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
                    && dlp->d_npartitions <= MAXPARTITIONS
                    && dkcksum(dlp) == 0) {
                        *lp = *dlp;
                        osdep->cd_labeloffset = (char *)dlp -
                            (char *)bp->b_data;
                        brelse(bp, 0);
                        return 1;
                }
        }
done:
        brelse(bp, 0);
        return 0;
}

/*
 * 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.
 *
 * This will read sector zero.  If this contains what looks like a valid
 * Macintosh boot sector, we attempt to fill in the disklabel structure.
 * If the first longword of the disk is a NetBSD disk label magic number,
 * then we assume that it's a real disklabel and return it.
 */
const char *
readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *osdep)
{
        struct buf *bp;
        const char *msg = NULL;

        if (lp->d_secperunit == 0)
                lp->d_secperunit = 0x1fffffff;

        if (lp->d_secpercyl == 0) {
                return msg = "Zero secpercyl";
        }
        bp = geteblk((int)lp->d_secsize);

        bp->b_dev = dev;
        bp->b_blkno = 0;
        bp->b_resid = 0;
        bp->b_bcount = lp->d_secsize;
        bp->b_flags |= B_READ;
        bp->b_cylinder = 1 / lp->d_secpercyl;
        (*strat)(bp);

        osdep->cd_start = -1;

        /* XXX cd_start is abused as a flag for fictious disklabel */

        if (biowait(bp)) {
                msg = "I/O error reading block zero";
                goto done;
        }
        osdep->cd_labelsector = LABELSECTOR;
        osdep->cd_labeloffset = LABELOFFSET;
        if (get_netbsd_label(dev, strat, lp, osdep))
                osdep->cd_start = 0;
        else {
                u_int16_t *sbSigp;

                sbSigp = (u_int16_t *)bp->b_data;
                if (*sbSigp == 0x4552) {
                        /* it ignores labelsector/offset */
                        msg = read_mac_label(dev, strat, lp, osdep);
                        /* the disklabel is fictious */
                } else if (bswap16(*(u_int16_t *)((char *)bp->b_data +
                    MBR_MAGIC_OFFSET)) == MBR_MAGIC) {
                        /* read_dos_label figures out labelsector/offset */
                        msg = read_dos_label(dev, strat, lp, osdep);
                        if (!msg)
                                osdep->cd_start = 0;
                } else {
                        msg = read_bsd_label(dev, strat, lp, osdep);
                        if (!msg)
                                osdep->cd_start = 0;    /* XXX for now */
                }
        }

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 *osdep)
{
        /* 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;

        /* openmask parameter ignored */

        *olp = *nlp;
        return 0;
}

/*
 * Write disk label back to device after modification.
 */
int
writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *osdep)
{
        struct buf *bp;
        int error;
        struct disklabel label;

        /*
         * Try to re-read a disklabel, in case he changed the MBR.
         */
        label = *lp;
        readdisklabel(dev, strat, &label, osdep);
        if (osdep->cd_start < 0)
                return EINVAL;

        /* get a buffer and initialize it */
        bp = geteblk(lp->d_secsize);
        bp->b_dev = dev;

        bp->b_blkno = osdep->cd_start + osdep->cd_labelsector;
        bp->b_cylinder = bp->b_blkno / (lp->d_secsize / DEV_BSIZE) / lp->d_secpercyl;
        bp->b_bcount = lp->d_secsize;

        bp->b_flags |= B_READ;
        (*strat)(bp);
        error = biowait(bp);
        if (error != 0)
                goto done;

        bp->b_flags &= ~B_READ;
        bp->b_flags |= B_WRITE;
        bp->b_oflags &= ~BO_DONE;

        memcpy((char *)bp->b_data + osdep->cd_labeloffset, (void *)lp,
            sizeof *lp);

        (*strat)(bp);
        error = biowait(bp);

done:
        brelse(bp, 0);

        return error;
}