From owner-freebsd-geom@FreeBSD.ORG Fri Oct 15 13:18:10 2010 Return-Path: Delivered-To: freebsd-geom@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 9CAA3106566C for ; Fri, 15 Oct 2010 13:18:10 +0000 (UTC) (envelope-from bu7cher@yandex.ru) Received: from forward15.mail.yandex.net (forward15.mail.yandex.net [95.108.130.119]) by mx1.freebsd.org (Postfix) with ESMTP id EE3108FC14 for ; Fri, 15 Oct 2010 13:18:09 +0000 (UTC) Received: from smtp14.mail.yandex.net (smtp14.mail.yandex.net [95.108.131.192]) by forward15.mail.yandex.net (Yandex) with ESMTP id 524E65140032; Fri, 15 Oct 2010 17:03:02 +0400 (MSD) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex.ru; s=mail; t=1287147782; bh=sFKr+DkrMvA1uba/THUS66gEcz2B/RWR3E6EhploFwQ=; h=Message-ID:Date:From:MIME-Version:To:CC:Subject:Content-Type; b=npV1Ud/kzIrcg/Yr9gAh8qOl3mfdHUtnUZGso3KMMOITvENYZECN6LunDKRbgXJb6 lSpSUoSsvIcV/z/lDdNRgD36yF0acQfkJYoEo45139MZhXj40bwOC9nH4tqIywqNU5 iVptJRkbF91aOFQc579BXUV69SyOSS6FWUpn/iKc= Received: from [10.43.163.197] (nat-77-130.kirovnet.ru [92.39.77.130]) by smtp14.mail.yandex.net (Yandex) with ESMTPSA id 8A94919B80A3; Fri, 15 Oct 2010 17:03:01 +0400 (MSD) Message-ID: <4CB850CC.4040206@yandex.ru> Date: Fri, 15 Oct 2010 17:02:04 +0400 From: "Andrey V. Elsukov" User-Agent: Mozilla/5.0 (X11; U; FreeBSD amd64; en-US; rv:1.9.2.9) Gecko/20100925 Thunderbird/3.1.4 MIME-Version: 1.0 To: freebsd-geom@freebsd.org X-Enigmail-Version: 1.1.2 Content-Type: multipart/signed; micalg=pgp-sha1; protocol="application/pgp-signature"; boundary="------------enigD2733AF13F5EA54DBBCE0745" X-Yandex-TimeMark: 1287147782 X-Yandex-Spam: 1 X-Yandex-Front: smtp14.mail.yandex.net Cc: Konstantin Belousov , Alexander Motin , Marcel Moolenaar , Pawel Jakub Dawidek Subject: [RFC][patch] GPT recovering support X-BeenThere: freebsd-geom@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: GEOM-specific discussions and implementations List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 15 Oct 2010 13:18:10 -0000 This is an OpenPGP/MIME signed message (RFC 2440 and 3156) --------------enigD2733AF13F5EA54DBBCE0745 Content-Type: multipart/mixed; boundary="------------080108010201050905060301" This is a multi-part message in MIME format. --------------080108010201050905060301 Content-Type: text/plain; charset=KOI8-R Content-Transfer-Encoding: quoted-printable Hi All, GPT supports primary and secondary partition tables for redundancy. Some of them can be corrupted but GPART class is able to detect and work with corrupt table. Corrupt GPT should be recovered because further corruption can lead to lost ability to work with partitions. GPT's meta-data consists from primary and secondary headers and tables. Also to work with GPT first sector (LBA 0) should contains PMBR. If you have wiped first several sectors first of you should restore PMBR, otherwise GPART will not able to detect GPT even when you have proper secondary GPT header and table. Any other cases can be recovered with 'gpart recover' command. But it will work only if GPT still able to detected by GPART class. So, about patch. It add following changes: sbin/geom/class/part/geom_part.c * add recover verb to class_commands. * show "[CORRUPT]" mark for corrupt tables, for example: # gpart show =3D> 34 488397101 ad4 GPT (233G) [CORRUPT] sys/geom/part/g_part.h * add gpt_corrupt flag to struct g_part_table that indicates about detected corruption. sys/geom/part/g_part_if.m * add new kobj method G_PART_RECOVER sys/geom/part/g_part.c * implement recover verb ctl handler. * deny any modifications of corrupt tables. Only recover and destroy allowed. * add table state reporting to dumpconf. sys/geom/part/g_part_gpt.c * add new kobj method g_part_gpt_recover. * remove some spaces amd style(9) fixes. * s/tlbsz/tblsz/g for g_part_gpt_write. * teach gpt_read_hdr to read secondary GPT header from AlternateLBA. * replace magic number with macro. * before wiping last sector check that it contains secondary header. This can protect from destroying another GEOM's meta-data. * set gpt_corrupt =3D 1 when 1. Primary table or header is corrupt. 2. Secondary table or header is corrupt. 3. Secondary header is not in the last LBA. * implement g_part_gpt_recover kobj method. This method only corrects in memory copy of GPT. This changes can be committed later and all changes will be saved to disk. Since we able to call `gpart recover` we already have detected GPT and most of meta-data should be valid. When we call `gpart recover` it restores location of all 4 blobs (2 header and 2 tables). Also it corrects partitions area bounds. It seems all other information should be correct, otherwise this GPT will not be detected. TODO: * update man page; * add ability to detect GPT on medium with reduced mediasize --=20 WBR, Andrey V. Elsukov --------------080108010201050905060301 Content-Type: text/plain; name="gpart_recover.diff" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="gpart_recover.diff" Index: head/sbin/geom/class/part/geom_part.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- head/sbin/geom/class/part/geom_part.c (revision 213888) +++ head/sbin/geom/class/part/geom_part.c (working copy) @@ -167,6 +167,11 @@ struct g_command PUBSYM(class_commands)[] =3D { 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 }; =20 @@ -539,13 +544,17 @@ gpart_show_geom(struct ggeom *gp, const char *elem s =3D find_geomcfg(gp, "last"); last =3D (off_t)strtoimax(s, NULL, 0); wblocks =3D strlen(s); + s =3D find_geomcfg(gp, "state"); + if (s !=3D NULL && *s !=3D 'C') + s =3D NULL; wname =3D strlen(gp->lg_name); pp =3D LIST_FIRST(&gp->lg_consumer)->lg_provider; secsz =3D pp->lg_sectorsize; - printf("=3D>%*jd %*jd %*s %s (%s)\n", + printf("=3D>%*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]": ""); =20 while ((pp =3D find_provider(gp, first)) !=3D NULL) { s =3D find_provcfg(pp, "start"); Index: head/sys/geom/part/g_part.h =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- head/sys/geom/part/g_part.h (revision 213888) +++ head/sys/geom/part/g_part.h (working copy) @@ -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. */ }; =20 struct g_part_entry *g_part_new_entry(struct g_part_table *, int, quad_t= , Index: head/sys/geom/part/g_part_if.m =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- head/sys/geom/part/g_part_if.m (revision 213888) +++ head/sys/geom/part/g_part_if.m (working copy) @@ -65,6 +65,12 @@ CODE { { return (ENOSYS); } + + static int + default_recover(struct g_part_table *t __unused) + { + return (ENOSYS); + } }; =20 # add() - scheme specific processing for the add verb. @@ -163,6 +169,11 @@ METHOD int read { struct g_consumer *cp; }; =20 +# 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; Index: head/sys/geom/part/g_part_gpt.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- head/sys/geom/part/g_part_gpt.c (revision 213888) +++ head/sys/geom/part/g_part_gpt.c (working copy) @@ -94,7 +94,7 @@ static int g_part_gpt_destroy(struct g_part_table static void g_part_gpt_dumpconf(struct g_part_table *, struct g_part_ent= ry *, 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 = *, =20 +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_t 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 *); =20 static kobj_method_t g_part_gpt_methods[] =3D { KOBJMETHOD(g_part_add, g_part_gpt_add), @@ -120,6 +121,7 @@ static kobj_method_t g_part_gpt_methods[] =3D { 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 =3D GPT_ENT_TYPE_ =20 static struct g_part_uuid_alias { struct uuid *uuid; - int alias; =09 + int alias; } gpt_uuid_alias_match[] =3D { { &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, struc =20 pp =3D cp->provider; last =3D (pp->mediasize / pp->sectorsize) - 1; - table->lba[elt] =3D (elt =3D=3D GPT_ELT_PRIHDR) ? 1 : last; table->state[elt] =3D GPT_STATE_MISSING; + /* + * If the primary header is valid look for secondary + * header in AlternateLBA, otherwise in the last medium's LBA. + */ + if (elt =3D=3D GPT_ELT_SECHDR) { + if (table->state[GPT_ELT_PRIHDR] !=3D GPT_STATE_OK) + table->lba[elt] =3D last; + } else + table->lba[elt] =3D 1; buf =3D g_read_data(cp, table->lba[elt] * pp->sectorsize, pp->sectorsiz= e, &error); if (buf =3D=3D NULL) @@ -244,12 +254,15 @@ gpt_read_hdr(struct g_part_gpt_table *table, struc =20 table->state[elt] =3D GPT_STATE_INVALID; hdr->hdr_revision =3D le32toh(buf->hdr_revision); - if (hdr->hdr_revision < 0x00010000) + if (hdr->hdr_revision < GPT_HDR_REVISION) goto fail; hdr->hdr_lba_self =3D le64toh(buf->hdr_lba_self); if (hdr->hdr_lba_self !=3D table->lba[elt]) goto fail; hdr->hdr_lba_alt =3D le64toh(buf->hdr_lba_alt); + if (hdr->hdr_lba_alt =3D=3D hdr->hdr_lba_self || + hdr->hdr_lba_alt > last) + goto fail; =20 /* Check the managed area. */ hdr->hdr_lba_start =3D le64toh(buf->hdr_lba_start); @@ -283,6 +296,10 @@ gpt_read_hdr(struct g_part_gpt_table *table, struc le_uuid_dec(&buf->hdr_uuid, &hdr->hdr_uuid); hdr->hdr_crc_table =3D le32toh(buf->hdr_crc_table); =20 + /* save LBA for secondary header */ + if (elt =3D=3D GPT_ELT_PRIHDR) + table->lba[GPT_ELT_SECHDR] =3D hdr->hdr_lba_alt; + g_free(buf); return (hdr); =20 @@ -490,18 +507,20 @@ 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; =20 table =3D (struct g_part_gpt_table *)basetable; - if (table->hdr !=3D NULL) - g_free(table->hdr); + pp =3D LIST_FIRST(&basetable->gpt_gp->consumer)->provider; + g_free(table->hdr); table->hdr =3D NULL; =20 /* - * 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 secondary header. */ basetable->gpt_smhead |=3D 3; - basetable->gpt_smtail |=3D 1; + if (table->lba[GPT_ELT_SECHDR] =3D=3D pp->mediasize / pp->sectorsize - = 1) + basetable->gpt_smtail |=3D 1; return (0); } =20 @@ -665,10 +684,12 @@ g_part_gpt_read(struct g_part_table *basetable, st struct g_part_gpt_table *table; struct g_part_gpt_entry *entry; u_char *buf; + uint64_t last; int error, index; =20 table =3D (struct g_part_gpt_table *)basetable; pp =3D cp->provider; + last =3D (pp->mediasize / pp->sectorsize) - 1; =20 /* Read the PMBR */ buf =3D g_read_data(cp, 0, pp->sectorsize, &error); @@ -732,6 +753,7 @@ g_part_gpt_read(struct g_part_table *basetable, st printf("GEOM: %s: using the secondary instead -- recovery " "strongly advised.\n", pp->name); table->hdr =3D sechdr; + basetable->gpt_corrupt =3D 1; if (prihdr !=3D NULL) g_free(prihdr); tbl =3D sectbl; @@ -743,6 +765,11 @@ g_part_gpt_read(struct g_part_table *basetable, st "or invalid.\n", pp->name); printf("GEOM: %s: using the primary only -- recovery " "suggested.\n", pp->name); + basetable->gpt_corrupt =3D 1; + } else if (table->lba[GPT_ELT_SECHDR] !=3D last) { + printf( "GEOM: %s: the secondary GPT header is not in " + "the last LBA.\n", pp->name); + basetable->gpt_corrupt =3D 1; } table->hdr =3D prihdr; if (sechdr !=3D NULL) @@ -759,8 +786,9 @@ g_part_gpt_read(struct g_part_table *basetable, st for (index =3D basetable->gpt_entries - 1; index >=3D 0; index--) { if (EQUUID(&tbl[index].ent_type, &gpt_uuid_unused)) continue; - entry =3D (struct g_part_gpt_entry *)g_part_new_entry(basetable, =20 - index+1, tbl[index].ent_lba_start, tbl[index].ent_lba_end); + entry =3D (struct g_part_gpt_entry *)g_part_new_entry( + basetable, index + 1, tbl[index].ent_lba_start, + tbl[index].ent_lba_end); entry->ent =3D tbl[index]; } =20 @@ -769,6 +797,38 @@ g_part_gpt_read(struct g_part_table *basetable, st } =20 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 =3D (struct g_part_gpt_table *)basetable; + pp =3D LIST_FIRST(&basetable->gpt_gp->consumer)->provider; + last =3D pp->mediasize / pp->sectorsize - 1; + tblsz =3D (table->hdr->hdr_entries * table->hdr->hdr_entsz + + pp->sectorsize - 1) / pp->sectorsize; + + table->lba[GPT_ELT_PRIHDR] =3D 1; + table->lba[GPT_ELT_PRITBL] =3D 2; + table->lba[GPT_ELT_SECHDR] =3D last; + table->lba[GPT_ELT_SECTBL] =3D last - tblsz; + table->state[GPT_ELT_PRIHDR] =3D GPT_STATE_OK; + table->state[GPT_ELT_PRITBL] =3D GPT_STATE_OK; + table->state[GPT_ELT_SECHDR] =3D GPT_STATE_OK; + table->state[GPT_ELT_SECTBL] =3D GPT_STATE_OK; + table->hdr->hdr_lba_start =3D 2 + tblsz; + table->hdr->hdr_lba_end =3D last - tblsz - 1; + + basetable->gpt_first =3D table->hdr->hdr_lba_start; + basetable->gpt_last =3D table->hdr->hdr_lba_end; + basetable->gpt_corrupt =3D 0; + + return (0); +} + +static int g_part_gpt_setunset(struct g_part_table *table, struct g_part_entry *bas= eentry, const char *attrib, unsigned int set) { @@ -867,13 +927,13 @@ g_part_gpt_write(struct g_part_table *basetable, s 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; =20 pp =3D cp->provider; table =3D (struct g_part_gpt_table *)basetable; - tlbsz =3D (table->hdr->hdr_entries * table->hdr->hdr_entsz + + tblsz =3D (table->hdr->hdr_entries * table->hdr->hdr_entsz + pp->sectorsize - 1) / pp->sectorsize; =20 /* Write the PMBR */ @@ -885,7 +945,7 @@ g_part_gpt_write(struct g_part_table *basetable, s return (error); =20 /* Allocate space for the header and entries. */ - buf =3D g_malloc((tlbsz + 1) * pp->sectorsize, M_WAITOK | M_ZERO); + buf =3D g_malloc((tblsz + 1) * pp->sectorsize, M_WAITOK | M_ZERO); =20 memcpy(buf, table->hdr->hdr_sig, sizeof(table->hdr->hdr_sig)); le32enc(buf + 8, table->hdr->hdr_revision); @@ -924,7 +984,7 @@ g_part_gpt_write(struct g_part_table *basetable, s le32enc(buf + 16, crc); =20 error =3D 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 =3D g_write_data(cp, table->lba[GPT_ELT_PRIHDR] * pp->sectorsize,= @@ -941,7 +1001,7 @@ g_part_gpt_write(struct g_part_table *basetable, s le32enc(buf + 16, crc); =20 error =3D 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 =3D g_write_data(cp, table->lba[GPT_ELT_SECHDR] * pp->sectorsize,= Index: head/sys/geom/part/g_part.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- head/sys/geom/part/g_part.c (revision 213888) +++ head/sys/geom/part/g_part.c (working copy) @@ -1037,8 +1037,39 @@ g_part_ctl_move(struct gctl_req *req, struct g_par= 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 =3D gpp->gpp_geom; + G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name)); + g_topology_assert(); + table =3D gp->softc; + error =3D recovered =3D 0; + + if (table->gpt_corrupt) { + error =3D G_PART_RECOVER(table); + if (error) { + gctl_error(req, "%d recovering '%s' failed", + error, gp->name); + return (error); + } + recovered =3D 1; + } + /* Provide feedback if so requested. */ + if (gpp->gpp_parms & G_PART_PARM_OUTPUT) { + sb =3D 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); } =20 static int @@ -1524,6 +1555,13 @@ g_part_ctlreq(struct gctl_req *req, struct g_class= table =3D NULL; if (modifies && (gpp.gpp_parms & G_PART_PARM_GEOM)) { table =3D gpp.gpp_geom->softc; + if (table !=3D NULL && table->gpt_corrupt && + ctlreq !=3D G_PART_CTL_DESTROY && + ctlreq !=3D G_PART_CTL_RECOVER) { + gctl_error(req, "%d table '%s' is corrupt", + EPERM, gpp.gpp_geom->name); + return; + } if (table !=3D NULL && !table->gpt_opened) { error =3D g_access(LIST_FIRST(&gpp.gpp_geom->consumer), 1, 1, 1); @@ -1789,6 +1827,8 @@ g_part_dumpconf(struct sbuf *sb, const char *inden 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); } } --------------080108010201050905060301-- --------------enigD2733AF13F5EA54DBBCE0745 Content-Type: application/pgp-signature; name="signature.asc" Content-Description: OpenPGP digital signature Content-Disposition: attachment; filename="signature.asc" -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.16 (FreeBSD) iQEcBAEBAgAGBQJMuFDVAAoJEAHF6gQQyKF6tp0H/3YSJg/33q9gSIUDgm7FFqvU Pm/iu97RlwiDhs7dFHjG7rEJWYqmwjxdpP4LMX72/wz1qY4E2cUwDS0LEBQAft4o syr4V0/+uQpUXjRDOjNMtVkgkLYMlaQvk/Bnqp4pFf5t/sBoS3BxKIc9U1obc/MP fpPZWKOUgiZA/QMZlqQyYRUGV0U2xolZgsXK5K2NSb6mz+GrViMbiiDVnyB3AxHL uutmU/7pQkinTDyEzFbLjPfxnPoHPjgLf48xbgCBLliWgVMlMZBAWnb4PSt3pLw2 chodCgf3z8wuhbLY3ov1AyNB3KqoRGl7LJV+Ow3gT5Zw+Rxm//GLesIyPkz2wZg= =7oN9 -----END PGP SIGNATURE----- --------------enigD2733AF13F5EA54DBBCE0745--