Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 25 Oct 2010 16:23:35 +0000 (UTC)
From:      "Andrey V. Elsukov" <ae@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r214352 - in head: sbin/geom/class/part sys/geom/part
Message-ID:  <201010251623.o9PGNZ3V070926@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ae
Date: Mon Oct 25 16:23:35 2010
New Revision: 214352
URL: http://svn.freebsd.org/changeset/base/214352

Log:
  Reimplemented "gpart destroy -F". Now it does all work in kernel.
  This was needed for recover implementation.
  
  Implement the recover command for GPT. Now GPT will marked as
  corrupt when any of three types of corruption will be detected:
  1. Damaged primary GPT header or table
  2. Damaged secondary GPT header or table
  3. Secondary header is not located in the last LBA
  Marked GPT becomes read-only. Any changes with corrupt table
  are prohibited. Only "destroy" and "recover" commands are allowed.
  
  Discussed with:	geom@ (mostly silence)
  Tested by:	Ilya A. Arhipov
  Approved by:	mav (mentor)
  MFC after:	2 weeks

Modified:
  head/sbin/geom/class/part/geom_part.c
  head/sbin/geom/class/part/gpart.8
  head/sys/geom/part/g_part.c
  head/sys/geom/part/g_part.h
  head/sys/geom/part/g_part_gpt.c
  head/sys/geom/part/g_part_if.m

Modified: head/sbin/geom/class/part/geom_part.c
==============================================================================
--- head/sbin/geom/class/part/geom_part.c	Mon Oct 25 16:11:37 2010	(r214351)
+++ head/sbin/geom/class/part/geom_part.c	Mon Oct 25 16:23:35 2010	(r214352)
@@ -67,7 +67,6 @@ static char ssize[32];
 #define	GPART_PARAM_BOOTCODE	"bootcode"
 #define	GPART_PARAM_INDEX	"index"
 #define	GPART_PARAM_PARTCODE	"partcode"
-#define	GPART_PARAM_FORCE	"force"
 
 static struct gclass *find_class(struct gmesh *, const char *);
 static struct ggeom * find_geom(struct gclass *, const char *);
@@ -85,7 +84,6 @@ static void gpart_show_geom(struct ggeom
 static int gpart_show_hasopt(struct gctl_req *, const char *, const char *);
 static void gpart_write_partcode(struct ggeom *, int, void *, ssize_t);
 static void gpart_write_partcode_vtoc8(struct ggeom *, int, void *);
-static void gpart_destroy(struct gctl_req *, unsigned int);
 static void gpart_print_error(const char *);
 
 struct g_command PUBSYM(class_commands)[] = {
@@ -123,8 +121,8 @@ struct g_command PUBSYM(class_commands)[
 		G_OPT_SENTINEL },
 	    "-i index [-f flags] geom"
 	},
-	{ "destroy", 0, gpart_destroy, {
-		{ 'F', GPART_PARAM_FORCE, NULL, G_TYPE_BOOL },
+	{ "destroy", 0, gpart_issue, {
+		{ 'F', "force", NULL, G_TYPE_BOOL },
 		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
 		G_OPT_SENTINEL },
 	    "[-F] [-f flags] geom"
@@ -167,6 +165,11 @@ struct g_command PUBSYM(class_commands)[
 		G_OPT_SENTINEL },
 	    "[-s size] -i index [-f flags] geom"
 	},
+	{ "recover", 0, gpart_issue, {
+		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
+		G_OPT_SENTINEL },
+	    "[-f flags] geom"
+	},
 	G_CMD_SENTINEL
 };
 
@@ -539,13 +542,17 @@ gpart_show_geom(struct ggeom *gp, const 
 	s = find_geomcfg(gp, "last");
 	last = (off_t)strtoimax(s, NULL, 0);
 	wblocks = strlen(s);
+	s = find_geomcfg(gp, "state");
+	if (s != NULL && *s != 'C')
+		s = NULL;
 	wname = strlen(gp->lg_name);
 	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
 	secsz = pp->lg_sectorsize;
-	printf("=>%*jd  %*jd  %*s  %s  (%s)\n",
+	printf("=>%*jd  %*jd  %*s  %s  (%s)%s\n",
 	    wblocks, (intmax_t)first, wblocks, (intmax_t)(last - first + 1),
 	    wname, gp->lg_name,
-	    scheme, fmtsize(pp->lg_mediasize));
+	    scheme, fmtsize(pp->lg_mediasize),
+	    s ? " [CORRUPT]": "");
 
 	while ((pp = find_provider(gp, first)) != NULL) {
 		s = find_provcfg(pp, "start");
@@ -858,83 +865,6 @@ gpart_bootcode(struct gctl_req *req, uns
 }
 
 static void
-gpart_destroy(struct gctl_req *req, unsigned int fl)
-{
-	struct gmesh mesh;
-	struct gclass *classp;
-	struct gctl_req *req2;
-	struct ggeom *gp;
-	struct gprovider *pp;
-	const char *s;
-	int error, val;
-	intmax_t idx;
-
-	if (gctl_has_param(req, GPART_PARAM_FORCE)) {
-		val = gctl_get_int(req, GPART_PARAM_FORCE);
-		error = gctl_delete_param(req, GPART_PARAM_FORCE);
-		if (error)
-			errc(EXIT_FAILURE, error, "internal error");
-		if (val == 0)
-			goto done;
-		s = gctl_get_ascii(req, "class");
-		if (s == NULL)
-			abort();
-		error = geom_gettree(&mesh);
-		if (error != 0)
-			errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
-		classp = find_class(&mesh, s);
-		if (classp == NULL) {
-			geom_deletetree(&mesh);
-			errx(EXIT_FAILURE, "Class %s not found.", s);
-		}
-		s = gctl_get_ascii(req, "arg0");
-		if (s == NULL)
-			abort();
-		gp = find_geom(classp, s);
-		if (gp == NULL)
-			errx(EXIT_FAILURE, "No such geom: %s.", s);
-		val = 0;
-		LIST_FOREACH(pp, &gp->lg_provider, lg_provider){
-			s = find_provcfg(pp, "index");
-			if (s == NULL)
-				errx(EXIT_FAILURE, "Index not found for %s.",
-				    pp->lg_name);
-			idx = strtoimax(s, NULL, 0);
-			req2 = gctl_get_handle();
-			gctl_ro_param(req2, "class", -1, classp->lg_name);
-			gctl_ro_param(req2, "arg0", -1, gp->lg_name);
-			gctl_ro_param(req2, "verb", -1, "delete");
-			gctl_ro_param(req2, GPART_PARAM_INDEX,
-			    sizeof(intmax_t), &idx);
-			gctl_ro_param(req2, "flags", -1, "X");
-			s = gctl_issue(req2);
-			if (s != NULL && s[0] != '\0') {
-				gpart_print_error(s);
-				gctl_free(req2);
-				if (val) { /* try to undo changes */
-					req2 = gctl_get_handle();
-					gctl_ro_param(req2, "verb", -1,
-					    "undo");
-					gctl_ro_param(req2, "class", -1,
-					    classp->lg_name);
-					gctl_ro_param(req2, "arg0", -1,
-					    gp->lg_name);
-					gctl_issue(req2);
-					gctl_free(req2);
-				}
-				geom_deletetree(&mesh);
-				exit(EXIT_FAILURE);
-			}
-			gctl_free(req2);
-			val = 1;
-		}
-		geom_deletetree(&mesh);
-	}
-done:
-	gpart_issue(req, fl);
-}
-
-static void
 gpart_print_error(const char *errstr)
 {
 	char *errmsg;

Modified: head/sbin/geom/class/part/gpart.8
==============================================================================
--- head/sbin/geom/class/part/gpart.8	Mon Oct 25 16:11:37 2010	(r214351)
+++ head/sbin/geom/class/part/gpart.8	Mon Oct 25 16:23:35 2010	(r214352)
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd September 28, 2010
+.Dd October 25, 2010
 .Dt GPART 8
 .Os
 .Sh NAME
@@ -129,6 +129,11 @@ utility:
 .Op Fl t Ar type
 .Op Fl f Ar flags
 .Ar geom
+.\" ==== RECOVER ====
+.Nm
+.Cm recover
+.Op Fl f Ar flags
+.Ar geom
 .\" ==== RESIZE ====
 .Nm
 .Cm resize
@@ -353,6 +358,23 @@ See the section entitled
 below for a discussion
 about its use.
 .El
+.\" ==== RECOVER ====
+.It Cm recover
+Recover corrupt partition's scheme metadata on the geom
+.Ar geom .
+See the section entitled
+.Sx "RECOVERING"
+below for the additional information.
+.Pp
+Additional options include:
+.Bl -tag -width 10n
+.It Fl f Ar flags
+Additional operational flags.
+See the section entitled
+.Sx "OPERATIONAL FLAGS"
+below for a discussion
+about its use.
+.El
 .\" ==== RESIZE ====
 .It Cm resize
 Resize a partition from geom
@@ -629,6 +651,68 @@ the
 action or reverted with the
 .Cm undo
 action.
+.Sh RECOVERING
+The GEOM class PART supports recovering of partition tables only for GPT.
+The GUID partition table has a primary and secondary (backup) copy of
+metadata for redundance. They are stored in the begining and in the end
+of device respectively. Therefore it is acceptable to have some corruptions
+in the metadata that are not fatal to work with GPT. When kernel detects
+corrupt metadata it marks this table as corrupt and reports about corruption.
+Any changes in corrupt table are prohibited except
+.Cm destroy
+and
+.Cm recover . 
+.Pp
+In case when only first sector is corrupt kernel can not detect GPT even
+if partition table is not corrupt. You can write protective MBR with
+.Xr dd 1
+command to restore ability of GPT detection. The copy of protective MBR is
+usually located in the
+.Pa /boot/pmbr
+file.
+.Pp
+In case when some of metadata is corrupt you will get to know about this
+from kernel's messages like these:
+.Bd -literal -offset indent
+GEOM: provider: the primary GPT table is corrupt or invalid.
+GEOM: provider: using the secondary instead -- recovery strongly advised.
+.Ed
+.Pp
+or
+.Bd -literal -offset indent
+GEOM: provider: the secondary GPT table is corrupt or invalid.
+GEOM: provider: using the primary only -- recovery suggested.
+.Ed
+.Pp
+Also 
+.Cm gpart 
+commands like 
+.Cm show ,
+.Cm status
+and
+.Cm list
+will report about corrupt table.
+.Pp
+In case when the size of device has changed (e.g. volume expansion) the 
+secondary GPT header will become located not in the last sector. This is 
+not a metadata corruption, but it is dangerous because any corruption of 
+the primary GPT will lead to lost of partition table. Kernel reports about 
+this problem with message:
+.Bd -literal -offset indent
+GEOM: provider: the secondary GPT header is not in the last LBA.
+.Ed
+.Pp
+A corrupt table can be recovered with 
+.Cm gpart recover 
+command. This command does reconstruction of corrupt metadata using 
+known valid metadata. Also it can relocate secondary GPT to the end of 
+device.
+.Pp
+.Pa NOTE :
+The GEOM class PART can detect the same partition table on different GEOM 
+providers and some of them will marked as corrupt. Be careful when choising 
+a provider for recovering. If you did incorrect choise you can destroy 
+metadata of another GEOM class, e.g. GEOM MIRROR or GEOM LABEL.
 .Sh EXIT STATUS
 Exit status is 0 on success, and 1 if the command fails.
 .Sh EXAMPLES
@@ -687,6 +771,7 @@ After having created all required partit
 /sbin/gpart bootcode -p /boot/boot1 da0
 .Ed
 .Sh SEE ALSO
+.Xr dd 1 ,
 .Xr geom 4 ,
 .Xr geom 8
 .Sh HISTORY

Modified: head/sys/geom/part/g_part.c
==============================================================================
--- head/sys/geom/part/g_part.c	Mon Oct 25 16:11:37 2010	(r214351)
+++ head/sys/geom/part/g_part.c	Mon Oct 25 16:23:35 2010	(r214352)
@@ -924,7 +924,7 @@ g_part_ctl_destroy(struct gctl_req *req,
 	struct g_consumer *cp;
 	struct g_geom *gp;
 	struct g_provider *pp;
-	struct g_part_entry *entry;
+	struct g_part_entry *entry, *tmp;
 	struct g_part_table *null, *table;
 	struct sbuf *sb;
 	int error;
@@ -934,13 +934,34 @@ g_part_ctl_destroy(struct gctl_req *req,
 	g_topology_assert();
 
 	table = gp->softc;
+	/* Check for busy providers. */
 	LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
 		if (entry->gpe_deleted || entry->gpe_internal)
 			continue;
+		if (gpp->gpp_force) {
+			pp = entry->gpe_pp;
+			if (pp == NULL)
+				continue;
+			if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0)
+				continue;
+		}
 		gctl_error(req, "%d", EBUSY);
 		return (EBUSY);
 	}
 
+	if (gpp->gpp_force) {
+		/* Destroy all providers. */
+		LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) {
+			pp = entry->gpe_pp;
+			if (pp != NULL) {
+				pp->private = NULL;
+				g_wither_provider(pp, ENXIO);
+			}
+			LIST_REMOVE(entry, gpe_entry);
+			g_free(entry);
+		}
+	}
+
 	error = G_PART_DESTROY(table, gpp);
 	if (error) {
 		gctl_error(req, "%d", error);
@@ -1037,8 +1058,39 @@ g_part_ctl_move(struct gctl_req *req, st
 static int
 g_part_ctl_recover(struct gctl_req *req, struct g_part_parms *gpp)
 {
-	gctl_error(req, "%d verb 'recover'", ENOSYS);
-	return (ENOSYS);
+	struct g_part_table *table;
+	struct g_geom *gp;
+	struct sbuf *sb;
+	int error, recovered;
+
+	gp = gpp->gpp_geom;
+	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
+	g_topology_assert();
+	table = gp->softc;
+	error = recovered = 0;
+
+	if (table->gpt_corrupt) {
+		error = G_PART_RECOVER(table);
+		if (error) {
+			gctl_error(req, "%d recovering '%s' failed",
+			    error, gp->name);
+			return (error);
+		}
+		recovered = 1;
+	}
+	/* Provide feedback if so requested. */
+	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
+		sb = sbuf_new_auto();
+		if (recovered)
+			sbuf_printf(sb, "%s recovered\n", gp->name);
+		else
+			sbuf_printf(sb, "%s recovering is not needed\n",
+			    gp->name);
+		sbuf_finish(sb);
+		gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
+		sbuf_delete(sb);
+	}
+	return (0);
 }
 
 static int
@@ -1341,6 +1393,7 @@ g_part_ctlreq(struct gctl_req *req, stru
 		} else if (!strcmp(verb, "destroy")) {
 			ctlreq = G_PART_CTL_DESTROY;
 			mparms |= G_PART_PARM_GEOM;
+			oparms |= G_PART_PARM_FORCE;
 		}
 		break;
 	case 'm':
@@ -1415,6 +1468,8 @@ g_part_ctlreq(struct gctl_req *req, stru
 		case 'f':
 			if (!strcmp(ap->name, "flags"))
 				parm = G_PART_PARM_FLAGS;
+			else if (!strcmp(ap->name, "force"))
+				parm = G_PART_PARM_FORCE;
 			break;
 		case 'i':
 			if (!strcmp(ap->name, "index"))
@@ -1453,7 +1508,8 @@ g_part_ctlreq(struct gctl_req *req, stru
 		}
 		switch (parm) {
 		case G_PART_PARM_ATTRIB:
-			error = g_part_parm_str(req, ap->name, &gpp.gpp_attrib);
+			error = g_part_parm_str(req, ap->name,
+			    &gpp.gpp_attrib);
 			break;
 		case G_PART_PARM_BOOTCODE:
 			error = g_part_parm_bootcode(req, ap->name,
@@ -1466,11 +1522,16 @@ g_part_ctlreq(struct gctl_req *req, stru
 		case G_PART_PARM_FLAGS:
 			error = g_part_parm_str(req, ap->name, &gpp.gpp_flags);
 			break;
+		case G_PART_PARM_FORCE:
+			error = g_part_parm_uint32(req, ap->name,
+			    &gpp.gpp_force);
+			break;
 		case G_PART_PARM_GEOM:
 			error = g_part_parm_geom(req, ap->name, &gpp.gpp_geom);
 			break;
 		case G_PART_PARM_INDEX:
-			error = g_part_parm_intmax(req, ap->name, &gpp.gpp_index);
+			error = g_part_parm_intmax(req, ap->name,
+			    &gpp.gpp_index);
 			break;
 		case G_PART_PARM_LABEL:
 			error = g_part_parm_str(req, ap->name, &gpp.gpp_label);
@@ -1490,7 +1551,8 @@ g_part_ctlreq(struct gctl_req *req, stru
 			error = g_part_parm_quad(req, ap->name, &gpp.gpp_size);
 			break;
 		case G_PART_PARM_START:
-			error = g_part_parm_quad(req, ap->name, &gpp.gpp_start);
+			error = g_part_parm_quad(req, ap->name,
+			    &gpp.gpp_start);
 			break;
 		case G_PART_PARM_TYPE:
 			error = g_part_parm_str(req, ap->name, &gpp.gpp_type);
@@ -1524,6 +1586,13 @@ g_part_ctlreq(struct gctl_req *req, stru
 	table = NULL;
 	if (modifies && (gpp.gpp_parms & G_PART_PARM_GEOM)) {
 		table = gpp.gpp_geom->softc;
+		if (table != NULL && table->gpt_corrupt &&
+		    ctlreq != G_PART_CTL_DESTROY &&
+		    ctlreq != G_PART_CTL_RECOVER) {
+			gctl_error(req, "%d table '%s' is corrupt",
+			    EPERM, gpp.gpp_geom->name);
+			return;
+		}
 		if (table != NULL && !table->gpt_opened) {
 			error = g_access(LIST_FIRST(&gpp.gpp_geom->consumer),
 			    1, 1, 1);
@@ -1789,6 +1858,8 @@ g_part_dumpconf(struct sbuf *sb, const c
 		    table->gpt_sectors);
 		sbuf_printf(sb, "%s<fwheads>%u</fwheads>\n", indent,
 		    table->gpt_heads);
+		sbuf_printf(sb, "%s<state>%s</state>\n", indent,
+		    table->gpt_corrupt ? "CORRUPT": "OK");
 		G_PART_DUMPCONF(table, NULL, sb, indent);
 	}
 }

Modified: head/sys/geom/part/g_part.h
==============================================================================
--- head/sys/geom/part/g_part.h	Mon Oct 25 16:11:37 2010	(r214351)
+++ head/sys/geom/part/g_part.h	Mon Oct 25 16:23:35 2010	(r214352)
@@ -132,6 +132,7 @@ struct g_part_table {
 	int		gpt_modified:1;	/* Table changes have been made. */
 	int		gpt_opened:1;	/* Permissions obtained. */
 	int		gpt_fixgeom:1;	/* Geometry is fixed. */
+	int		gpt_corrupt:1;	/* Table is corrupt. */
 };
 
 struct g_part_entry *g_part_new_entry(struct g_part_table *, int, quad_t,
@@ -169,6 +170,7 @@ enum g_part_ctl {
 #define	G_PART_PARM_VERSION	0x0800
 #define	G_PART_PARM_BOOTCODE	0x1000
 #define	G_PART_PARM_ATTRIB	0x2000
+#define	G_PART_PARM_FORCE	0x4000
 
 struct g_part_parms {
 	unsigned int	gpp_parms;
@@ -186,6 +188,7 @@ struct g_part_parms {
 	const void	*gpp_codeptr;
 	unsigned int	gpp_codesize;
 	const char	*gpp_attrib;
+	unsigned int	gpp_force;
 };
 
 void g_part_geometry_heads(off_t, u_int, off_t *, u_int *);

Modified: head/sys/geom/part/g_part_gpt.c
==============================================================================
--- head/sys/geom/part/g_part_gpt.c	Mon Oct 25 16:11:37 2010	(r214351)
+++ head/sys/geom/part/g_part_gpt.c	Mon Oct 25 16:23:35 2010	(r214352)
@@ -94,7 +94,7 @@ static int g_part_gpt_destroy(struct g_p
 static void g_part_gpt_dumpconf(struct g_part_table *, struct g_part_entry *,
     struct sbuf *, const char *);
 static int g_part_gpt_dumpto(struct g_part_table *, struct g_part_entry *);
-static int g_part_gpt_modify(struct g_part_table *, struct g_part_entry *,  
+static int g_part_gpt_modify(struct g_part_table *, struct g_part_entry *,
     struct g_part_parms *);
 static const char *g_part_gpt_name(struct g_part_table *, struct g_part_entry *,
     char *, size_t);
@@ -107,6 +107,7 @@ static const char *g_part_gpt_type(struc
 static int g_part_gpt_write(struct g_part_table *, struct g_consumer *);
 static int g_part_gpt_resize(struct g_part_table *, struct g_part_entry *,
     struct g_part_parms *);
+static int g_part_gpt_recover(struct g_part_table *);
 
 static kobj_method_t g_part_gpt_methods[] = {
 	KOBJMETHOD(g_part_add,		g_part_gpt_add),
@@ -120,6 +121,7 @@ static kobj_method_t g_part_gpt_methods[
 	KOBJMETHOD(g_part_name,		g_part_gpt_name),
 	KOBJMETHOD(g_part_probe,	g_part_gpt_probe),
 	KOBJMETHOD(g_part_read,		g_part_gpt_read),
+	KOBJMETHOD(g_part_recover,	g_part_gpt_recover),
 	KOBJMETHOD(g_part_setunset,	g_part_gpt_setunset),
 	KOBJMETHOD(g_part_type,		g_part_gpt_type),
 	KOBJMETHOD(g_part_write,	g_part_gpt_write),
@@ -170,7 +172,7 @@ static struct uuid gpt_uuid_unused = GPT
 
 static struct g_part_uuid_alias {
 	struct uuid *uuid;
-	int alias;		
+	int alias;
 } gpt_uuid_alias_match[] = {
 	{ &gpt_uuid_apple_boot,		G_PART_ALIAS_APPLE_BOOT },
 	{ &gpt_uuid_apple_hfs,		G_PART_ALIAS_APPLE_HFS },
@@ -217,8 +219,16 @@ gpt_read_hdr(struct g_part_gpt_table *ta
 
 	pp = cp->provider;
 	last = (pp->mediasize / pp->sectorsize) - 1;
-	table->lba[elt] = (elt == GPT_ELT_PRIHDR) ? 1 : last;
 	table->state[elt] = GPT_STATE_MISSING;
+	/*
+	 * If the primary header is valid look for secondary
+	 * header in AlternateLBA, otherwise in the last medium's LBA.
+	 */
+	if (elt == GPT_ELT_SECHDR) {
+		if (table->state[GPT_ELT_PRIHDR] != GPT_STATE_OK)
+			table->lba[elt] = last;
+	} else
+		table->lba[elt] = 1;
 	buf = g_read_data(cp, table->lba[elt] * pp->sectorsize, pp->sectorsize,
 	    &error);
 	if (buf == NULL)
@@ -244,12 +254,15 @@ gpt_read_hdr(struct g_part_gpt_table *ta
 
 	table->state[elt] = GPT_STATE_INVALID;
 	hdr->hdr_revision = le32toh(buf->hdr_revision);
-	if (hdr->hdr_revision < 0x00010000)
+	if (hdr->hdr_revision < GPT_HDR_REVISION)
 		goto fail;
 	hdr->hdr_lba_self = le64toh(buf->hdr_lba_self);
 	if (hdr->hdr_lba_self != table->lba[elt])
 		goto fail;
 	hdr->hdr_lba_alt = le64toh(buf->hdr_lba_alt);
+	if (hdr->hdr_lba_alt == hdr->hdr_lba_self ||
+	    hdr->hdr_lba_alt > last)
+		goto fail;
 
 	/* Check the managed area. */
 	hdr->hdr_lba_start = le64toh(buf->hdr_lba_start);
@@ -283,6 +296,10 @@ gpt_read_hdr(struct g_part_gpt_table *ta
 	le_uuid_dec(&buf->hdr_uuid, &hdr->hdr_uuid);
 	hdr->hdr_crc_table = le32toh(buf->hdr_crc_table);
 
+	/* save LBA for secondary header */
+	if (elt == GPT_ELT_PRIHDR)
+		table->lba[GPT_ELT_SECHDR] = hdr->hdr_lba_alt;
+
 	g_free(buf);
 	return (hdr);
 
@@ -490,18 +507,21 @@ static int
 g_part_gpt_destroy(struct g_part_table *basetable, struct g_part_parms *gpp)
 {
 	struct g_part_gpt_table *table;
+	struct g_provider *pp;
 
 	table = (struct g_part_gpt_table *)basetable;
-	if (table->hdr != NULL)
-		g_free(table->hdr);
+	pp = LIST_FIRST(&basetable->gpt_gp->consumer)->provider;
+	g_free(table->hdr);
 	table->hdr = NULL;
 
 	/*
-	 * Wipe the first 2 sectors as well as the last to clear the
-	 * partitioning.
+	 * Wipe the first 2 sectors to clear the partitioning. Wipe the last
+	 * sector only if it has valid secondary header.
 	 */
 	basetable->gpt_smhead |= 3;
-	basetable->gpt_smtail |= 1;
+	if (table->state[GPT_ELT_SECHDR] == GPT_STATE_OK &&
+	    table->lba[GPT_ELT_SECHDR] == pp->mediasize / pp->sectorsize - 1)
+		basetable->gpt_smtail |= 1;
 	return (0);
 }
 
@@ -665,10 +685,12 @@ g_part_gpt_read(struct g_part_table *bas
 	struct g_part_gpt_table *table;
 	struct g_part_gpt_entry *entry;
 	u_char *buf;
+	uint64_t last;
 	int error, index;
 
 	table = (struct g_part_gpt_table *)basetable;
 	pp = cp->provider;
+	last = (pp->mediasize / pp->sectorsize) - 1;
 
 	/* Read the PMBR */
 	buf = g_read_data(cp, 0, pp->sectorsize, &error);
@@ -732,6 +754,7 @@ g_part_gpt_read(struct g_part_table *bas
 		printf("GEOM: %s: using the secondary instead -- recovery "
 		    "strongly advised.\n", pp->name);
 		table->hdr = sechdr;
+		basetable->gpt_corrupt = 1;
 		if (prihdr != NULL)
 			g_free(prihdr);
 		tbl = sectbl;
@@ -743,6 +766,11 @@ g_part_gpt_read(struct g_part_table *bas
 			    "or invalid.\n", pp->name);
 			printf("GEOM: %s: using the primary only -- recovery "
 			    "suggested.\n", pp->name);
+			basetable->gpt_corrupt = 1;
+		} else if (table->lba[GPT_ELT_SECHDR] != last) {
+			printf( "GEOM: %s: the secondary GPT header is not in "
+			    "the last LBA.\n", pp->name);
+			basetable->gpt_corrupt = 1;
 		}
 		table->hdr = prihdr;
 		if (sechdr != NULL)
@@ -759,8 +787,9 @@ g_part_gpt_read(struct g_part_table *bas
 	for (index = basetable->gpt_entries - 1; index >= 0; index--) {
 		if (EQUUID(&tbl[index].ent_type, &gpt_uuid_unused))
 			continue;
-		entry = (struct g_part_gpt_entry *)g_part_new_entry(basetable,  
-		    index+1, tbl[index].ent_lba_start, tbl[index].ent_lba_end);
+		entry = (struct g_part_gpt_entry *)g_part_new_entry(
+		    basetable, index + 1, tbl[index].ent_lba_start,
+		    tbl[index].ent_lba_end);
 		entry->ent = tbl[index];
 	}
 
@@ -769,6 +798,38 @@ g_part_gpt_read(struct g_part_table *bas
 }
 
 static int
+g_part_gpt_recover(struct g_part_table *basetable)
+{
+	struct g_part_gpt_table *table;
+	struct g_provider *pp;
+	uint64_t last;
+	size_t tblsz;
+
+	table = (struct g_part_gpt_table *)basetable;
+	pp = LIST_FIRST(&basetable->gpt_gp->consumer)->provider;
+	last = pp->mediasize / pp->sectorsize - 1;
+	tblsz = (table->hdr->hdr_entries * table->hdr->hdr_entsz +
+	    pp->sectorsize - 1) / pp->sectorsize;
+
+	table->lba[GPT_ELT_PRIHDR] = 1;
+	table->lba[GPT_ELT_PRITBL] = 2;
+	table->lba[GPT_ELT_SECHDR] = last;
+	table->lba[GPT_ELT_SECTBL] = last - tblsz;
+	table->state[GPT_ELT_PRIHDR] = GPT_STATE_OK;
+	table->state[GPT_ELT_PRITBL] = GPT_STATE_OK;
+	table->state[GPT_ELT_SECHDR] = GPT_STATE_OK;
+	table->state[GPT_ELT_SECTBL] = GPT_STATE_OK;
+	table->hdr->hdr_lba_start = 2 + tblsz;
+	table->hdr->hdr_lba_end = last - tblsz - 1;
+
+	basetable->gpt_first = table->hdr->hdr_lba_start;
+	basetable->gpt_last = table->hdr->hdr_lba_end;
+	basetable->gpt_corrupt = 0;
+
+	return (0);
+}
+
+static int
 g_part_gpt_setunset(struct g_part_table *table, struct g_part_entry *baseentry,
     const char *attrib, unsigned int set)
 {
@@ -867,13 +928,13 @@ g_part_gpt_write(struct g_part_table *ba
 	struct g_part_entry *baseentry;
 	struct g_part_gpt_entry *entry;
 	struct g_part_gpt_table *table;
-	size_t tlbsz;
+	size_t tblsz;
 	uint32_t crc;
 	int error, index;
 
 	pp = cp->provider;
 	table = (struct g_part_gpt_table *)basetable;
-	tlbsz = (table->hdr->hdr_entries * table->hdr->hdr_entsz +
+	tblsz = (table->hdr->hdr_entries * table->hdr->hdr_entsz +
 	    pp->sectorsize - 1) / pp->sectorsize;
 
 	/* Write the PMBR */
@@ -885,7 +946,7 @@ g_part_gpt_write(struct g_part_table *ba
 		return (error);
 
 	/* Allocate space for the header and entries. */
-	buf = g_malloc((tlbsz + 1) * pp->sectorsize, M_WAITOK | M_ZERO);
+	buf = g_malloc((tblsz + 1) * pp->sectorsize, M_WAITOK | M_ZERO);
 
 	memcpy(buf, table->hdr->hdr_sig, sizeof(table->hdr->hdr_sig));
 	le32enc(buf + 8, table->hdr->hdr_revision);
@@ -924,7 +985,7 @@ g_part_gpt_write(struct g_part_table *ba
 	le32enc(buf + 16, crc);
 
 	error = g_write_data(cp, table->lba[GPT_ELT_PRITBL] * pp->sectorsize,
-	    buf + pp->sectorsize, tlbsz * pp->sectorsize);
+	    buf + pp->sectorsize, tblsz * pp->sectorsize);
 	if (error)
 		goto out;
 	error = g_write_data(cp, table->lba[GPT_ELT_PRIHDR] * pp->sectorsize,
@@ -941,7 +1002,7 @@ g_part_gpt_write(struct g_part_table *ba
 	le32enc(buf + 16, crc);
 
 	error = g_write_data(cp, table->lba[GPT_ELT_SECTBL] * pp->sectorsize,
-	    buf + pp->sectorsize, tlbsz * pp->sectorsize);
+	    buf + pp->sectorsize, tblsz * pp->sectorsize);
 	if (error)
 		goto out;
 	error = g_write_data(cp, table->lba[GPT_ELT_SECHDR] * pp->sectorsize,

Modified: head/sys/geom/part/g_part_if.m
==============================================================================
--- head/sys/geom/part/g_part_if.m	Mon Oct 25 16:11:37 2010	(r214351)
+++ head/sys/geom/part/g_part_if.m	Mon Oct 25 16:23:35 2010	(r214352)
@@ -65,6 +65,12 @@ CODE {
 	{
 		return (ENOSYS);
 	}
+
+	static int
+	default_recover(struct g_part_table *t __unused)
+	{
+		return (ENOSYS);
+	}
 };
 
 # add() - scheme specific processing for the add verb.
@@ -163,6 +169,11 @@ METHOD int read {
 	struct g_consumer *cp;
 };
 
+# recover() - scheme specific processing for the recover verb.
+METHOD int recover {
+	struct g_part_table *table;
+} DEFAULT default_recover;
+
 # setunset() - set or unset partition entry attributes.
 METHOD int setunset {
 	struct g_part_table *table;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201010251623.o9PGNZ3V070926>