diff --git a/sbin/geom/class/part/geom_part.c b/sbin/geom/class/part/geom_part.c
index 55db274052e5..15bfa0bef28b 100644
--- a/sbin/geom/class/part/geom_part.c
+++ b/sbin/geom/class/part/geom_part.c
@@ -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 *, const char *);
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 char *element)
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");
@@ -857,83 +864,6 @@ gpart_bootcode(struct gctl_req *req, unsigned int fl)
geom_deletetree(&mesh);
}
-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)
{
diff --git a/sbin/geom/class/part/gpart.8 b/sbin/geom/class/part/gpart.8
index cb9b5c9ed499..f01e56a4a7ec 100644
--- a/sbin/geom/class/part/gpart.8
+++ b/sbin/geom/class/part/gpart.8
@@ -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 partitions, embed bootstrap code into them.
/sbin/gpart bootcode -p /boot/boot1 da0
.Ed
.Sh SEE ALSO
+.Xr dd 1 ,
.Xr geom 4 ,
.Xr geom 8
.Sh HISTORY
diff --git a/sys/geom/part/g_part.c b/sys/geom/part/g_part.c
index d88213b1d65a..155daae02982 100644
--- a/sys/geom/part/g_part.c
+++ b/sys/geom/part/g_part.c
@@ -924,7 +924,7 @@ g_part_ctl_destroy(struct gctl_req *req, struct g_part_parms *gpp)
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, struct g_part_parms *gpp)
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, struct g_part_parms *gpp)
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, struct g_class *mp, const char *verb)
} 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, struct g_class *mp, const char *verb)
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, struct g_class *mp, const char *verb)
}
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, struct g_class *mp, const char *verb)
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, struct g_class *mp, const char *verb)
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, struct g_class *mp, const char *verb)
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 char *indent, struct g_geom *gp,
table->gpt_sectors);
sbuf_printf(sb, "%s%u\n", indent,
table->gpt_heads);
+ sbuf_printf(sb, "%s%s\n", indent,
+ table->gpt_corrupt ? "CORRUPT": "OK");
G_PART_DUMPCONF(table, NULL, sb, indent);
}
}
diff --git a/sys/geom/part/g_part.h b/sys/geom/part/g_part.h
index 63cb06925568..e6722c378063 100644
--- a/sys/geom/part/g_part.h
+++ b/sys/geom/part/g_part.h
@@ -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 *);
diff --git a/sys/geom/part/g_part_gpt.c b/sys/geom/part/g_part_gpt.c
index ee319cf1f496..3aa858d25ff6 100644
--- a/sys/geom/part/g_part_gpt.c
+++ b/sys/geom/part/g_part_gpt.c
@@ -94,7 +94,7 @@ static int g_part_gpt_destroy(struct g_part_table *, struct g_part_parms *);
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(struct g_part_table *, struct g_part_entry *,
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_ENT_TYPE_UNUSED;
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 *table, struct g_consumer *cp,
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 *table, struct g_consumer *cp,
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 *table, struct g_consumer *cp,
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 *basetable, struct g_consumer *cp)
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 *basetable, struct g_consumer *cp)
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 *basetable, struct g_consumer *cp)
"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 *basetable, struct g_consumer *cp)
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];
}
@@ -768,6 +797,38 @@ g_part_gpt_read(struct g_part_table *basetable, struct g_consumer *cp)
return (0);
}
+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 *basetable, struct g_consumer *cp)
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 *basetable, struct g_consumer *cp)
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 *basetable, struct g_consumer *cp)
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 *basetable, struct g_consumer *cp)
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,
diff --git a/sys/geom/part/g_part_if.m b/sys/geom/part/g_part_if.m
index 04440fef1221..4152e87fb02a 100644
--- a/sys/geom/part/g_part_if.m
+++ b/sys/geom/part/g_part_if.m
@@ -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;