/*	$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;
}
