From 1bdb20a68e6b06ee65651d0da1615caccfe04a1a Mon Sep 17 00:00:00 2001
From: Poul-Henning Kamp <phk@FreeBSD.org>
Date: Tue, 9 Apr 2002 15:43:32 +0000
Subject: [PATCH] Implement DIOCGFRONTSTUFF ioctl which reports how many bytes
 from the start of the device magic stuff might occupy.

Sponsored by: DARPA & NAI Labs.
---
 sys/geom/geom_bsd.c      | 21 ++++++++++----------
 sys/geom/geom_dev.c      |  5 ++++-
 sys/geom/geom_disk.c     |  2 ++
 sys/geom/geom_mbr.c      | 43 ++++++++++++++++++++++++++--------------
 sys/geom/geom_pc98.c     | 17 ++++++++--------
 sys/geom/geom_slice.c    | 29 ++++++++++++++++++++++++---
 sys/geom/geom_slice.h    |  2 ++
 sys/geom/geom_sunlabel.c | 11 +++++-----
 sys/kern/subr_disk.c     |  4 ++++
 sys/sys/disk.h           |  8 ++++++++
 10 files changed, 99 insertions(+), 43 deletions(-)

diff --git a/sys/geom/geom_bsd.c b/sys/geom/geom_bsd.c
index af19a4b8c23c..419998adf156 100644
--- a/sys/geom/geom_bsd.c
+++ b/sys/geom/geom_bsd.c
@@ -226,7 +226,7 @@ g_bsd_lesum(struct disklabel *dl, u_char *p)
 }
 
 static int
-g_bsd_i386(struct g_consumer *cp, int secsize, struct disklabel *dl)
+g_bsd_i386(struct g_slicer *gsp, struct g_consumer *cp, int secsize, struct disklabel *dl)
 {
 	int error;
 	u_char *buf;
@@ -242,11 +242,12 @@ g_bsd_i386(struct g_consumer *cp, int secsize, struct disklabel *dl)
 	else
 		error = ENOENT;
 	g_free(buf);
+	gsp->frontstuff = 16 * secsize;
 	return(error);
 }
 
 static int
-g_bsd_alpha(struct g_consumer *cp, int secsize, struct disklabel *dl)
+g_bsd_alpha(struct g_slicer *gsp, struct g_consumer *cp, int secsize, struct disklabel *dl)
 {
 	int error;
 	u_char *buf;
@@ -262,6 +263,7 @@ g_bsd_alpha(struct g_consumer *cp, int secsize, struct disklabel *dl)
 	else
 		error = ENOENT;
 	g_free(buf);
+	gsp->frontstuff = 16 * secsize;
 	return(error);
 }
 
@@ -320,6 +322,7 @@ g_bsd_taste(struct g_class *mp, struct g_provider *pp, int flags)
 	u_int fwsectors, fwheads;
 	off_t mediasize;
 	struct partition *ppp, *ppr;
+	struct g_slicer *gsp;
 
 	g_trace(G_T_TOPOLOGY, "bsd_taste(%s,%s)", mp->name, pp->name);
 	g_topology_assert();
@@ -329,31 +332,29 @@ g_bsd_taste(struct g_class *mp, struct g_provider *pp, int flags)
 	gp = g_slice_new(mp, 8, pp, &cp, &ms, sizeof *ms, g_bsd_start);
 	if (gp == NULL)
 		return (NULL);
+	gsp = gp->softc;
 	g_topology_unlock();
 	gp->dumpconf = g_bsd_dumpconf;
 	npart = 0;
 	while (1) {	/* a trick to allow us to use break */
-		j = sizeof i;
-		error = g_io_getattr("MBR::type", cp, &j, &i);
+		error = g_getattr("MBR::type", cp, &i);
 		if (!error && i != 165 && flags == G_TF_NORMAL)
 			break;
-		j = sizeof secsize;
-		error = g_io_getattr("GEOM::sectorsize", cp, &j, &secsize);
+		error = g_getattr("GEOM::sectorsize", cp, &secsize);
 		if (error) {
 			secsize = 512;
 			printf("g_bsd_taste: error %d Sectors are %d bytes\n",
 			    error, secsize);
 		}
-		j = sizeof mediasize;
-		error = g_io_getattr("GEOM::mediasize", cp, &j, &mediasize);
+		error = g_getattr("GEOM::mediasize", cp, &mediasize);
 		if (error) {
 			mediasize = 0;
 			printf("g_error %d Mediasize is %lld bytes\n",
 			    error, (long long)mediasize);
 		}
-		error = g_bsd_i386(cp, secsize, &ms->ondisk);
+		error = g_bsd_i386(gsp, cp, secsize, &ms->ondisk);
 		if (error)
-			error = g_bsd_alpha(cp, secsize, &ms->ondisk);
+			error = g_bsd_alpha(gsp, cp, secsize, &ms->ondisk);
 		if (error)
 			break;
 		dl = &ms->ondisk;
diff --git a/sys/geom/geom_dev.c b/sys/geom/geom_dev.c
index a2deec1ce040..3711fb04e4bd 100644
--- a/sys/geom/geom_dev.c
+++ b/sys/geom/geom_dev.c
@@ -155,7 +155,7 @@ g_dev_taste(struct g_class *mp, struct g_provider *pp, int insist __unused)
 		error = g_io_getattr("GEOM::mediasize", cp, &j, &mediasize);
 		if (error) {
 			mediasize = 0;
-			printf("g_dev_taste: %d Mediasize is %lld bytes\n",
+			printf("g_dev_taste: error %d Mediasize is %lld bytes\n",
 			    error, (long long)mediasize);
 		}
 		g_topology_lock();
@@ -262,6 +262,9 @@ g_dev_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
 	case DIOCGFWHEADS:
 		error = g_io_getattr("GEOM::fwheads", cp, &i, data);
 		break;
+	case DIOCGFRONTSTUFF:
+		error = g_io_getattr("GEOM::frontstuff", cp, &i, data);
+		break;
 	default:
 		gio = g_malloc(sizeof *gio, M_WAITOK);
 		gio->cmd = cmd;
diff --git a/sys/geom/geom_disk.c b/sys/geom/geom_disk.c
index 3b7b239d96d4..e6a2bcd09757 100644
--- a/sys/geom/geom_disk.c
+++ b/sys/geom/geom_disk.c
@@ -146,6 +146,8 @@ g_disk_start(struct bio *bp)
 		else if (g_haveattr_off_t(bp, "GEOM::mediasize",
 		    dp->d_label.d_secsize * (off_t)dp->d_label.d_secperunit))
 			break;
+		else if (g_haveattr_off_t(bp, "GEOM::frontstuff", 0))
+			break;
 		else if (!strcmp(bp->bio_attribute, "GEOM::ioctl") &&
 		    bp->bio_length == sizeof *gio) {
 			gio = (struct g_ioctl *)bp->bio_data;
diff --git a/sys/geom/geom_mbr.c b/sys/geom/geom_mbr.c
index 9520bde6da03..0dc6c320a4d5 100644
--- a/sys/geom/geom_mbr.c
+++ b/sys/geom/geom_mbr.c
@@ -169,33 +169,35 @@ g_mbr_taste(struct g_class *mp, struct g_provider *pp, int insist)
 	struct g_geom *gp;
 	struct g_consumer *cp;
 	struct g_provider *pp2;
-	int error, i, j, npart;
+	int error, i, npart;
 	struct dos_partition dp[NDOSPART];
 	struct g_mbr_softc *ms;
+	struct g_slicer *gsp;
+	u_int fwsectors, sectorsize;
 	u_char *buf;
 
-	if (sizeof(struct dos_partition) != 16) {
-		printf("WARNING: struct dos_partition compiles to %d bytes, should be 16.\n",
-		    (int)sizeof(struct dos_partition));
-		return (NULL);
-	}
 	g_trace(G_T_TOPOLOGY, "mbr_taste(%s,%s)", mp->name, pp->name);
 	g_topology_assert();
 	gp = g_slice_new(mp, NDOSPART, pp, &cp, &ms, sizeof *ms, g_mbr_start);
 	if (gp == NULL)
 		return (NULL);
+	gsp = gp->softc;
 	g_topology_unlock();
 	gp->dumpconf = g_mbr_dumpconf;
 	npart = 0;
 	while (1) {	/* a trick to allow us to use break */
 		if (gp->rank != 2 && insist == 0)
 			break;
-		j = sizeof i;
-		/* For now we only support 512 bytes sectors */
-		error = g_io_getattr("GEOM::sectorsize", cp, &j, &i);
-		if (!error && i != 512)
+		error = g_getattr("GEOM::fwsectors", cp, &fwsectors);
+		if (error)
+			fwsectors = 17;
+		error = g_getattr("GEOM::sectorsize", cp, &sectorsize);
+		if (error)
 			break;
-		buf = g_read_data(cp, 0, 512, &error);
+		if (!error && sectorsize != 512)
+			break;
+		gsp->frontstuff = sectorsize * fwsectors;
+		buf = g_read_data(cp, 0, sectorsize, &error);
 		if (buf == NULL || error != 0)
 			break;
 		if (buf[0x1fe] != 0x55 && buf[0x1ff] != 0xaa) {
@@ -307,11 +309,13 @@ g_mbrext_taste(struct g_class *mp, struct g_provider *pp, int insist __unused)
 	struct g_geom *gp;
 	struct g_consumer *cp;
 	struct g_provider *pp2;
-	int error, i, j, slice;
+	int error, i, slice;
 	struct g_mbrext_softc *ms;
 	off_t off;
 	u_char *buf;
 	struct dos_partition dp[4];
+	u_int fwsectors, sectorsize;
+	struct g_slicer *gsp;
 
 	g_trace(G_T_TOPOLOGY, "g_mbrext_taste(%s,%s)", mp->name, pp->name);
 	g_topology_assert();
@@ -320,17 +324,26 @@ g_mbrext_taste(struct g_class *mp, struct g_provider *pp, int insist __unused)
 	gp = g_slice_new(mp, NDOSEXTPART, pp, &cp, &ms, sizeof *ms, g_mbrext_start);
 	if (gp == NULL)
 		return (NULL);
+	gsp = gp->softc;
 	g_topology_unlock();
 	gp->dumpconf = g_mbrext_dumpconf;
 	off = 0;
 	slice = 0;
 	while (1) {	/* a trick to allow us to use break */
-		j = sizeof i;
-		error = g_io_getattr("MBR::type", cp, &j, &i);
+		error = g_getattr("MBR::type", cp, &i);
 		if (error || i != DOSPTYP_EXT)
 			break;
+		error = g_getattr("GEOM::fwsectors", cp, &fwsectors);
+		if (error)
+			fwsectors = 17;
+		error = g_getattr("GEOM::sectorsize", cp, &sectorsize);
+		if (error)
+			break;
+		if (!error && sectorsize != 512)
+			break;
+		gsp->frontstuff = sectorsize * fwsectors;
 		for (;;) {
-			buf = g_read_data(cp, off, DEV_BSIZE, &error);
+			buf = g_read_data(cp, off, sectorsize, &error);
 			if (buf == NULL || error != 0)
 				break;
 			if (buf[0x1fe] != 0x55 && buf[0x1ff] != 0xaa)
diff --git a/sys/geom/geom_pc98.c b/sys/geom/geom_pc98.c
index ce5e1814d667..a89bf36452a7 100644
--- a/sys/geom/geom_pc98.c
+++ b/sys/geom/geom_pc98.c
@@ -89,12 +89,13 @@ g_pc98_taste(struct g_class *mp, struct g_provider *pp, int flags)
 	struct g_geom *gp;
 	struct g_consumer *cp;
 	struct g_provider *pp2;
-	int error, i, j, npart;
+	int error, i, npart;
 	u_char *buf;
 	struct g_pc98_softc *ms;
 	u_int sectorsize, u, v;
 	u_int fwsect, fwhead;
 	off_t mediasize, start, length;
+	struct g_slicer *gsp;
 
 	g_trace(G_T_TOPOLOGY, "g_pc98_taste(%s,%s)", mp->name, pp->name);
 	g_topology_assert();
@@ -104,40 +105,38 @@ g_pc98_taste(struct g_class *mp, struct g_provider *pp, int flags)
 	gp = g_slice_new(mp, 8, pp, &cp, &ms, sizeof *ms, g_pc98_start);
 	if (gp == NULL)
 		return (NULL);
+	gsp = gp->softc;
 	g_topology_unlock();
 	gp->dumpconf = g_pc98_dumpconf;
 	npart = 0;
 	while (1) {	/* a trick to allow us to use break */
 		if (gp->rank != 2 && flags == G_TF_NORMAL)
 			break;
-		j = sizeof sectorsize;
-		error = g_io_getattr("GEOM::sectorsize", cp, &j, &sectorsize);
+		error = g_getattr("GEOM::sectorsize", cp, &sectorsize);
 		if (error) {
 			sectorsize = 512;
 			printf("g_pc98_taste: error %d Sectors are %d bytes\n",
 			    error, sectorsize);
 		}
-		j = sizeof mediasize;
-		error = g_io_getattr("GEOM::mediasize", cp, &j, &mediasize);
+		error = g_getattr("GEOM::mediasize", cp, &mediasize);
 		if (error) {
 			mediasize = 0;
 			printf("g_error %d Mediasize is %lld bytes\n",
 			    error, (long long)mediasize);
 		}
-		j = sizeof fwsect;
-		error = g_io_getattr("GEOM::fwsectors", cp, &j, &fwsect);
+		error = g_getattr("GEOM::fwsectors", cp, &fwsect);
 		if (error || fwsect == 0) {
 			fwsect = 17;
 			printf("g_pc98_taste: error %d guessing %d sectors\n",
 			    error, fwsect);
 		}
-		j = sizeof fwhead;
-		error = g_io_getattr("GEOM::fwheads", cp, &j, &fwhead);
+		error = g_getattr("GEOM::fwheads", cp, &fwhead);
 		if (error || fwhead == 0) {
 			fwhead = 8;
 			printf("g_pc98_taste: error %d guessing %d heads\n",
 			    error, fwhead);
 		}
+		gsp->frontstuff = fwsect * sectorsize;
 		buf = g_read_data(cp, 0,
 		    sectorsize < 1024 ? 1024 : sectorsize, &error);
 		if (buf == NULL || error != 0)
diff --git a/sys/geom/geom_slice.c b/sys/geom/geom_slice.c
index 1ac1d0abc602..21b514c114ca 100644
--- a/sys/geom/geom_slice.c
+++ b/sys/geom/geom_slice.c
@@ -129,17 +129,18 @@ g_slice_start(struct bio *bp)
 	struct g_slicer *gsp;
 	struct g_slice *gsl;
 	int index;
+	off_t t;
 
 	pp = bp->bio_to;
 	gp = pp->geom;
 	gsp = gp->softc;
 	cp = LIST_FIRST(&gp->consumer);
 	index = pp->index;
+	gsl = &gsp->slices[index];
 	switch(bp->bio_cmd) {
 	case BIO_READ:
 	case BIO_WRITE:
 	case BIO_DELETE:
-		gsl = &gsp->slices[index];
 		if (bp->bio_offset > gsl->length) {
 			bp->bio_error = EINVAL; /* XXX: EWHAT ? */
 			g_io_deliver(bp);
@@ -154,11 +155,24 @@ g_slice_start(struct bio *bp)
 		return;
 	case BIO_GETATTR:
 	case BIO_SETATTR:
+		/* Give the real method a chance to override */
+		if (gsp->start(bp))
+			return;
 		if (g_haveattr_off_t(bp, "GEOM::mediasize",
 		    gsp->slices[index].length))
 			return;
-		if (gsp->start(bp))
+		if (!strcmp("GEOM::frontstuff", bp->bio_attribute)) {
+			t = gsp->cfrontstuff;
+			if (gsp->frontstuff > t)
+				t = gsp->frontstuff;
+			t -= gsl->offset;
+			if (t < 0)
+				t = 0;
+			if (t > gsl->length)
+				t = gsl->length;
+			g_haveattr_off_t(bp, "GEOM::frontstuff", t);
 			return;
+		}
 		bp2 = g_clone_bio(bp);
 		bp2->bio_done = g_std_done;
 		g_io_request(bp2, cp);
@@ -178,6 +192,10 @@ g_slice_dumpconf(struct sbuf *sb, char *indent, struct g_geom *gp, struct g_cons
 
 	gsp = gp->softc;
 	mp = gsp->softc;
+	if (gp != NULL) {
+		sbuf_printf(sb, "%s<frontstuff>%llu</frontstuff>\n",
+		    indent, (unsigned long long)gsp->frontstuff);
+	}
 	if (pp != NULL) {
 		sbuf_printf(sb, "%s<index>%u</index>\n", indent, pp->index);
 		sbuf_printf(sb, "%s<length>%llu</length>\n",
@@ -224,7 +242,7 @@ g_slice_new(struct g_class *mp, int slices, struct g_provider *pp, struct g_cons
 	struct g_slicer *gsp;
 	struct g_consumer *cp;
 	void **vp;
-	int error;
+	int error, i;
 
 	g_topology_assert();
 	vp = (void **)extrap;
@@ -248,6 +266,11 @@ g_slice_new(struct g_class *mp, int slices, struct g_provider *pp, struct g_cons
 		g_destroy_geom(gp);
 		return (NULL);
 	}
+	/* Find out if there are any magic bytes on the consumer */
+	i = sizeof gsp->cfrontstuff;
+	error = g_io_getattr("GEOM::frontstuff", cp, &i, &gsp->cfrontstuff);
+	if (error)
+		gsp->cfrontstuff = 0;
 	*vp = gsp->softc;
 	*cpp = cp;
 	return (gp);
diff --git a/sys/geom/geom_slice.h b/sys/geom/geom_slice.h
index 4d215333252d..acc20ee08f7c 100644
--- a/sys/geom/geom_slice.h
+++ b/sys/geom/geom_slice.h
@@ -45,6 +45,8 @@ typedef int g_slice_start_t (struct bio *bp);
 
 struct g_slicer {
 	int		nslice;
+	off_t		cfrontstuff;
+	off_t		frontstuff;
 	struct g_slice	*slices;
 	void		*softc;
 	g_slice_start_t	*start;
diff --git a/sys/geom/geom_sunlabel.c b/sys/geom/geom_sunlabel.c
index 78d9c8702644..3363f4e325c3 100644
--- a/sys/geom/geom_sunlabel.c
+++ b/sys/geom/geom_sunlabel.c
@@ -89,11 +89,12 @@ g_sunlabel_taste(struct g_class *mp, struct g_provider *pp, int flags)
 	struct g_geom *gp;
 	struct g_consumer *cp;
 	struct g_provider *pp2;
-	int error, i, j, npart;
+	int error, i, npart;
 	u_char *buf;
 	struct g_sunlabel_softc *ms;
 	u_int sectorsize, u, v, csize;
 	off_t mediasize;
+	struct g_slicer *gsp;
 
 	g_trace(G_T_TOPOLOGY, "g_sunlabel_taste(%s,%s)", mp->name, pp->name);
 	g_topology_assert();
@@ -103,21 +104,21 @@ g_sunlabel_taste(struct g_class *mp, struct g_provider *pp, int flags)
 	gp = g_slice_new(mp, 8, pp, &cp, &ms, sizeof *ms, g_sunlabel_start);
 	if (gp == NULL)
 		return (NULL);
+	gsp = gp->softc;
 	g_topology_unlock();
 	gp->dumpconf = g_sunlabel_dumpconf;
 	npart = 0;
 	while (1) {	/* a trick to allow us to use break */
 		if (gp->rank != 2 && flags == G_TF_NORMAL)
 			break;
-		j = sizeof sectorsize;
-		error = g_io_getattr("GEOM::sectorsize", cp, &j, &sectorsize);
+		error = g_getattr("GEOM::sectorsize", cp, &sectorsize);
 		if (error) {
 			sectorsize = 512;
 			printf("g_sunlabel_taste: error %d Sectors are %d bytes\n",
 			    error, sectorsize);
 		}
-		j = sizeof mediasize;
-		error = g_io_getattr("GEOM::mediasize", cp, &j, &mediasize);
+		gsp->frontstuff = 16 * sectorsize;
+		error = g_getattr("GEOM::mediasize", cp, &mediasize);
 		if (error) {
 			mediasize = 0;
 			printf("g_error %d Mediasize is %lld bytes\n",
diff --git a/sys/kern/subr_disk.c b/sys/kern/subr_disk.c
index b1f3e6a9e452..1982e7f68c38 100644
--- a/sys/kern/subr_disk.c
+++ b/sys/kern/subr_disk.c
@@ -394,6 +394,10 @@ diskioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
 		u = *(u_int *)data;
 		return (diskdumpconf(u, dev, dp));
 	}
+	if (cmd == DIOCGFRONTSTUFF) {
+		*(off_t *)data = 8192;	/* XXX: crude but enough) */
+		return (0);
+	}
 	error = dsioctl(dev, cmd, data, fflag, &dp->d_slice);
 	if (error == ENOIOCTL)
 		error = dp->d_devsw->d_ioctl(dev, cmd, data, fflag, td);
diff --git a/sys/sys/disk.h b/sys/sys/disk.h
index a0fbb0d8613d..ffbd7868ef2b 100644
--- a/sys/sys/disk.h
+++ b/sys/sys/disk.h
@@ -80,4 +80,12 @@ void disk_invalidate(struct disk *disk);
 	 * core dumps.
 	 */
 	
+#define DIOCGFRONTSTUFF _IOR('d', 134, off_t)
+	/*-
+	 * Many disk formats have some amount of space reserved at the
+	 * start of the disk to hold bootblocks, various disklabels and
+	 * similar stuff.  This ioctl returns the number of such bytes
+	 * which may apply to the device.
+	 */
+
 #endif /* _SYS_DISK_H_ */