[RFC] Patch to add GEOM support to libdisk and sysinstall

Craig Rodrigues rodrigc at crodrigues.org
Thu Sep 15 09:59:41 PDT 2005


Hi,

I have been working on a patch to add GEOM support
to libdisk and sysinstall.  These
fixes allow you to label a disk which you
currently have a partition mounted on.
This is one of the "Desired features" for
the 6.0-RELEASE.  I copied a lot of code
from boot0cfg and bsdlabel into libdisk.

My code is available in  Perforce on the
"rodrigc_libdisk_geom" branch.

Also attached are patches against CURRENT CVS.
Note: my code is not in CVS yet.

Since I am new to GEOM and disk partitioning
issues, I would appreciate any feedback. 

-- 
Craig Rodrigues        
rodrigc at crodrigues.org
-------------- next part --------------
Index: Makefile
===================================================================
RCS file: /home/ncvs/src/lib/libdisk/Makefile,v
retrieving revision 1.44
diff -u -u -r1.44 Makefile
--- Makefile	21 Dec 2004 09:33:46 -0000	1.44
+++ Makefile	15 Sep 2005 16:52:55 -0000
@@ -1,5 +1,7 @@
 # $FreeBSD: src/lib/libdisk/Makefile,v 1.44 2004/12/21 09:33:46 ru Exp $
 
+.PATH: ${.CURDIR}/../../sys/geom
+
 .if ${MACHINE_ARCH} == "ia64"
 _open_disk=	open_ia64_disk.c
 .else
@@ -9,12 +11,13 @@
 
 LIB=	disk
 SRCS=	blocks.c ${_change} chunk.c create_chunk.c disk.c ${_open_disk} \
-	rules.c write_disk.c write_${MACHINE}_disk.c
+	rules.c write_disk.c write_${MACHINE}_disk.c geom_bsd_enc.c \
+	bsdlabel.c boot0cfg.c
 INCS=	libdisk.h
 
 WARNS?=	2
 
-CFLAGS+=	-I${.CURDIR}/../../sys/geom
+CFLAGS+=	-I${.CURDIR}/../../sys/geom -I${.CURDIR}/../../sbin/bsdlabel
 
 .if ${MACHINE} == "pc98"
 CFLAGS+=	-DPC98
@@ -29,7 +32,7 @@
 .include <bsd.lib.mk>
 
 tst01: tst01.o libdisk.a
-	cc ${CFLAGS} -static tst01.o -o tst01 libdisk.a
+	cc ${CFLAGS} -static tst01.o -o tst01 libdisk.a -lgeom
 
 ad0:	all install tst01
 	./tst01 ad0
Index: boot0cfg.c
===================================================================
RCS file: boot0cfg.c
diff -N boot0cfg.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ boot0cfg.c	15 Sep 2005 16:52:55 -0000
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 1999 Robert Nordier
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+#ifdef _STDIO_H_
+#error "BLAH"
+#endif
+
+#include <sys/param.h>
+#include <sys/disklabel.h>
+#include <sys/diskmbr.h>
+#include <sys/stat.h>
+ 
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <libgeom.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "boot0cfg.h"
+
+/*
+ * Read in the MBR of the disk.  If it is boot0, then use the version to
+ * read in all of it if necessary.  Use pointers to return a malloc'd
+ * buffer containing the MBR and then return its size.
+ */
+int
+read_mbr(const char *disk, u_int8_t **mbr, int check_version)
+{
+    u_int8_t buf[MBRSIZE];
+    int mbr_size, fd;
+    ssize_t n;
+
+    if ((fd = open(disk, O_RDONLY)) == -1)
+        err(1, "open %s", disk);
+    if ((n = read(fd, buf, MBRSIZE)) == -1) {
+        warn("read %s", disk);
+	return -1;
+    }
+    if (n != MBRSIZE) {
+        warnx("%s: short read", disk);
+        return -1;
+    }
+    if (cv2(buf + OFF_MAGIC) != 0xaa55)
+        warnx("%s: bad magic", disk);
+
+    if (!boot0bs(buf)) {
+	if (check_version)
+	    warnx("%s: unknown or incompatible boot code", disk);
+    } else if (boot0version(buf) == 0x101) {
+	mbr_size = 1024;
+	if ((*mbr = malloc(mbr_size)) == NULL)
+	    errx(1, "%s: unable to allocate read buffer", disk);
+	if (lseek(fd, 0, SEEK_SET) == -1 ||
+	    (n = read(fd, *mbr, mbr_size)) == -1)
+	    err(1, "%s", disk);
+	if (n != mbr_size)
+	    errx(1, "%s: short read", disk);
+	return (mbr_size);
+    }
+    *mbr = malloc(sizeof(buf));
+    memcpy(*mbr, buf, sizeof(buf));
+
+    return sizeof(buf);
+}
+
+extern FILE *libdisk_debug; 
+/*
+ * Write out the mbr to the specified file.
+ */
+int
+write_mbr(const char *fname, int flags, u_int8_t *mbr, int mbr_size)
+{
+    int fd, p;
+    ssize_t n;
+    char *s;
+    const char *q;
+    struct gctl_req *grq;
+   
+    fd = open(fname, O_WRONLY | flags, 0666);
+    if (fd != -1) {
+	n = write(fd, mbr, mbr_size);
+	close(fd);
+	if (n != mbr_size) {
+	   warn("%s:%d %s: short write", __FILE__, __LINE__, fname);
+	   return (-1); 
+	}
+	return (0);
+    }
+
+    if (flags != 0) {
+	warn("%s:%d %s", __FILE__, __LINE__, fname);
+	return (-1);
+    }
+    grq = gctl_get_handle();
+    gctl_ro_param(grq, "verb", -1, "write MBR");
+    gctl_ro_param(grq, "class", -1, "MBR");
+    q = strrchr(fname, '/');
+    if (q == NULL)
+	q = fname;
+    else
+	q++;
+    gctl_ro_param(grq, "geom", -1, q);
+    gctl_ro_param(grq, "data", mbr_size, mbr);
+    q = gctl_issue(grq);
+    gctl_dump(grq, libdisk_debug);  
+    if (q == NULL) {
+	warnx("%s:%d Succeeded", __FILE__, __LINE__);
+	gctl_free(grq);
+	return (0);
+    }
+
+    warnx("FAILED: %s:%d %s: %s", __FILE__, __LINE__, fname, q);
+    gctl_free(grq);
+
+#ifdef DIOCSMBR
+    for (p = 1; p < 5; p++) {
+	asprintf(&s, "%ss%d", fname, p);
+	fd = open(s, O_RDONLY);
+	if (fd < 0) {
+	    free(s);
+	    continue;
+	}
+	n = ioctl(fd, DIOCSMBR, (mbr));
+	if (n != 0) {
+	   warn("%s: ioctl DIOCSMBR", fname);
+ 	   return (-1); 
+	}
+	close(fd);
+	free(s);
+	return (0);
+    }
+#endif
+    warn("write_mbr: %s", fname);
+    return (-1);
+}
+
+/*
+ * Return the boot0 version with the minor revision in the low byte, and
+ * the major revision in the next higher byte.
+ */
+int
+boot0version(const u_int8_t *bs)
+{
+    static u_int8_t idold[] = {0xfe, 0x45, 0xf2, 0xe9, 0x00, 0x8a};
+
+    /* Check for old version, and return 0x100 if found. */
+    if (memcmp(bs + 0x1c, idold, sizeof(idold)) == 0)
+        return 0x100;
+
+    /* We have a newer boot0, so extract the version number and return it. */
+    return *(const int *)(bs + OFF_VERSION) & 0xffff;
+}
+
+/*
+ * Decide if we have valid boot0 boot code by looking for
+ * characteristic byte sequences at fixed offsets.
+ */
+int
+boot0bs(const u_int8_t *bs)
+{
+    static u_int8_t id0[] = {0xfc, 0x31, 0xc0, 0x8e, 0xc0, 0x8e, 0xd8,
+			     0x8e, 0xd0, 0xbc, 0x00, 0x7c };
+    static u_int8_t id1[] = {'D', 'r', 'i', 'v', 'e', ' '};
+    static struct {
+	unsigned off;
+	unsigned len;
+	u_int8_t *key;
+    } ident[2] = {
+        {0x0,   sizeof(id0), id0},
+        {0x1b2, sizeof(id1), id1}
+    };
+    unsigned int i;
+
+    for (i = 0; i < sizeof(ident) / sizeof(ident[0]); i++)
+        if (memcmp(bs + ident[i].off, ident[i].key, ident[i].len))
+	    return 0;
+    return 1;
+}
Index: boot0cfg.h
===================================================================
RCS file: boot0cfg.h
diff -N boot0cfg.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ boot0cfg.h	15 Sep 2005 16:52:55 -0000
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1999 Robert Nordier
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BOOT0CFG_H_
+#define _BOOT0CFG_H_
+
+#define MBRSIZE         512     /* master boot record size */
+
+#define OFF_VERSION	0x1b0	/* offset: version number */
+#define OFF_OPT		0x1b9	/* offset: default boot option */
+#define OFF_DRIVE	0x1ba	/* offset: setdrv drive */
+#define OFF_FLAGS       0x1bb   /* offset: option flags */
+#define OFF_TICKS       0x1bc   /* offset: clock ticks */
+#define OFF_PTBL        0x1be   /* offset: partition table */
+#define OFF_MAGIC       0x1fe   /* offset: magic number */
+
+#define cv2(p)  ((p)[0] | (p)[1] << 010)
+
+#define mk2(p, x)                               \
+    (p)[0] = (u_int8_t)(x),                     \
+    (p)[1] = (u_int8_t)((x) >> 010)
+
+__BEGIN_DECLS
+int read_mbr(const char *, u_int8_t **, int);
+int write_mbr(const char *, int, u_int8_t *, int);
+int boot0version(const u_int8_t *);
+int boot0bs(const u_int8_t *);
+__END_DECLS
+
+#endif /* ! _BOOT0CFG_H_ */ 
Index: bsdlabel.c
===================================================================
RCS file: bsdlabel.c
diff -N bsdlabel.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ bsdlabel.c	15 Sep 2005 16:52:55 -0000
@@ -0,0 +1,1313 @@
+/*
+ * Copyright (c) 1994, 1995 Gordon W. Ross
+ * Copyright (c) 1994 Theo de Raadt
+ * All rights reserved.
+ * Copyright (c) 1987, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Symmetric Computer Systems.
+ *
+ * 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.
+ *      This product includes software developed by Theo de Raadt.
+ * 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: $NetBSD: disksubr.c,v 1.13 2000/12/17 22:39:18 pk $
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1987, 1993\n\
+	The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)disklabel.c	8.2 (Berkeley) 1/7/94";
+/* from static char sccsid[] = "@(#)disklabel.c	1.2 (Symmetric) 11/28/85"; */
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sbin/bsdlabel/bsdlabel.c,v 1.111 2005/08/14 22:46:50 iedowse Exp $");
+
+#include <sys/param.h>
+#include <stdint.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/disk.h>
+#define DKTYPENAMES
+#define FSTYPENAMES
+#include <sys/disklabel.h>
+
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <libgeom.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include "bsdlabel.h"
+#include "pathnames.h"
+
+static char	*skip(char *);
+static char	*word(char *);
+static int	editit(void);
+static int	getasciilabel(const char *, FILE *, struct disklabel *, int, int);
+static int	getasciipartspec(char *, struct disklabel *, int, int);
+static struct disklabel *getvirginlabel(const char *specname, int);
+
+#define	DEFEDITOR	_PATH_VI
+
+static char	tmpfil[] = PATH_TMPFILE;
+
+//static struct disklabel lab;
+static off_t	mediasize;
+static u_int	secsize;
+//static char	blank[] = "";
+//static char	unknown[] = "unknown";
+
+#define MAX_PART ('z')
+#define MAX_NUM_PARTS (1 + MAX_PART - 'a')
+static char    part_size_type[MAX_NUM_PARTS];
+static char    part_offset_type[MAX_NUM_PARTS];
+static int     part_set[MAX_NUM_PARTS];
+
+//static int	allfields;	/* present all fields in edit */
+//static char const *xxboot;	/* primary boot */
+
+static off_t mbroffset;
+#ifndef LABELSECTOR
+#define LABELSECTOR -1
+#endif
+#ifndef LABELOFFSET
+#define LABELOFFSET -1
+#endif
+static int labelsoffset = LABELSECTOR;
+static int labeloffset = LABELOFFSET;
+static int bbsize = BBSIZE;
+static int alphacksum =
+#if defined(__alpha__)
+	1;
+#else
+	0;
+#endif
+
+enum	{
+	UNSPEC, EDIT, READ, RESTORE, WRITE, WRITEBOOT
+} op = UNSPEC;
+
+
+void
+fixlabel(struct disklabel *lp)
+{
+	struct partition *dp;
+	int i;
+
+	for (i = 0; i < MAXPARTITIONS; i++) {
+		if (i == RAW_PART)
+			continue;
+		if (lp->d_partitions[i].p_size)
+			return;
+	}
+
+	dp = &lp->d_partitions[0];
+	dp->p_offset = BBSIZE / secsize;
+	dp->p_size = lp->d_secperunit - dp->p_offset;
+}
+
+/*
+ * Construct a prototype disklabel from /etc/disktab.
+ */
+void
+makelabel(const char *specname, const char *type, struct disklabel *lp, int is_file)
+{
+	struct disklabel *dp;
+
+	if (strcmp(type, "auto") == 0)
+		dp = getvirginlabel(specname, is_file);
+	else
+		dp = getdiskbyname(type);
+	if (dp == NULL)
+		errx(1, "%s: unknown disk type", type);
+	*lp = *dp;
+	bzero(lp->d_packname, sizeof(lp->d_packname));
+}
+
+static void
+readboot(const char *xxboot, u_char *bootarea)
+{
+	int fd, i;
+	struct stat st;
+	uint64_t *p;
+
+	if (xxboot == NULL)
+		xxboot = "/boot/boot";
+	fd = open(xxboot, O_RDONLY);
+	if (fd < 0)
+		err(1, "cannot open %s", xxboot);
+	fstat(fd, &st);
+	if (alphacksum && st.st_size <= BBSIZE - 512) {
+		i = read(fd, bootarea + 512, st.st_size);
+		if (i != st.st_size)
+			err(1, "read error %s", xxboot);
+
+		/*
+		 * Set the location and length so SRM can find the
+		 * boot blocks.
+		 */
+		p = (uint64_t *)bootarea;
+		p[60] = (st.st_size + secsize - 1) / secsize;
+		p[61] = 1;
+		p[62] = 0;
+		return;
+	} else if ((!alphacksum) && st.st_size <= BBSIZE) {
+		i = read(fd, bootarea, st.st_size);
+		if (i != st.st_size)
+			err(1, "read error %s", xxboot);
+		return;
+	}
+	errx(1, "boot code %s is wrong size", xxboot);
+}
+
+int
+writelabel(struct disklabel *lp, const char *specname, const char *dkname, const char *xxboot,
+	int disable_write, int is_file, int installboot, int allfields)
+{
+	uint64_t *p, sum;
+	int i, fd;
+	struct gctl_req *grq;
+	char const *errstr;
+	static u_char	bootarea[BBSIZE];
+
+	if (disable_write) {
+		warnx("write to disk label supressed - label was as follows:");
+		display(specname, stdout, NULL, allfields);
+		return (0);
+	}
+
+	lp->d_magic = DISKMAGIC;
+	lp->d_magic2 = DISKMAGIC;
+	lp->d_checksum = 0;
+	lp->d_checksum = dkcksum(lp);
+	if (installboot)
+		readboot(xxboot, bootarea);
+	for (i = 0; i < lp->d_npartitions; i++)
+		if (lp->d_partitions[i].p_size)
+			lp->d_partitions[i].p_offset += mbroffset;
+	bsd_disklabel_le_enc(bootarea + labeloffset + labelsoffset * secsize,
+	    lp);
+	if (alphacksum) {
+		/* Generate the bootblock checksum for the SRM console.  */
+		for (p = (uint64_t *)bootarea, i = 0, sum = 0; i < 63; i++)
+			sum += p[i];
+		p[63] = sum;
+	}
+
+	fd = open(specname, O_RDWR);
+	if (fd < 0) {
+		if (is_file) {
+			warn("cannot open file %s for writing label", specname);
+			return(1);
+		}
+		grq = gctl_get_handle();
+		gctl_ro_param(grq, "verb", -1, "write label");
+		gctl_ro_param(grq, "class", -1, "BSD");
+		gctl_ro_param(grq, "geom", -1, dkname);
+		gctl_ro_param(grq, "label", 148+16*8,
+			bootarea + labeloffset + labelsoffset * secsize);
+		errstr = gctl_issue(grq);
+		if (errstr != NULL) {
+			warnx("%s", errstr);
+			gctl_free(grq);
+			return(1);
+		}
+		gctl_free(grq);
+		if (installboot) {
+			grq = gctl_get_handle();
+			gctl_ro_param(grq, "verb", -1, "write bootcode");
+			gctl_ro_param(grq, "class", -1, "BSD");
+			gctl_ro_param(grq, "geom", -1, dkname);
+			gctl_ro_param(grq, "bootcode", BBSIZE, bootarea);
+			errstr = gctl_issue(grq);
+			if (errstr != NULL) {
+				warnx("%s", errstr);
+				gctl_free(grq);
+				return (1);
+			}
+			gctl_free(grq);
+		}
+	} else {
+		if (write(fd, bootarea, bbsize) != bbsize) {
+			warn("write %s", specname);
+			close (fd);
+			return (1);
+		}
+		close (fd);
+	}
+	return (0);
+}
+
+static void
+get_file_parms(const char *specname, int f)
+{
+	int i;
+	struct stat sb;
+
+	if (fstat(f, &sb) != 0)
+		err(4, "fstat failed");
+	i = sb.st_mode & S_IFMT;
+	if (i != S_IFREG && i != S_IFLNK)
+		errx(4, "%s is not a valid file or link", specname);
+	secsize = DEV_BSIZE;
+	mediasize = sb.st_size;
+}
+
+/*
+ * Fetch disklabel for disk.
+ * Use ioctl to get label unless -r flag is given.
+ */
+int
+readlabel(struct disklabel *lp, const char *specname, const char *dkname, u_char *bootarea,
+	int flag, int is_file)
+{
+	int f, i;
+	int error;
+	struct gctl_req *grq;
+	char const *errstr;
+
+	f = open(specname, O_RDONLY);
+	if (f < 0)
+		err(1, specname);
+	if (is_file)
+		get_file_parms(specname, f);
+	else if ((ioctl(f, DIOCGMEDIASIZE, &mediasize) != 0) ||
+	    (ioctl(f, DIOCGSECTORSIZE, &secsize) != 0)) {
+		err(4, "cannot get disk geometry");
+	}
+	if (mediasize > (off_t)0xffffffff * secsize)
+		errx(1,
+		    "disks with more than 2^32-1 sectors are not supported");
+	(void)lseek(f, (off_t)0, SEEK_SET);
+	if (read(f, bootarea, BBSIZE) != BBSIZE)
+		err(4, "%s read", specname);
+	close (f);
+	error = bsd_disklabel_le_dec(
+	    bootarea + (labeloffset + labelsoffset * secsize),
+	    lp, MAXPARTITIONS);
+	if (flag && error)
+		errx(1, "%s: no valid label found", specname);
+
+	grq = gctl_get_handle();
+	gctl_ro_param(grq, "verb", -1, "read mbroffset");
+	gctl_ro_param(grq, "class", -1, "BSD");
+	gctl_ro_param(grq, "geom", -1, dkname);
+	gctl_rw_param(grq, "mbroffset", sizeof(mbroffset), &mbroffset);
+	errstr = gctl_issue(grq);
+	if (errstr != NULL) {
+		mbroffset = 0;
+		gctl_free(grq);
+		return (error);
+	}
+	if (secsize != 0)
+		mbroffset /= secsize;
+
+	if (lp->d_partitions[RAW_PART].p_offset == mbroffset)
+		for (i = 0; i < lp->d_npartitions; i++)
+			if (lp->d_partitions[i].p_size)
+				lp->d_partitions[i].p_offset -= mbroffset;
+	return (error);
+}
+
+
+void
+display(const char *specname, FILE *f, const struct disklabel *lp, int allfields)
+{
+	int i, j;
+	const struct partition *pp;
+
+	if (lp == NULL)
+		return;
+
+	fprintf(f, "# %s:\n", specname);
+	if (allfields) {
+		if (lp->d_type < DKMAXTYPES)
+			fprintf(f, "type: %s\n", dktypenames[lp->d_type]);
+		else
+			fprintf(f, "type: %u\n", lp->d_type);
+		fprintf(f, "disk: %.*s\n", (int)sizeof(lp->d_typename),
+			lp->d_typename);
+		fprintf(f, "label: %.*s\n", (int)sizeof(lp->d_packname),
+			lp->d_packname);
+		fprintf(f, "flags:");
+		if (lp->d_flags & D_REMOVABLE)
+			fprintf(f, " removeable");
+		if (lp->d_flags & D_ECC)
+			fprintf(f, " ecc");
+		if (lp->d_flags & D_BADSECT)
+			fprintf(f, " badsect");
+		fprintf(f, "\n");
+		fprintf(f, "bytes/sector: %lu\n", (u_long)lp->d_secsize);
+		fprintf(f, "sectors/track: %lu\n", (u_long)lp->d_nsectors);
+		fprintf(f, "tracks/cylinder: %lu\n", (u_long)lp->d_ntracks);
+		fprintf(f, "sectors/cylinder: %lu\n", (u_long)lp->d_secpercyl);
+		fprintf(f, "cylinders: %lu\n", (u_long)lp->d_ncylinders);
+		fprintf(f, "sectors/unit: %lu\n", (u_long)lp->d_secperunit);
+		fprintf(f, "rpm: %u\n", lp->d_rpm);
+		fprintf(f, "interleave: %u\n", lp->d_interleave);
+		fprintf(f, "trackskew: %u\n", lp->d_trackskew);
+		fprintf(f, "cylinderskew: %u\n", lp->d_cylskew);
+		fprintf(f, "headswitch: %lu\t\t# milliseconds\n",
+		    (u_long)lp->d_headswitch);
+		fprintf(f, "track-to-track seek: %ld\t# milliseconds\n",
+		    (u_long)lp->d_trkseek);
+		fprintf(f, "drivedata: ");
+		for (i = NDDATA - 1; i >= 0; i--)
+			if (lp->d_drivedata[i])
+				break;
+		if (i < 0)
+			i = 0;
+		for (j = 0; j <= i; j++)
+			fprintf(f, "%lu ", (u_long)lp->d_drivedata[j]);
+		fprintf(f, "\n\n");
+	}
+	fprintf(f, "%u partitions:\n", lp->d_npartitions);
+	fprintf(f,
+	    "#        size   offset    fstype   [fsize bsize bps/cpg]\n");
+	pp = lp->d_partitions;
+	for (i = 0; i < lp->d_npartitions; i++, pp++) {
+		if (pp->p_size) {
+			fprintf(f, "  %c: %8lu %8lu  ", 'a' + i,
+			   (u_long)pp->p_size, (u_long)pp->p_offset);
+			if (pp->p_fstype < FSMAXTYPES)
+				fprintf(f, "%8.8s", fstypenames[pp->p_fstype]);
+			else
+				fprintf(f, "%8d", pp->p_fstype);
+			switch (pp->p_fstype) {
+
+			case FS_UNUSED:				/* XXX */
+				fprintf(f, "    %5lu %5lu %5.5s ",
+				    (u_long)pp->p_fsize,
+				    (u_long)(pp->p_fsize * pp->p_frag), "");
+				break;
+
+			case FS_BSDFFS:
+				fprintf(f, "    %5lu %5lu %5u ",
+				    (u_long)pp->p_fsize,
+				    (u_long)(pp->p_fsize * pp->p_frag),
+				    pp->p_cpg);
+				break;
+
+			case FS_BSDLFS:
+				fprintf(f, "    %5lu %5lu %5d",
+				    (u_long)pp->p_fsize,
+				    (u_long)(pp->p_fsize * pp->p_frag),
+				    pp->p_cpg);
+				break;
+
+			default:
+				fprintf(f, "%20.20s", "");
+				break;
+			}
+			if (i == RAW_PART) {
+				fprintf(f, "  # \"raw\" part, don't edit");
+			}
+			fprintf(f, "\n");
+		}
+	}
+	fflush(f);
+}
+
+int
+edit(const char *specname, const char *dkname, int disable_write, int is_file, int installboot, int allfields)
+{
+	int c, fd;
+	struct disklabel label;
+	FILE *fp;
+	struct disklabel lab;
+
+	if ((fd = mkstemp(tmpfil)) == -1 ||
+	    (fp = fdopen(fd, "w")) == NULL) {
+		warnx("can't create %s", tmpfil);
+		return (1);
+	}
+	display(specname, fp, NULL, allfields);
+	fclose(fp);
+	for (;;) {
+		if (!editit())
+			break;
+		fp = fopen(tmpfil, "r");
+		if (fp == NULL) {
+			warnx("can't reopen %s for reading", tmpfil);
+			break;
+		}
+		bzero((char *)&label, sizeof(label));
+		c = getasciilabel(specname, fp, &label, is_file, allfields);
+		fclose(fp);
+		if (c) {
+			lab = label;
+			if (writelabel(&lab, specname, dkname, NULL, disable_write,
+				is_file, installboot, allfields) == 0) {
+				(void) unlink(tmpfil);
+				return (0);
+			}
+		}
+		printf("re-edit the label? [y]: ");
+		fflush(stdout);
+		c = getchar();
+		if (c != EOF && c != (int)'\n')
+			while (getchar() != (int)'\n')
+				;
+		if  (c == (int)'n')
+			break;
+	}
+	(void) unlink(tmpfil);
+	return (1);
+}
+
+static int
+editit(void)
+{
+	int pid, xpid;
+	int locstat, omask;
+	const char *ed;
+
+	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
+	while ((pid = fork()) < 0) {
+		if (errno == EPROCLIM) {
+			warnx("you have too many processes");
+			return(0);
+		}
+		if (errno != EAGAIN) {
+			warn("fork");
+			return(0);
+		}
+		sleep(1);
+	}
+	if (pid == 0) {
+		sigsetmask(omask);
+		setgid(getgid());
+		setuid(getuid());
+		if ((ed = getenv("EDITOR")) == (char *)0)
+			ed = DEFEDITOR;
+		execlp(ed, ed, tmpfil, (char *)0);
+		err(1, "%s", ed);
+	}
+	while ((xpid = wait(&locstat)) >= 0)
+		if (xpid == pid)
+			break;
+	sigsetmask(omask);
+	return(!locstat);
+}
+
+static char *
+skip(char *cp)
+{
+
+	while (*cp != '\0' && isspace(*cp))
+		cp++;
+	if (*cp == '\0' || *cp == '#')
+		return (NULL);
+	return (cp);
+}
+
+static char *
+word(char *cp)
+{
+	char c;
+
+	while (*cp != '\0' && !isspace(*cp) && *cp != '#')
+		cp++;
+	if ((c = *cp) != '\0') {
+		*cp++ = '\0';
+		if (c != '#')
+			return (skip(cp));
+	}
+	return (NULL);
+}
+
+/*
+ * Read an ascii label in from fd f,
+ * in the same format as that put out by display(),
+ * and fill in lp.
+ */
+static int
+getasciilabel(const char *specname, FILE *f, struct disklabel *lp, int is_file, int allfields)
+{
+	char *cp;
+	const char **cpp;
+	u_int part;
+	char *tp, line[BUFSIZ];
+	u_long v;
+	int lineno = 0, errors = 0;
+	int i;
+	char	blank[] = "";
+	char	unknown[] = "unknown";
+
+	makelabel(specname, "auto", lp, is_file);
+	bzero(&part_set, sizeof(part_set));
+	bzero(&part_size_type, sizeof(part_size_type));
+	bzero(&part_offset_type, sizeof(part_offset_type));
+	lp->d_bbsize = BBSIZE;				/* XXX */
+	lp->d_sbsize = 0;				/* XXX */
+	while (fgets(line, sizeof(line) - 1, f)) {
+		lineno++;
+		if ((cp = index(line,'\n')) != 0)
+			*cp = '\0';
+		cp = skip(line);
+		if (cp == NULL)
+			continue;
+		tp = index(cp, ':');
+		if (tp == NULL) {
+			fprintf(stderr, "line %d: syntax error\n", lineno);
+			errors++;
+			continue;
+		}
+		*tp++ = '\0', tp = skip(tp);
+		if (!strcmp(cp, "type")) {
+			if (tp == NULL)
+				tp = unknown;
+			cpp = dktypenames;
+			for (; cpp < &dktypenames[DKMAXTYPES]; cpp++)
+				if (*cpp && !strcmp(*cpp, tp)) {
+					lp->d_type = cpp - dktypenames;
+					break;
+				}
+			if (cpp < &dktypenames[DKMAXTYPES])
+				continue;
+			v = strtoul(tp, NULL, 10);
+			if (v >= DKMAXTYPES)
+				fprintf(stderr, "line %d:%s %lu\n", lineno,
+				    "Warning, unknown disk type", v);
+			lp->d_type = v;
+			continue;
+		}
+		if (!strcmp(cp, "flags")) {
+			for (v = 0; (cp = tp) && *cp != '\0';) {
+				tp = word(cp);
+				if (!strcmp(cp, "removeable"))
+					v |= D_REMOVABLE;
+				else if (!strcmp(cp, "ecc"))
+					v |= D_ECC;
+				else if (!strcmp(cp, "badsect"))
+					v |= D_BADSECT;
+				else {
+					fprintf(stderr,
+					    "line %d: %s: bad flag\n",
+					    lineno, cp);
+					errors++;
+				}
+			}
+			lp->d_flags = v;
+			continue;
+		}
+		if (!strcmp(cp, "drivedata")) {
+			for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) {
+				lp->d_drivedata[i++] = strtoul(cp, NULL, 10);
+				tp = word(cp);
+			}
+			continue;
+		}
+		if (sscanf(cp, "%lu partitions", &v) == 1) {
+			if (v == 0 || v > MAXPARTITIONS) {
+				fprintf(stderr,
+				    "line %d: bad # of partitions\n", lineno);
+				lp->d_npartitions = MAXPARTITIONS;
+				errors++;
+			} else
+				lp->d_npartitions = v;
+			continue;
+		}
+		if (tp == NULL)
+			tp = blank;
+		if (!strcmp(cp, "disk")) {
+			strncpy(lp->d_typename, tp, sizeof (lp->d_typename));
+			continue;
+		}
+		if (!strcmp(cp, "label")) {
+			strncpy(lp->d_packname, tp, sizeof (lp->d_packname));
+			continue;
+		}
+		if (!strcmp(cp, "bytes/sector")) {
+			v = strtoul(tp, NULL, 10);
+			if (v == 0 || (v % DEV_BSIZE) != 0) {
+				fprintf(stderr,
+				    "line %d: %s: bad sector size\n",
+				    lineno, tp);
+				errors++;
+			} else
+				lp->d_secsize = v;
+			continue;
+		}
+		if (!strcmp(cp, "sectors/track")) {
+			v = strtoul(tp, NULL, 10);
+#if (ULONG_MAX != 0xffffffffUL)
+			if (v == 0 || v > 0xffffffff)
+#else
+			if (v == 0)
+#endif
+			{
+				fprintf(stderr, "line %d: %s: bad %s\n",
+				    lineno, tp, cp);
+				errors++;
+			} else
+				lp->d_nsectors = v;
+			continue;
+		}
+		if (!strcmp(cp, "sectors/cylinder")) {
+			v = strtoul(tp, NULL, 10);
+			if (v == 0) {
+				fprintf(stderr, "line %d: %s: bad %s\n",
+				    lineno, tp, cp);
+				errors++;
+			} else
+				lp->d_secpercyl = v;
+			continue;
+		}
+		if (!strcmp(cp, "tracks/cylinder")) {
+			v = strtoul(tp, NULL, 10);
+			if (v == 0) {
+				fprintf(stderr, "line %d: %s: bad %s\n",
+				    lineno, tp, cp);
+				errors++;
+			} else
+				lp->d_ntracks = v;
+			continue;
+		}
+		if (!strcmp(cp, "cylinders")) {
+			v = strtoul(tp, NULL, 10);
+			if (v == 0) {
+				fprintf(stderr, "line %d: %s: bad %s\n",
+				    lineno, tp, cp);
+				errors++;
+			} else
+				lp->d_ncylinders = v;
+			continue;
+		}
+		if (!strcmp(cp, "sectors/unit")) {
+			v = strtoul(tp, NULL, 10);
+			if (v == 0) {
+				fprintf(stderr, "line %d: %s: bad %s\n",
+				    lineno, tp, cp);
+				errors++;
+			} else
+				lp->d_secperunit = v;
+			continue;
+		}
+		if (!strcmp(cp, "rpm")) {
+			v = strtoul(tp, NULL, 10);
+			if (v == 0 || v > USHRT_MAX) {
+				fprintf(stderr, "line %d: %s: bad %s\n",
+				    lineno, tp, cp);
+				errors++;
+			} else
+				lp->d_rpm = v;
+			continue;
+		}
+		if (!strcmp(cp, "interleave")) {
+			v = strtoul(tp, NULL, 10);
+			if (v == 0 || v > USHRT_MAX) {
+				fprintf(stderr, "line %d: %s: bad %s\n",
+				    lineno, tp, cp);
+				errors++;
+			} else
+				lp->d_interleave = v;
+			continue;
+		}
+		if (!strcmp(cp, "trackskew")) {
+			v = strtoul(tp, NULL, 10);
+			if (v > USHRT_MAX) {
+				fprintf(stderr, "line %d: %s: bad %s\n",
+				    lineno, tp, cp);
+				errors++;
+			} else
+				lp->d_trackskew = v;
+			continue;
+		}
+		if (!strcmp(cp, "cylinderskew")) {
+			v = strtoul(tp, NULL, 10);
+			if (v > USHRT_MAX) {
+				fprintf(stderr, "line %d: %s: bad %s\n",
+				    lineno, tp, cp);
+				errors++;
+			} else
+				lp->d_cylskew = v;
+			continue;
+		}
+		if (!strcmp(cp, "headswitch")) {
+			v = strtoul(tp, NULL, 10);
+			lp->d_headswitch = v;
+			continue;
+		}
+		if (!strcmp(cp, "track-to-track seek")) {
+			v = strtoul(tp, NULL, 10);
+			lp->d_trkseek = v;
+			continue;
+		}
+		/* the ':' was removed above */
+		if (*cp < 'a' || *cp > MAX_PART || cp[1] != '\0') {
+			fprintf(stderr,
+			    "line %d: %s: Unknown disklabel field\n", lineno,
+			    cp);
+			errors++;
+			continue;
+		}
+
+		/* Process a partition specification line. */
+		part = *cp - 'a';
+		if (part >= lp->d_npartitions) {
+			fprintf(stderr,
+			    "line %d: partition name out of range a-%c: %s\n",
+			    lineno, 'a' + lp->d_npartitions - 1, cp);
+			errors++;
+			continue;
+		}
+		part_set[part] = 1;
+
+		if (getasciipartspec(tp, lp, part, lineno) != 0) {
+			errors++;
+			break;
+		}
+	}
+	errors += checklabel(specname, lp, is_file, allfields);
+	return (errors == 0);
+}
+
+#define NXTNUM(n) do { \
+	if (tp == NULL) { \
+		fprintf(stderr, "line %d: too few numeric fields\n", lineno); \
+		return (1); \
+	} else { \
+		cp = tp, tp = word(cp); \
+		(n) = strtoul(cp, NULL, 10); \
+	} \
+} while (0)
+
+/* retain 1 character following number */
+#define NXTWORD(w,n) do { \
+	if (tp == NULL) { \
+		fprintf(stderr, "line %d: too few numeric fields\n", lineno); \
+		return (1); \
+	} else { \
+		char *tmp; \
+		cp = tp, tp = word(cp); \
+		(n) = strtoul(cp, &tmp, 10); \
+		if (tmp) (w) = *tmp; \
+	} \
+} while (0)
+
+/*
+ * Read a partition line into partition `part' in the specified disklabel.
+ * Return 0 on success, 1 on failure.
+ */
+static int
+getasciipartspec(char *tp, struct disklabel *lp, int part, int lineno)
+{
+	struct partition *pp;
+	char *cp;
+	const char **cpp;
+	u_long v;
+
+	pp = &lp->d_partitions[part];
+	cp = NULL;
+
+	v = 0;
+	NXTWORD(part_size_type[part],v);
+	if (v == 0 && part_size_type[part] != '*') {
+		fprintf(stderr,
+		    "line %d: %s: bad partition size\n", lineno, cp);
+		return (1);
+	}
+	pp->p_size = v;
+
+	v = 0;
+	NXTWORD(part_offset_type[part],v);
+	if (v == 0 && part_offset_type[part] != '*' &&
+	    part_offset_type[part] != '\0') {
+		fprintf(stderr,
+		    "line %d: %s: bad partition offset\n", lineno, cp);
+		return (1);
+	}
+	pp->p_offset = v;
+	if (tp == NULL) {
+		fprintf(stderr, "line %d: missing file system type\n", lineno);
+		return (1);
+	}
+	cp = tp, tp = word(cp);
+	for (cpp = fstypenames; cpp < &fstypenames[FSMAXTYPES]; cpp++)
+		if (*cpp && !strcmp(*cpp, cp))
+			break;
+	if (*cpp != NULL) {
+		pp->p_fstype = cpp - fstypenames;
+	} else {
+		if (isdigit(*cp))
+			v = strtoul(cp, NULL, 10);
+		else
+			v = FSMAXTYPES;
+		if (v >= FSMAXTYPES) {
+			fprintf(stderr,
+			    "line %d: Warning, unknown file system type %s\n",
+			    lineno, cp);
+			v = FS_UNUSED;
+		}
+		pp->p_fstype = v;
+	}
+
+	switch (pp->p_fstype) {
+	case FS_UNUSED:
+	case FS_BSDFFS:
+	case FS_BSDLFS:
+		/* accept defaults for fsize/frag/cpg */
+		if (tp) {
+			NXTNUM(pp->p_fsize);
+			if (pp->p_fsize == 0)
+				break;
+			NXTNUM(v);
+			pp->p_frag = v / pp->p_fsize;
+			if (tp != NULL)
+				NXTNUM(pp->p_cpg);
+		}
+		/* else default to 0's */
+		break;
+	default:
+		break;
+	}
+	return (0);
+}
+
+/*
+ * Check disklabel for errors and fill in
+ * derived fields according to supplied values.
+ */
+int
+checklabel(const char *specname, struct disklabel *lp, int is_file, int allfields)
+{
+	struct partition *pp;
+	int i, errors = 0;
+	char part;
+	u_long base_offset, needed, total_size, total_percent, current_offset;
+	long free_space;
+	int seen_default_offset;
+	int hog_part;
+	int j;
+	struct partition *pp2;
+
+	if (lp == NULL)
+		return(-1);
+
+	if (allfields) {
+
+		if (lp->d_secsize == 0) {
+			fprintf(stderr, "sector size 0\n");
+			return (1);
+		}
+		if (lp->d_nsectors == 0) {
+			fprintf(stderr, "sectors/track 0\n");
+			return (1);
+		}
+		if (lp->d_ntracks == 0) {
+			fprintf(stderr, "tracks/cylinder 0\n");
+			return (1);
+		}
+		if  (lp->d_ncylinders == 0) {
+			fprintf(stderr, "cylinders/unit 0\n");
+			errors++;
+		}
+		if (lp->d_rpm == 0)
+			warnx("revolutions/minute 0");
+		if (lp->d_secpercyl == 0)
+			lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
+		if (lp->d_secperunit == 0)
+			lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
+		if (lp->d_bbsize == 0) {
+			fprintf(stderr, "boot block size 0\n");
+			errors++;
+		} else if (lp->d_bbsize % lp->d_secsize)
+			warnx("boot block size %% sector-size != 0");
+		if (lp->d_npartitions > MAXPARTITIONS)
+			warnx("number of partitions (%lu) > MAXPARTITIONS (%d)",
+			    (u_long)lp->d_npartitions, MAXPARTITIONS);
+	} else {
+		struct disklabel *vl;
+
+		vl = getvirginlabel(specname, is_file);
+		lp->d_secsize = vl->d_secsize;
+		lp->d_nsectors = vl->d_nsectors;
+		lp->d_ntracks = vl->d_ntracks;
+		lp->d_ncylinders = vl->d_ncylinders;
+		lp->d_rpm = vl->d_rpm;
+		lp->d_interleave = vl->d_interleave;
+		lp->d_secpercyl = vl->d_secpercyl;
+		lp->d_secperunit = vl->d_secperunit;
+		lp->d_bbsize = vl->d_bbsize;
+		lp->d_npartitions = vl->d_npartitions;
+	}
+
+
+	/* first allocate space to the partitions, then offsets */
+	total_size = 0; /* in sectors */
+	total_percent = 0; /* in percent */
+	hog_part = -1;
+	/* find all fixed partitions */
+	for (i = 0; i < lp->d_npartitions; i++) {
+		pp = &lp->d_partitions[i];
+		if (part_set[i]) {
+			if (part_size_type[i] == '*') {
+				if (i == RAW_PART) {
+					pp->p_size = lp->d_secperunit;
+				} else {
+					if (hog_part != -1)
+						warnx("Too many '*' partitions (%c and %c)",
+						    hog_part + 'a',i + 'a');
+					else
+						hog_part = i;
+				}
+			} else {
+				off_t size;
+
+				size = pp->p_size;
+				switch (part_size_type[i]) {
+				case '%':
+					total_percent += size;
+					break;
+				case 't':
+				case 'T':
+					size *= 1024ULL;
+					/* FALLTHROUGH */
+				case 'g':
+				case 'G':
+					size *= 1024ULL;
+					/* FALLTHROUGH */
+				case 'm':
+				case 'M':
+					size *= 1024ULL;
+					/* FALLTHROUGH */
+				case 'k':
+				case 'K':
+					size *= 1024ULL;
+					break;
+				case '\0':
+					break;
+				default:
+					warnx("unknown multiplier suffix '%c' for partition %c (should be K, M, G or T)",
+					    part_size_type[i], i + 'a');
+					break;
+				}
+				/* don't count %'s yet */
+				if (part_size_type[i] != '%') {
+					/*
+					 * for all not in sectors, convert to
+					 * sectors
+					 */
+					if (part_size_type[i] != '\0') {
+						if (size % lp->d_secsize != 0)
+							warnx("partition %c not an integer number of sectors",
+							    i + 'a');
+						size /= lp->d_secsize;
+						pp->p_size = size;
+					}
+					/* else already in sectors */
+					if (i != RAW_PART)
+						total_size += size;
+				}
+			}
+		}
+	}
+
+	/* Find out the total free space, excluding the boot block area. */
+	base_offset = BBSIZE / secsize;
+	free_space = 0;
+	for (i = 0; i < lp->d_npartitions; i++) {
+		pp = &lp->d_partitions[i];
+		if (!part_set[i] || i == RAW_PART ||
+		    part_size_type[i] == '%' || part_size_type[i] == '*')
+			continue;
+		if (pp->p_offset > base_offset)
+			free_space += pp->p_offset - base_offset;
+		if (pp->p_offset + pp->p_size > base_offset)
+			base_offset = pp->p_offset + pp->p_size;
+	}
+	if (base_offset < lp->d_secperunit)
+		free_space += lp->d_secperunit - base_offset;
+
+	/* handle % partitions - note %'s don't need to add up to 100! */
+	if (total_percent != 0) {
+		if (total_percent > 100) {
+			fprintf(stderr,"total percentage %lu is greater than 100\n",
+			    total_percent);
+			errors++;
+		}
+
+		if (free_space > 0) {
+			for (i = 0; i < lp->d_npartitions; i++) {
+				pp = &lp->d_partitions[i];
+				if (part_set[i] && part_size_type[i] == '%') {
+					/* careful of overflows! and integer roundoff */
+					pp->p_size = ((double)pp->p_size/100) * free_space;
+					total_size += pp->p_size;
+
+					/* FIX we can lose a sector or so due to roundoff per
+					   partition.  A more complex algorithm could avoid that */
+				}
+			}
+		} else {
+			fprintf(stderr,
+			    "%ld sectors available to give to '*' and '%%' partitions\n",
+			    free_space);
+			errors++;
+			/* fix?  set all % partitions to size 0? */
+		}
+	}
+	/* give anything remaining to the hog partition */
+	if (hog_part != -1) {
+		/*
+		 * Find the range of offsets usable by '*' partitions around
+		 * the hog partition and how much space they need.
+		 */
+		needed = 0;
+		base_offset = BBSIZE / secsize;
+		for (i = hog_part - 1; i >= 0; i--) {
+			pp = &lp->d_partitions[i];
+			if (!part_set[i] || i == RAW_PART)
+				continue;
+			if (part_offset_type[i] == '*') {
+				needed += pp->p_size;
+				continue;
+			}
+			base_offset = pp->p_offset + pp->p_size;
+			break;
+		}
+		current_offset = lp->d_secperunit;
+		for (i = lp->d_npartitions - 1; i > hog_part; i--) {
+			pp = &lp->d_partitions[i];
+			if (!part_set[i] || i == RAW_PART)
+				continue;
+			if (part_offset_type[i] == '*') {
+				needed += pp->p_size;
+				continue;
+			}
+			current_offset = pp->p_offset;
+		}
+
+		if (current_offset - base_offset <= needed) {
+			fprintf(stderr, "Cannot find space for partition %c\n",
+			    hog_part + 'a');
+			fprintf(stderr,
+			    "Need more than %lu sectors between %lu and %lu\n",
+			    needed, base_offset, current_offset);
+			errors++;
+			lp->d_partitions[hog_part].p_size = 0;
+		} else {
+			lp->d_partitions[hog_part].p_size = current_offset -
+			    base_offset - needed;
+			total_size += lp->d_partitions[hog_part].p_size;
+		}
+	}
+
+	/* Now set the offsets for each partition */
+	current_offset = BBSIZE / secsize; /* in sectors */
+	seen_default_offset = 0;
+	for (i = 0; i < lp->d_npartitions; i++) {
+		part = 'a' + i;
+		pp = &lp->d_partitions[i];
+		if (part_set[i]) {
+			if (part_offset_type[i] == '*') {
+				if (i == RAW_PART) {
+					pp->p_offset = 0;
+				} else {
+					pp->p_offset = current_offset;
+					seen_default_offset = 1;
+				}
+			} else {
+				/* allow them to be out of order for old-style tables */
+				if (pp->p_offset < current_offset &&
+				    seen_default_offset && i != RAW_PART &&
+				    pp->p_fstype != FS_VINUM) {
+					fprintf(stderr,
+"Offset %ld for partition %c overlaps previous partition which ends at %lu\n",
+					    (long)pp->p_offset,i+'a',current_offset);
+					fprintf(stderr,
+"Labels with any *'s for offset must be in ascending order by sector\n");
+					errors++;
+				} else if (pp->p_offset != current_offset &&
+				    i != RAW_PART && seen_default_offset) {
+					/*
+					 * this may give unneeded warnings if
+					 * partitions are out-of-order
+					 */
+					warnx(
+"Offset %ld for partition %c doesn't match expected value %ld",
+					    (long)pp->p_offset, i + 'a', current_offset);
+				}
+			}
+			if (i != RAW_PART)
+				current_offset = pp->p_offset + pp->p_size;
+		}
+	}
+
+	for (i = 0; i < lp->d_npartitions; i++) {
+		part = 'a' + i;
+		pp = &lp->d_partitions[i];
+		if (pp->p_size == 0 && pp->p_offset != 0)
+			warnx("partition %c: size 0, but offset %lu",
+			    part, (u_long)pp->p_offset);
+#ifdef notdef
+		if (pp->p_size % lp->d_secpercyl)
+			warnx("partition %c: size %% cylinder-size != 0",
+			    part);
+		if (pp->p_offset % lp->d_secpercyl)
+			warnx("partition %c: offset %% cylinder-size != 0",
+			    part);
+#endif
+		if (pp->p_offset > lp->d_secperunit) {
+			fprintf(stderr,
+			    "partition %c: offset past end of unit\n", part);
+			errors++;
+		}
+		if (pp->p_offset + pp->p_size > lp->d_secperunit) {
+			fprintf(stderr,
+			"partition %c: partition extends past end of unit\n",
+			    part);
+			errors++;
+		}
+		if (i == RAW_PART) {
+			if (pp->p_fstype != FS_UNUSED)
+				warnx("partition %c is not marked as unused!",part);
+			if (pp->p_offset != 0)
+				warnx("partition %c doesn't start at 0!",part);
+			if (pp->p_size != lp->d_secperunit)
+				warnx("partition %c doesn't cover the whole unit!",part);
+
+			if ((pp->p_fstype != FS_UNUSED) || (pp->p_offset != 0) ||
+			    (pp->p_size != lp->d_secperunit)) {
+				warnx("An incorrect partition %c may cause problems for "
+				    "standard system utilities",part);
+			}
+		}
+
+		/* check for overlaps */
+		/* this will check for all possible overlaps once and only once */
+		for (j = 0; j < i; j++) {
+			pp2 = &lp->d_partitions[j];
+			if (j != RAW_PART && i != RAW_PART &&
+			    pp->p_fstype != FS_VINUM &&
+			    pp2->p_fstype != FS_VINUM &&
+			    part_set[i] && part_set[j]) {
+				if (pp2->p_offset < pp->p_offset + pp->p_size &&
+				    (pp2->p_offset + pp2->p_size > pp->p_offset ||
+					pp2->p_offset >= pp->p_offset)) {
+					fprintf(stderr,"partitions %c and %c overlap!\n",
+					    j + 'a', i + 'a');
+					errors++;
+				}
+			}
+		}
+	}
+	for (; i < MAXPARTITIONS; i++) {
+		part = 'a' + i;
+		pp = &lp->d_partitions[i];
+		if (pp->p_size || pp->p_offset)
+			warnx("unused partition %c: size %d offset %lu",
+			    'a' + i, pp->p_size, (u_long)pp->p_offset);
+	}
+	return (errors);
+}
+
+/*
+ * When operating on a "virgin" disk, try getting an initial label
+ * from the associated device driver.  This might work for all device
+ * drivers that are able to fetch some initial device parameters
+ * without even having access to a (BSD) disklabel, like SCSI disks,
+ * most IDE drives, or vn devices.
+ *
+ * The device name must be given in its "canonical" form.
+ */
+static struct disklabel *
+getvirginlabel(const char *specname, int is_file)
+{
+	static struct disklabel loclab;
+	struct partition *dp;
+	int f;
+	u_int u;
+
+	if ((f = open(specname, O_RDONLY)) == -1) {
+		warn("cannot open %s", specname);
+		return (NULL);
+	}
+
+	if (is_file)
+		get_file_parms(specname, f);
+	else if ((ioctl(f, DIOCGMEDIASIZE, &mediasize) != 0) ||
+	    (ioctl(f, DIOCGSECTORSIZE, &secsize) != 0)) {
+		close (f);
+		return (NULL);
+	}
+	memset(&loclab, 0, sizeof loclab);
+	loclab.d_magic = DISKMAGIC;
+	loclab.d_magic2 = DISKMAGIC;
+	loclab.d_secsize = secsize;
+	loclab.d_secperunit = mediasize / secsize;
+
+	/*
+	 * Nobody in these enligthened days uses the CHS geometry for
+	 * anything, but nontheless try to get it right.  If we fail
+	 * to get any good ideas from the device, construct something
+	 * which is IBM-PC friendly.
+	 */
+	if (ioctl(f, DIOCGFWSECTORS, &u) == 0)
+		loclab.d_nsectors = u;
+	else
+		loclab.d_nsectors = 63;
+	if (ioctl(f, DIOCGFWHEADS, &u) == 0)
+		loclab.d_ntracks = u;
+	else if (loclab.d_secperunit <= 63*1*1024)
+		loclab.d_ntracks = 1;
+	else if (loclab.d_secperunit <= 63*16*1024)
+		loclab.d_ntracks = 16;
+	else
+		loclab.d_ntracks = 255;
+	loclab.d_secpercyl = loclab.d_ntracks * loclab.d_nsectors;
+	loclab.d_ncylinders = loclab.d_secperunit / loclab.d_secpercyl;
+	loclab.d_npartitions = MAXPARTITIONS;
+
+	/* Various (unneeded) compat stuff */
+	loclab.d_rpm = 3600;
+	loclab.d_bbsize = BBSIZE;
+	loclab.d_interleave = 1;
+	strncpy(loclab.d_typename, "amnesiac",
+	    sizeof(loclab.d_typename));
+
+	dp = &loclab.d_partitions[RAW_PART];
+	dp->p_size = loclab.d_secperunit;
+	loclab.d_checksum = dkcksum(&loclab);
+	close (f);
+	return (&loclab);
+}
Index: bsdlabel.h
===================================================================
RCS file: bsdlabel.h
diff -N bsdlabel.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ bsdlabel.h	15 Sep 2005 16:52:55 -0000
@@ -0,0 +1,14 @@
+#ifndef _BSDLABEL_H_
+#define _BSD_LABEL_H_
+
+__BEGIN_DECLS
+void	makelabel(const char *, const char *, struct disklabel *, int);
+int	writelabel(struct disklabel *, const char *, const char *, const char *, int, int, int, int);
+int	readlabel(struct disklabel *, const char *, const char *, u_char *, int, int);
+void	display(const char *specname, FILE *, const struct disklabel *, int);
+int	edit(const char *, const char *, int, int, int, int);
+void	fixlabel(struct disklabel *);
+int	checklabel(const char *, struct disklabel *, int, int);
+__END_DECLS
+
+#endif /* ! _BSDLABEL_H_ */
Index: libdisk.h
===================================================================
RCS file: /home/ncvs/src/lib/libdisk/libdisk.h,v
retrieving revision 1.62
diff -u -u -r1.62 libdisk.h
--- libdisk.h	21 Apr 2004 23:21:13 -0000	1.62
+++ libdisk.h	15 Sep 2005 16:52:55 -0000
@@ -10,6 +10,9 @@
 *
 */
 
+#ifndef LIBDISK_H
+#define LIBDISK_H
+
 /* #define DEBUG 1 */
 /* You can define a particular architecture here if you are debugging. */
 /* #define P_DEBUG p_sparc64 */
@@ -361,3 +364,5 @@
  *
  *
  */
+
+#endif /* ! LIBDISK_H */
Index: write_i386_disk.c
===================================================================
RCS file: /home/ncvs/src/lib/libdisk/write_i386_disk.c,v
retrieving revision 1.9
diff -u -u -r1.9 write_i386_disk.c
--- write_i386_disk.c	4 Jun 2004 11:49:11 -0000	1.9
+++ write_i386_disk.c	15 Sep 2005 16:52:55 -0000
@@ -11,6 +11,7 @@
 __FBSDID("$FreeBSD: src/lib/libdisk/write_i386_disk.c,v 1.9 2004/06/04 11:49:11 brian Exp $");
 
 #include <stdio.h>
+#include <libgeom.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <fcntl.h>
@@ -20,39 +21,40 @@
 #include <sys/disklabel.h>
 #include <sys/diskmbr.h>
 #include <paths.h>
+#include <err.h>  /* XXX: debug */
 #include "libdisk.h"
+#include "boot0cfg.h"
+#include "bsdlabel.h"
 
+#define LABELSIZE (148 + 16 * MAXPARTITIONS)
+
+/* XXX: debug */ 
+FILE *libdisk_debug;
+const char *junk = "CRAIG"__DATE__ __TIME__ ;
 /*
  * XXX: A lot of hardcoded 512s probably should be foo->sector_size;
  *	I'm not sure which, so I leave it like it worked before. --schweikh
  */
 static int
-Write_FreeBSD(int fd, const struct disk *new, const struct chunk *c1)
+Write_BSDLabel(const char *device, const struct disk *new, const struct chunk *c1)
 {
-	struct disklabel *dl;
-	int i;
-	void *p;
+	struct disklabel dl = { 0 };
+	//int i, fd;
+	//void *p;
 	u_char buf[BBSIZE];
+	//u_char label[LABELSIZE];
+	int installboot = (new->boot1 || new->boot2);
+	//const char *q;
+	//struct gctl_req *grq;
+	int error;
+
+	readlabel(&dl, device, c1->name, buf, 0, 0);
+	makelabel(device, "auto", &dl, 0);
+	fixlabel(&dl);
+	if(checklabel(device, &dl, 0, 0) == 0)
+		error = writelabel(&dl, device, c1->name, NULL, 0, 0, installboot, 1);
 
-	for (i = 0; i < BBSIZE/512; i++) {
-		if (!(p = read_block(fd, i + c1->offset, 512)))
-			return (1);
-		memcpy(buf + 512 * i, p, 512);
-		free(p);
-	}
-	if (new->boot1)
-		memcpy(buf, new->boot1, 512);
-
-	if (new->boot2)
-		memcpy(buf + 512, new->boot2, BBSIZE - 512);
-
-	dl = (struct disklabel *)(buf + 512 * LABELSECTOR + LABELOFFSET);
-	Fill_Disklabel(dl, new, c1);
-
-	for (i = 0; i < BBSIZE / 512; i++)
-		write_block(fd, i + c1->offset, buf + 512 * i, 512);
-
-	return 0;
+	return(error);
 }
 
 static void
@@ -84,7 +86,7 @@
 int
 Write_Disk(const struct disk *d1)
 {
-	int fd, j;
+	int j, fd;
 	uint i;
 	struct chunk *c1;
 	int ret = 0;
@@ -92,24 +94,24 @@
 	u_char *mbrblk;
 	struct dos_partition *dp,work[NDOSPART];
 	int s[4];
+	int mbr_size;
 	int need_edd = 0;	/* Need EDD (packet interface) */
 
 	strcpy(device, _PATH_DEV);
         strcat(device, d1->name);
 
-        fd = open(device, O_RDWR);
-        if (fd < 0)
-                return 1;
+	/* XXX: debug */
+	libdisk_debug = fopen("/tmp/debug.txt", "w");
+	err_set_file(libdisk_debug);
 
 	memset(s, 0, sizeof s);
-	if (!(mbrblk = read_block(fd, 0, d1->sector_size))) {
-		close (fd);
-		return (1);
+	if ((mbr_size = read_mbr(device, &mbrblk, 1)) < 0) {
+		return 1;
 	}
 	dp = (struct dos_partition *)(mbrblk + DOSPARTOFF);
 	memcpy(work, dp, sizeof work);
 	dp = work;
-	free(mbrblk);
+
 	for (c1 = d1->chunks->part; c1; c1 = c1->next) {
 		if (c1->type == unused)
 			continue;
@@ -119,8 +121,6 @@
 		if (j < 0 || j > 3)
 			continue;
 		s[j]++;
-		if (c1->type == freebsd)
-			ret += Write_FreeBSD(fd, d1, c1);
 
 		Write_Int32(&dp[j].dp_start, c1->offset);
 		Write_Int32(&dp[j].dp_size, c1->size);
@@ -182,10 +182,6 @@
 			if (dp[i].dp_typ == 0xa5)
 				dp[i].dp_flag = 0x80;
 
-	if (!(mbrblk = read_block(fd, 0, d1->sector_size))) {
-		close (fd);
-		return (1);
-	}
 	if (d1->bootmgr) {
 		memcpy(mbrblk, d1->bootmgr, DOSPARTOFF);
 		Cfg_Boot_Mgr(mbrblk, need_edd);
@@ -193,12 +189,40 @@
 	memcpy(mbrblk + DOSPARTOFF, dp, sizeof *dp * NDOSPART);
 	mbrblk[512-2] = 0x55;
 	mbrblk[512-1] = 0xaa;
-	write_block(fd, 0, mbrblk, d1->sector_size);
+
 	if (d1->bootmgr && d1->bootmgr_size > d1->sector_size)
-		for (i = 1; i * d1->sector_size <= d1->bootmgr_size; i++)
-			write_block(fd, i, &d1->bootmgr[i * d1->sector_size],
-				    d1->sector_size);
+		for (i = 1; (i * d1->sector_size <= d1->bootmgr_size) && i + d1->sector_size < mbr_size; i++) {
+			memcpy(&mbrblk[i], &d1->bootmgr[i * d1->sector_size],
+				    d1->sector_size);	
+			
+		}
+	/* XXX: debug */
+	warnx("LIBDISK: writing mbrblk here\n");
+	ret = write_mbr(device, 0, mbrblk, mbr_size);
+	if (ret != 0) {
+	   warnx("LIBDISK: failed writing mbrblk\n");
+	} else { // XXX: extra warning
+	   warnx("LIBDISK: succeeded mbrblk\n");
+	}
+
+	/* open the device to let GEOM reconfigure */
+	fd = open(device, O_WRONLY);
+	if (fd != -1)
+		close(fd);
 
-	close(fd);
-	return 0;
+	for (c1 = d1->chunks->part; c1; c1 = c1->next) {
+		if (c1->type == freebsd) {
+			j = Write_BSDLabel(device, d1, c1);
+			if (j !=0) {
+				warnx("LIBDISK: failed to write bsdlabel %s",
+				    c1->name);
+			} else { /* XXX: extra warning */
+				warnx("LIBDISK: succeeded writing bsdlabel %s",
+				    c1->name);
+			}
+
+			ret +=j;
+		}
+	}
+	return ret;
 }
-------------- next part --------------
Index: Makefile
===================================================================
RCS file: /home/ncvs/src/usr.sbin/sysinstall/Makefile,v
retrieving revision 1.134
diff -u -u -r1.134 Makefile
--- Makefile	19 Mar 2005 02:28:02 -0000	1.134
+++ Makefile	15 Sep 2005 16:53:22 -0000
@@ -24,8 +24,8 @@
 .endif
 CFLAGS+= -I${.CURDIR}/../../gnu/lib/libdialog -I.
 
-DPADD=	${LIBDIALOG} ${LIBNCURSES} ${LIBUTIL} ${LIBDISK} ${LIBFTPIO}
-LDADD=	-ldialog -lncurses -lutil -ldisk -lftpio
+DPADD=	${LIBDIALOG} ${LIBNCURSES} ${LIBUTIL} ${LIBDISK} ${LIBFTPIO} ${LIBGEOM}
+LDADD=	-ldialog -lncurses -lutil -ldisk -lftpio -lgeom
 
 CLEANFILES=	makedevs.c rtermcap
 CLEANFILES+=	keymap.tmp keymap.h


More information about the freebsd-current mailing list