Subject: handling NetBSD partitions in the Apple Partition Map [was Re: write-label changes]
To: None <port-macppc@netbsd.org>
From: Darrin B. Jewell <dbj@netbsd.org>
List: port-macppc
Date: 10/07/2002 00:48:53
--=-=-=


Bill Studenmund asked me to review some patches which
added support for writing out in-core netbsd disklabels to
apple partition maps.  The resulting discussion brought up
some issues better worked out on a public list.  Therefore,
with his permission, my current reply is being sent to
port-macppc with full context of the conversation included.

The primary issue under discussion is how to properly use
the Apple Partition Map to hold NetBSD partitions.  Initially,
the thought was to use a pmPartType of "NetBSD", and
keep additional information in the pmBootArgs reserved
area of the partition map entry.  Since we have discovered
some disadvantages to this idea, we are now considering both
encoding the fstype name in the pmPartType as well as optionally
encoding the extra disklabel partition information in the
pmBootArgs.

Bill Studenmund <wrstuden@netbsd.org> writes:

| On 23 Sep 2002, Darrin B. Jewell wrote:
|
| >   I've read over your changes and booted some kernels with
| > them installed.  I've tested the read disklabel behavior,
| > although not the new write functionality.
| >
| >   I do not see any fundamental problems with adding write
| > disklabel support, but I want to push back a little on some
| > of the details.  I'm particularly interested in changes we
| > make now that cannot be easily undone in the future.
| 
| Ok. Though if we're working out this much stuff, we should be talking on a
| public list. :-) Feel free to forward anything I've said so far to
| port-macppc or other lists as appropriate. :-)
| 
| >   I started by being concerned with misusing bzb structure
| > to hold the disklabel bits that aren't found in the
| > partition map.  These bits are more generically found in the
| > "u_int8_t pmBootArgs[128]" field of of a partition map
| > entry.  I've been talking here in person to the Apple
| > Developer who is in charge of the MacOS Disk Copy utility
| > and the author of the MacOS `bless' program.  He has
| > verified that as long as we use our own entry for the "char
| > pmPartType[32] field, we can put whatever we want in the
| > pmBootArgs bits.  However he does recommend that we use a
| > pmPartType of the form MySoftwareCompany_MyContent and cites
| > an apple tech pub. (see URL [1] below) Therefore, I would
| > like to recommend that we use "NetBSD_4.4LFS" for FS_BSDLFS
| > and even move to "NetBSD_4.2BSD" for FS_BSDFFS.  (If we
| > really want to support pmPartType of just "NetBSD", I
| > suggest that we use an on disk NetBSD disklabel within that
| > partition to describe how the disk data is laid out, but
| > since that has its own issues, i'm inclined not to go this
| > route either.)
| 
| I'd rather not go that route, where we have NetBSD_content. Mainly as we,
| strictly speaking, have over 20 types of content. Yes, a number of them
| aren't valid (NetBSD_HFS won't happen :-), and I doubt anyone will put a
| SVR3 fs on an apple-partitioned disk), but it just seems very clean to not
| have to translate from text <-> numbers.
We already have to parse the string to interpret it.  This operation
doesn't have to be particularly inefficient.  The key advantage to
following the apple guidelines for this is that we can completely
use apple tools to create a valid netbsd partition entry.  If we
require pmBootArgs bits to be set for an entry to be valid, then
only NetBSD tools will be able to create valid partition entries.

| 
| As for the NetBSD disklabel bit, we can't use NetBSD as it's in use as a
| NetBSD-specific variant partition. i.e. it's being used as the code uses
| it now. :-|
While it is true that the disksubr.c does contain current code that uses
this, there is nothing yet in the tree that creates partition entries
of this form.  Therefore, I'm not too concerned about breaking
backward compatiblity for these types of partitions.  I think best
practice is to remove support for "NetBSD" and avoid using it explicitly
in the apple partition map for now.  Therefore, 1.6 kernels will
not accidentally run accross partitions of this type and incorrectly
handle them.

| 
| My personal soap box (:-) is that the way we did NetBSD partitioning
| inside of mbr partitioning on x86 shouldn't be copied to other
| partitioning schemes. :-) So I don't like the idea of adding a NetBSD
| partition on disk.

I also do not think we should perpetuate this method with the
apple partition map.  However, I would like to point out some details
of Darwin's IOKit that addtionaly enforce the idea that we should
not be using pmPartType of "NetBSD".

Partition support for Darwin is implemented in its the IOStorageFamily
classes.  Specifically, different types of partitions are implemented
as derived classes of IOPartitionScheme.  This class abstracts away
the specifics of a partition table and maps a string provided by
darwin to the specific identifiers used in a partition table instance.
For the Apple Partition map, this string identifier is used directly
as the pmPartType.  For the fdisk/mbr partition table, this string is
mapped to the commonly used sysids.  Since the string "NetBSD" maps to
the mbr/fdisk partition entry of 0xA8, it would be poor to use this
same name in the apple partition map unless it is interpreted the same
way as the i386 mbr partition type, with an additional NetBSD
disklabel on disk.

| 
| >   Although we are safe in using the pmBootArgs field, i
| > rather avoid using it if we can.  Is there really
| > information in the NetBSD struct partition that we
| > absolutely need?  Apart from the fstype (which I just
| > proposed to encode in the pmPartType), I think we can get
| > the remaining information from the filesystem and provide it
| > expliclitly to newfs.  Avoiding secret magic in pmBootArgs
| 
| Yes, it is really needed. Mainly for disaster recovery. Say you have an
| ffs with a bad superblock. Without the blocksize, frag size, and c/g
| number, it's hard to find the alternate SBs for recovery. :-| That's why
| we put it in the disklabel, so that you can get it back for fsck -b.

This is what pencil, paper and all those numbers printed by newfs are
for!  More seriously though, you are correct, there are good reasons
to include this information.  I have no problem with optionally
using the pmBootArgs field for this information, but I want
to be able to function correctly if the pmBootArgs doesn't contain
it.

| 
| > will allow for more flexibility in the tools that can
| > manipulate NetBSD areas of the disk.  Since A/UX support is
| > disappearing from Apple utilities, this will also provide us
| > an ongoing plan for deprecating Apple_UNIX_SVR2.
| 
| I thik if we have a signature (and we only use special fields if that
| signature matches), then I think we're safe for Apple tools.
Being safe from the Apple tools is not the only requirement.  As I
mentioned, I want to be able to use apple tools to create NetBSD
partitions.  We can put a an optional struct partition in the
pmBootArgs field, with presense possibly indicated by a different
magic number than the one used by the bzb.  If it does not contradict
the rest of the apple partition map entry, then its additional values
can be considered valid, and read into the in-core disklabel.

| 
| >   If we do decide to continue using the pmPartType field, I
| > think we should get away from forcing the bzb structure on
| > the data.  I especially think we should not explicitly set
| > the bzbMagic bits to the historical value: 0xABADBABE.  I do
| > note that the pmPartType field is 128 bytes long, and
| > NetBSD's struct partition is only 16 bytes, so if we really
| > wanted, we could lay out pmPartType directly as a struct
| > partition.
| 
| Hmmm... True, we should probably use a different value, so that if you
| change a partition from the AUX one to NetBSD we aren't fooled.
| 
| As for a separate struct partition or what-not, I was lazy. Yes, we could
| just plop a struct partition in there, but since we already have a
| structure that has the extra bits we need, I didn't see why we should. :-)
| 
| >   I now mention my remaining observations, which mostly
| > involve decisions that can easily changed in the future if
| > we decide to do things differently.  I did notice that
| > unless a disklabel is written to the disk with the new
| > disklabel write support, the changes in your patch will not
| > cause disklabel partitions to be relettered.
| 
| Yes, that was one of my desires. :-)
| 
| >   Although it is not harmful to add the apple_map field to
| > the cpu_disklabel, I do think you can track your disklabel
| > changes without having keep the extra information around.
| > Specifically, a partition map is constrained as as follows:
| > "With the exception of the driver descriptor record in block
| > 0, every block on a disk must belong to a partition." (see
| > url [2] below) This means that if a disklabel entry changes,
| > you can easily rediscover how the disklabel entries moved
| > and the effect this would have on the underlying partition
| > map.
| 
| Yes, but I figured that the only place I wanted this code to be used was
| for updating fs types and frag/block/cpg values. So having a mapping from
| NetBSD to Apple partition makes life easier, rather than an O(n^2) search
| (for n partitions, find out where they are in the n on-disk ones), we just
| have a cached value.

This is premature optimization.  It technically doesn't have to be
implemented as O(n^2), although it may be easiest to implement as
such.  Even so, your changes don't remove the existing O(n^2) checks,
and since disklabels are evaluated relatively infrequently, and the
number of partitions involved is relatively low, a O(n^2) operation
here is mostly harmless.

| >   You also do several checks for valid conversions.  It
| > seems more appropriate to do validity checks in
| > setdisklabel(), especially since it has access to both the
| > new and the old disklabels without having to read it back in
| > from the partition map.
| 
| Note: a few things. 1) there really are three "disklabels" in play.
| There's the old in-kernel one, there's the new one we're setting, and
| there's whatever is on the disk. A good bit of this code is trying to
| catch the case where someone re-writes the on-disk partition and then
| something tries to re-write the in-core label. I don't want the kernel to
| ruin the on-disk one since it has in principle less info than the on disk
| one (there are partitions that exist on disk but not in the disklabel).

True that you don't want to ruin the on-disk one, but it should be safe
to assume that the in-core one was derived from the on-disk one.
Therefore, you can diff the in-core one with the new one and apply
the effects of the diff to the on-disk one.

| 
| 2) Leo W. proposed a few years ago moving towards some of the wedges ideas
| we've batted around way too long. One key bit of that is that the kernel
| gets out of the business of writing disklabels. It can update them, but
| for the most part, the kernel should get out of the business of writing
| disklabels. Especially as part of the idea (wedges & Leo's) is that you
| can have a "disklabel" that has partitoins from a number of different
| on-disk partitions (nested mbrs and net/free/open BSD disklabels in mbrs,
| etc). So I didn't make the kernel able to write Apple disklabels.
| 
| 3) You cite a constraint above for partition maps having non-overlapping
| partitions. That constraint applies (most certainly) to Apple partition
| maps, but it does not apply to disklabels. Dangerous, yes, but permitted.
| So strictly speaking, we have to verify that the new disklabel doesn't
| have overlapping partitions, if we want to support writing whole
| disklabels.
| 
| >   Your patches as well as my recent pdisk and Apple UFS
| > support have caused me to start to think about this area
| > a little more carefully and looking into the disklabel
| > code in more detail.  I'm thinking it would be useful
| > to get some of the problematic boot and installation tool
| > issues cleaned up.
| 
| Agreed.
| 
| >   For example, after noting your efforts to map partition
| > map entries to disklabel partitions, I spent some time
| > looking at the autoconf.c support where we ignore the booted
| > partition information and always choose partition 'a' for
| > the root.  This led me to spend significant time grovelling
| > the IEEE openfirmware specification and coming up to speed
| > on some of the broken bits about our bootloader mechanism.
| > I'm thinking about spending some time soon fixing some of
| > the boot loader problems and the complicated macintosh model
| > specific installation methodology.
| 
| Cool.
| 
| >   My goal in the next few weeks is to get the Apple UFS code
| > committed updated to the current sources and commited.
| > Hopefully, I will then have some NetBSD time to make
| > progress on bootloader issues including possibly cleaning up
| > disksubr.c, creating apple partition maps from scratch and
| > getting the apple tools to more cleanly handle coexisting
| > with a NetBSD install.  I just dug out an ofw 1.0.5 machine
| > so I can test changes as I go along.
| 
| Cool. The one thing I'd like is for creating from-scratch apple maps is a
| job of userland, not the kernel (see above mutterings about Leo's idea,
| etc.); I really think all of NetBSD will need to move to that, and I'd
| like this effort to say be a start, not something that needs retooling
| later.
| 
| Oh, have you heard anything from Apple about what they are going to do
| when disks get over 2 TB (2^32 512-byte blocks)? It'll happen soon if it
| hasn't already, and it'll need a different partition table type.

My understanding is that for the short term, they plan to lie about
the device blocksize in the drvr_map->sbBlockSize, so that the
overall block count of the device can fit in 32 bits.

| 
| Oh, have I sent you the apple partitoiner I wrote? It needs the ability to
| add a partition added to its UI, and I think it's ready to go (it has code
| to add partitions available from the command line, just not hooked into
| the curses UI).
| 
| Take care,
| 
| Bill
| 
| > [1] http://developer.apple.com/techpubs/macosx/Darwin/IOKit/DeviceDrivers/MassStorage/07_Media_Example/Edit_Your_D2operty_List.html
| >
| > [2] http://developer.apple.com/techpubs/mac/Devices/Devices-121.html#MARKER-2-27
| >
| > Bill Studenmund <wrstuden@netbsd.org> writes:
| >
| > | Darrin,
| > |
| > | Here are the changes I'm running with to add disklabel "write" ability.
| > |
| > | I don't add the ability to format a disk from scratch, but the idea is to
| > | permit partition format changing and also frag/block size/cpg setting.
| > |
| > | I've added a structure to the os-dep part of the in-core disklabel, and I
| > | store in it a mapping from NetBSD partition to the corresponding Apple
| > | partition. Thus if partition d (3) changes fs type, we want to change the
| > | Apple partition osdep->apple_map[3] to reflect that change.
| > |
| > | I don't think I have all of the error checking done when writing. I make
| > | sure the partition looks sane to change, and if so I update it to
| > | something that can take fs types (NETBSD w/ a bzb).
| > |
| > | The bzb used in a NETBSD{,/MACPPC,/MAC68k} is different from the one in
| > | A/UX partitions. I overloaded some fields to hold all the goodies from
| > | struct partition.
| > |
| > | Thoughts?
| > |
| > | Take care,
| > |
| > | Bill
| >

--=-=-=
Content-Disposition: attachment; filename=wrstuden.macppcdisksubr.diffs
Content-Description: wrstuden's original disksubr diffs with disklabel write support

Index: include/disklabel.h
===================================================================
RCS file: /cvsroot/syssrc/sys/arch/macppc/include/disklabel.h,v
retrieving revision 1.10
diff -u -r1.10 disklabel.h
--- include/disklabel.h	2002/09/10 11:28:56	1.10
+++ include/disklabel.h	2002/09/18 21:51:13
@@ -105,7 +105,10 @@
 
 struct cpu_disklabel {
 	daddr_t cd_start;	/* Offset to NetBSD partition in blocks */
+				/* -1 == Apple partition map */
 	daddr_t cd_labelsector;	/* label sector offset from cd_start */
+	int apple_map[MAXPARTITIONS]; /* array mapping our partitions to
+				 * source Apple partition entries */
 	int cd_labeloffset;	/* label byte offset within label sector */
 };
 
Index: macppc/disksubr.c
===================================================================
RCS file: /cvsroot/syssrc/sys/arch/macppc/macppc/disksubr.c,v
retrieving revision 1.24
diff -u -r1.24 disksubr.c
--- macppc/disksubr.c	2002/09/10 11:31:10	1.24
+++ macppc/disksubr.c	2002/09/18 21:51:14
@@ -132,7 +132,7 @@
 static void setpartition __P((struct part_map_entry *,
 		struct partition *, int));
 static int getNamedType __P((struct part_map_entry *, int,
-		struct disklabel *, int, int, int *));
+		struct disklabel *, struct cpu_disklabel *, int, int, int *));
 static char *read_mac_label __P((dev_t, void (*)(struct buf *),
 		struct disklabel *, struct cpu_disklabel *));
 static char *read_dos_label __P((dev_t, void (*)(struct buf *),
@@ -259,10 +259,11 @@
 }
 
 static int
-getNamedType(part, num_parts, lp, type, alt, maxslot)
+getNamedType(part, num_parts, lp, osdep, type, alt, maxslot)
 	struct part_map_entry *part;
 	int num_parts;
 	struct disklabel *lp;
+	struct cpu_disklabel *osdep;
 	int type, alt;
 	int *maxslot;
 {
@@ -280,20 +281,24 @@
 			if (alt >= 0 && alt != clust)
 				continue;
 			setpartition(part + i, &lp->d_partitions[0], realtype);
+			osdep->apple_map[0] = i;
 		} else if (type == UFS_PART) {
 			bzb = (struct blockzeroblock *)
 			    (&(part + i)->pmBootArgs);
 			if (alt >= 0 && alt != clust)
 				continue;
 			setpartition(part + i, &lp->d_partitions[6], realtype);
+			osdep->apple_map[6] = i;
 			if (*maxslot < 6)
 				*maxslot = 6;
 		} else if (type == SWAP_PART) {
 			setpartition(part + i, &lp->d_partitions[1], realtype);
+			osdep->apple_map[1] = i;
 			if (*maxslot < 1)
 				*maxslot = 1;
 		} else if (type == HFS_PART) {
 			setpartition(part + i, &lp->d_partitions[3], realtype);
+			osdep->apple_map[3] = i;
 			if (*maxslot < 3)
 				*maxslot = 3;
 		} else
@@ -358,16 +363,22 @@
 		goto done;
 	}
 
+	/* Init apple_map to known, invalid state */
+	for (i = 0; i < MAXPARTITIONS; i++)
+		osdep->apple_map[i] = -1;
+
 	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);
+	if (getNamedType(part, NUM_PARTS, lp, osdep, ROOT_PART, 0, &maxslot))
+		getNamedType(part, NUM_PARTS, lp, osdep, ROOT_PART, -1,
+			     &maxslot);
+	if (getNamedType(part, NUM_PARTS, lp, osdep, UFS_PART, 0, &maxslot))
+		getNamedType(part, NUM_PARTS, lp, osdep, UFS_PART, -1,
+			     &maxslot);
+	getNamedType(part, NUM_PARTS, lp, osdep, SWAP_PART, -1, &maxslot);
+	getNamedType(part, NUM_PARTS, lp, osdep, HFS_PART, -1, &maxslot);
 
 	/* Now get as many of the rest of the partitions as we can */
 	for (i = 0; i < NUM_PARTS; i++) {
@@ -384,6 +395,7 @@
 
 		if (whichType(part + i, &realtype, &clust)) {
 			setpartition(part + i, pp, realtype);
+			osdep->apple_map[slot] = i;
 		} else {
 			slot = 0;
 		}
@@ -392,6 +404,11 @@
 	}
 	lp->d_npartitions = ((maxslot >= RAW_PART) ? maxslot : RAW_PART) + 1;
 
+	for (i = 0; i < MAXPARTITIONS; i+= 4)
+		printf("Apm: %2d %2d, %2d %2d, %2d %2d, %2d %2d\n",
+		i, osdep->apple_map[i], i+1, osdep->apple_map[i+1],
+		i+2, osdep->apple_map[i+2], i+3, osdep->apple_map[i+3]);
+
 done:
 	brelse(bp);
 	return msg;
@@ -588,9 +605,6 @@
 	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";
@@ -607,7 +621,7 @@
 		if (*sbSigp == 0x4552) {
 			/* it ignores labelsector/offset */
 			msg = read_mac_label(dev, strat, lp, osdep);
-			/* the disklabel is fictious */
+			osdep->cd_start = -1;
 		} else if (bswap16(*(u_int16_t *)(bp->b_data + MBR_MAGICOFF))
 			   == MBR_MAGIC) {
 			/* read_dos_label figures out labelsector/offset */
@@ -655,7 +669,199 @@
 	return 0;
 }
 
+static int write_mac_label(dev_t, void (*strat)(struct buf *),
+			   struct disklabel *new, struct disklabel *old,
+			   struct cpu_disklabel *osdep);
+
 /*
+ * "Write" an apple partition map. To keep things simple, we just
+ * update existing partitions. Use a userland tool if
+ * you want to add new partitions.
+ *
+ * We first read in the disklabel like the read routine does. Then we scan
+ * through the partition list, and look at the changes. You can not change
+ * the size nor start of a partition. Also you can only update a partition
+ * if it is an APPLE_UNIX_SVR2 or some sort of NETBSD partition.
+ *
+ * Once we know all the changes are ok, we go through the list and make them.
+ * The resulting partition type depends on what the bsd partition type is.
+ * Swap partitions are still APPLE_UNIX_SVR2, while everything else is a
+ * NETBSD or NETBSD/MACPPC partition with a valid bzb.
+ *
+ * The logic as to when we care about fragment size changes etc. was
+ * inspired by disklabel. If it prints a value, we care about it. There
+ * are three cases. We care about none, p_fsize & p_frag, or p_fsize &
+ * p_frag & p_cpg. We make use of the fact that p_sgs is the same field as
+ * p_cpg and lump the two together.
+ */
+static int
+write_mac_label(dev_t dev, void (*strat)(struct buf *),
+		struct disklabel *new, struct disklabel *old,
+		struct cpu_disklabel *osdep)
+{
+	int	i, needcheck, retval = 0;
+	int	to_update[MAXPARTITIONS];
+	struct	partition *pn, *po;
+	struct	buf *bp;
+	struct	part_map_entry *part, *pe;
+	char	*p;
+
+	if (new->d_npartitions != old->d_npartitions)
+		return EINVAL;
+
+	/* get buffer and initialize it */
+	bp = geteblk((int)old->d_secsize * NUM_PARTS);
+	bp->b_dev = dev;
+
+	/* read partition map */
+	bp->b_blkno = 1;	/* partition map starts at blk 1 */
+	bp->b_bcount = old->d_secsize * NUM_PARTS;
+	bp->b_flags |= B_READ;
+	bp->b_cylinder = 1 / old->d_secpercyl;
+	(*strat)(bp);
+
+	if (biowait(bp)) {
+		retval = EINVAL;
+		goto write_done;
+	}
+
+	part = (struct part_map_entry *)bp->b_data;
+
+	for (i = 0; i < new->d_npartitions; i++) {
+		to_update[i] = 0;
+		pn = &new->d_partitions[i];
+		po = &old->d_partitions[i];
+		if (pn->p_size != po->p_size ||
+		    pn->p_offset != po->p_offset) {
+			retval = EINVAL;
+			goto write_done;
+		}
+		if (pn->p_size == 0)
+			continue;
+		needcheck = 0;
+		if (pn->p_fstype != po->p_fstype ) {
+			/* Make sure that both the old and new partition
+			 * types are ones that either can be NetBSD parts
+			 * or can be turned into NetBSD parts */
+			switch (pn->p_fstype) {
+			case FS_HFS:
+				/* You need an fs partitioner for this or we
+				 * need to be happy changing full type */
+				retval = EINVAL;
+				goto write_done;
+			default:
+				break;
+			}
+			switch (po->p_fstype) {
+			case FS_HFS:
+				/* You need an fs partitioner for this */
+				retval = EINVAL;
+				goto write_done;
+			case FS_UNUSED:
+				/* Make sure we don't change the raw part. It
+				 * doesn't have an apple part it came from */
+				if (osdep->apple_map[i] == -1) {
+					retval = EINVAL;
+					goto write_done;
+				}
+			case FS_OTHER:
+				/* I don't know why you would set the fs types
+				 * above, but you can if it was NETBSD */
+			case FS_BSDFFS:
+				needcheck = 1;
+			default:
+				/* Everything else had to have started with a
+				 * NetBSD partition type, so no need to check */
+				break;
+			}
+		} else {
+			switch (po->p_fstype) {
+			case FS_BSDFFS:
+				/* only ffs type might have been non-changable
+				 * partition type */
+				needcheck = 1;
+			case FS_BSDLFS:
+			case FS_ADOS:
+				if (pn->p_cpg != po->p_cpg)
+					to_update[i] = 1;
+				/* FALLTHOUGH */
+			case FS_UNUSED:
+			case FS_EX2FS:
+				if ((pn->p_fsize != po->p_fsize) ||
+				    (pn->p_frag != po->p_frag))
+					to_update[i] = 1;
+				break;
+			default:
+				break;
+			}
+		}
+		if (needcheck) {
+			/*
+			 * We want to change the partition type, make sure
+			 * it's a valid change.
+			 *
+			 * That means it has to have been a NetBSD partition
+			 * or an APPLE_UNIX_SVR2 that was cluster 0 ??
+			 */
+			char	ucname[32], *s;
+			struct	blockzeroblock *bzb;
+
+			pe = part + i;
+
+			strncpy(ucname, pe->pmPartType, sizeof(ucname));
+			ucname[sizeof(ucname) - 1] = '\0';
+			for (s = ucname; *s; s++)
+				if ((*s >= 'a') && (*s <= 'z'))
+					*s = (*s - 'a' + 'A');
+
+			if (strncmp(PART_TYPE_NETBSD, ucname, 6) == 0) {
+				/* It starts with NETBSD, it's good */
+			} else if (strcmp(PART_TYPE_UNIX, ucname) == 0) {
+				bzb = (struct blockzeroblock *)
+					(&pe->pmBootArgs);
+				/* This heuristic may need work */
+				if ((bzb->bzbMagic != BZB_MAGIC) ||
+				    (bzb->bzbCluster != 0)) {
+					retval = EINVAL;
+					goto write_done;
+				}
+			} else {
+				retval = EINVAL;
+				goto write_done;
+			}
+		}
+	}	/* for loop to check all partitions */
+
+	for (i = 0; i < new->d_npartitions; i++)
+		if (to_update[i]) {
+			pn = &new->d_partitions[i];
+			po = &old->d_partitions[i];
+			pe = part + i;
+
+			/*
+			 * first make sure the partition type is right.
+			 * We will need APPLE_UNIX_SVR2 for swap, or
+			 * NetBSD for everything else.
+			 */
+			if (pn->p_fstype == FS_SWAP) {
+				p = "Apple_Unix_SVR2";
+				memcpy(pe->pmPartType, p, strlen(p) + 1);
+			} else {
+			}
+		} /* loop to do update */
+
+	bp->b_flags &= ~(B_READ|B_DONE);
+	bp->b_flags |= B_WRITE;
+
+	(*strat)(bp);
+	retval = biowait(bp);
+
+write_done:
+	brelse(bp);
+	return retval;
+}
+
+/*
  * Write disk label back to device after modification.
  */
 int
@@ -670,12 +876,31 @@
 	struct disklabel label;
 
 	/*
-	 * Try to re-read a disklabel, in case he changed the MBR.
+	 * Try to re-read a disklabel. Make sure that the on-disk
+	 * one is still basically what we started with
 	 */
-	label = *lp;
-	readdisklabel(dev, strat, &label, osdep);
-	if (osdep->cd_start < 0)
-		return EINVAL;
+	if (osdep->cd_start == -1 ) {
+		int old_apple_map[MAXPARTITIONS];
+
+		/*
+		 * read in the on-disk apple partition map. If its
+		 * apple_map matches the current one, we assume that
+		 * the just-read disklabel is the same as the last one we
+		 * read, so we can use it as a point of comparison to do
+		 * the updating.
+		 */
+		memcpy(old_apple_map, osdep->apple_map, sizeof(old_apple_map));
+		readdisklabel(dev, strat, &label, osdep);
+		if ((osdep->cd_start != -1) || (0 != memcmp(old_apple_map,
+		    osdep->apple_map, sizeof(old_apple_map))))
+			return EINVAL;
+		return write_mac_label(dev, strat, lp, &label, osdep);
+	} else {
+		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);

--=-=-=--