diff --git a/sys/geom/geom_gpt.c b/sys/geom/geom_gpt.c index 77317b11ae25..a437984bcb01 100644 --- a/sys/geom/geom_gpt.c +++ b/sys/geom/geom_gpt.c @@ -1,247 +1,819 @@ /*- - * Copyright (c) 2002 Marcel Moolenaar - * Copyright (c) 2002 Poul-Henning Kamp - * Copyright (c) 2002 Networks Associates Technology, Inc. + * Copyright (c) 2002, 2005 Marcel Moolenaar * 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. - * 3. The names of the authors may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. * - * 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. + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. */ #include __FBSDID("$FreeBSD$"); #include -#include -#include -#include #include -#include -#include #include #include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include CTASSERT(offsetof(struct gpt_hdr, padding) == 92); CTASSERT(sizeof(struct gpt_ent) == 128); -/* - * XXX: GEOM is not dynamic enough. We are forced to use a compile-time - * limit. The minimum number of partitions (128) as required by EFI is - * most of the time just a waste of space. - */ -#define GPT_MAX_SLICES 128 +#define G_GPT_TRACE(args) /* g_trace args */ -struct g_gpt_softc { - struct gpt_ent *part[GPT_MAX_SLICES]; +/* + * The GEOM GPT class. Nothing fancy... + */ +static g_ctl_req_t g_gpt_ctlreq; +static g_ctl_destroy_geom_t g_gpt_destroy_geom; +static g_taste_t g_gpt_taste; + +static g_access_t g_gpt_access; +static g_dumpconf_t g_gpt_dumpconf; +static g_orphan_t g_gpt_orphan; +static g_spoiled_t g_gpt_spoiled; +static g_start_t g_gpt_start; + +static struct g_class g_gpt_class = { + .name = "GPT", + .version = G_VERSION, + /* Class methods. */ + .ctlreq = g_gpt_ctlreq, + .destroy_geom = g_gpt_destroy_geom, + .taste = g_gpt_taste, + /* Geom methods. */ + .access = g_gpt_access, + .dumpconf = g_gpt_dumpconf, + .orphan = g_gpt_orphan, + .spoiled = g_gpt_spoiled, + .start = g_gpt_start, }; -static int -is_gpt_hdr(struct gpt_hdr *hdr) -{ - uint32_t crc; +DECLARE_GEOM_CLASS(g_gpt_class, g_gpt); - if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig))) - return (0); - crc = le32toh(hdr->hdr_crc_self); - hdr->hdr_crc_self = 0; - if (crc32(hdr, le32toh(hdr->hdr_size)) != crc) - return (0); - hdr->hdr_crc_self = htole32(crc); - /* We're happy... */ - return (1); -} +/* + * The GEOM GPT instance data. + */ +struct g_gpt_part { + LIST_ENTRY(g_gpt_part) parts; + struct g_provider *provider; + off_t offset; + struct gpt_ent ent; + int index; +}; + +enum gpt_hdr_type { + GPT_HDR_PRIMARY, + GPT_HDR_SECONDARY, + GPT_HDR_COUNT +}; + +enum gpt_hdr_state { + GPT_HDR_UNKNOWN, + GPT_HDR_MISSING, + GPT_HDR_CORRUPT, + GPT_HDR_INVALID, + GPT_HDR_OK +}; + +struct g_gpt_softc { + LIST_HEAD(, g_gpt_part) parts; + struct gpt_hdr hdr[GPT_HDR_COUNT]; + enum gpt_hdr_state state[GPT_HDR_COUNT]; +}; + +static struct uuid g_gpt_freebsd = GPT_ENT_TYPE_FREEBSD; +static struct uuid g_gpt_freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP; +static struct uuid g_gpt_linux_swap = GPT_ENT_TYPE_LINUX_SWAP; +static struct uuid g_gpt_unused = GPT_ENT_TYPE_UNUSED; + +/* + * Support functions. + */ static int -is_pmbr(char *mbr) +g_gpt_has_pmbr(struct g_consumer *cp, int *error) { struct dos_partition *part; - int i; + char *buf; + int i, pmbr; uint16_t magic; - magic = le16toh(*(uint16_t *)(uintptr_t)(mbr + DOSMAGICOFFSET)); - if (magic != DOSMAGIC) + buf = g_read_data(cp, 0L, cp->provider->sectorsize, error); + if (*error != 0) return (0); - part = (struct dos_partition *)(uintptr_t)(mbr + DOSPARTOFF); + pmbr = 0; + + magic = le16toh(*(uint16_t *)(uintptr_t)(buf + DOSMAGICOFFSET)); + if (magic != DOSMAGIC) + goto out; + + part = (struct dos_partition *)(uintptr_t)(buf + DOSPARTOFF); for (i = 0; i < 4; i++) { if (part[i].dp_typ != 0 && part[i].dp_typ != DOSPTYP_PMBR) - return (0); + goto out; } - return (1); + pmbr = 1; + +out: + g_free(buf); + return (pmbr); +} + +static void +g_gpt_load_hdr(struct g_gpt_softc *softc, struct g_provider *pp, + enum gpt_hdr_type type, void *buf) +{ + struct uuid uuid; + struct gpt_hdr *hdr; + uint64_t lba, last; + uint32_t crc, sz; + + softc->state[type] = GPT_HDR_MISSING; + + hdr = softc->hdr + type; + bcopy(buf, hdr, sizeof(*hdr)); + if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0) + return; + + softc->state[type] = GPT_HDR_CORRUPT; + + sz = le32toh(hdr->hdr_size); + if (sz < 92 || sz > pp->sectorsize) + return; + crc = le32toh(hdr->hdr_crc_self); + hdr->hdr_crc_self = 0; + if (crc32(hdr, sz) != crc) + return; + hdr->hdr_size = sz; + hdr->hdr_crc_self = crc; + + softc->state[type] = GPT_HDR_INVALID; + + last = (pp->mediasize / pp->sectorsize) - 1; + hdr->hdr_revision = le32toh(hdr->hdr_revision); + if (hdr->hdr_revision < 0x00010000) + return; + hdr->hdr_lba_self = le64toh(hdr->hdr_lba_self); + if (hdr->hdr_lba_self != (type == GPT_HDR_PRIMARY ? 1 : last)) + return; + hdr->hdr_lba_alt = le64toh(hdr->hdr_lba_alt); + if (hdr->hdr_lba_alt != (type == GPT_HDR_PRIMARY ? last : 1)) + return; + + /* Check the managed area. */ + hdr->hdr_lba_start = le64toh(hdr->hdr_lba_start); + if (hdr->hdr_lba_start < 2 || hdr->hdr_lba_start >= last) + return; + hdr->hdr_lba_end = le64toh(hdr->hdr_lba_end); + if (hdr->hdr_lba_end < hdr->hdr_lba_start || hdr->hdr_lba_end >= last) + return; + + /* Check the table location and size of the table. */ + hdr->hdr_entries = le32toh(hdr->hdr_entries); + hdr->hdr_entsz = le32toh(hdr->hdr_entsz); + if (hdr->hdr_entries == 0 || hdr->hdr_entsz < 128 || + (hdr->hdr_entsz & 7) != 0) + return; + hdr->hdr_lba_table = le64toh(hdr->hdr_lba_table); + if (hdr->hdr_lba_table < 2 || hdr->hdr_lba_table >= last) + return; + if (hdr->hdr_lba_table >= hdr->hdr_lba_start && + hdr->hdr_lba_table <= hdr->hdr_lba_end) + return; + lba = hdr->hdr_lba_table + + (hdr->hdr_entries * hdr->hdr_entsz + pp->sectorsize - 1) / + pp->sectorsize - 1; + if (lba >= last) + return; + if (lba >= hdr->hdr_lba_start && lba <= hdr->hdr_lba_end) + return; + + softc->state[type] = GPT_HDR_OK; + + le_uuid_dec(&hdr->hdr_uuid, &uuid); + hdr->hdr_uuid = uuid; + hdr->hdr_crc_table = le32toh(hdr->hdr_crc_table); +} + +static void +g_gpt_load_tbl(struct g_geom *gp, struct g_provider *pp, struct gpt_hdr *hdr, + char *tbl) +{ + struct uuid uuid; + struct gpt_ent *ent; + struct g_gpt_part *last, *part; + struct g_gpt_softc *softc; + uint64_t part_start, part_end; + unsigned int ch, idx; + + softc = gp->softc; + + for (idx = 0, last = part = NULL; + idx < hdr->hdr_entries; + idx++, last = part, tbl += hdr->hdr_entsz) { + ent = (struct gpt_ent *)(uintptr_t)tbl; + le_uuid_dec(&ent->ent_type, &uuid); + if (!memcmp(&uuid, &g_gpt_unused, sizeof(struct uuid))) + continue; + part_start = le64toh(ent->ent_lba_start); + part_end = le64toh(ent->ent_lba_end); + if (part_start < hdr->hdr_lba_start || part_start > part_end || + part_end > hdr->hdr_lba_end) { + printf("GEOM: %s: GPT partition %d is invalid -- " + "ignored.\n", gp->name, idx + 1); + continue; + } + + part = g_malloc(sizeof(struct g_gpt_part), M_WAITOK | M_ZERO); + part->index = idx; + part->offset = part_start * pp->sectorsize; + if (last == NULL) + LIST_INSERT_HEAD(&softc->parts, part, parts); + else + LIST_INSERT_AFTER(last, part, parts); + part->ent.ent_type = uuid; + le_uuid_dec(&ent->ent_uuid, &part->ent.ent_uuid); + part->ent.ent_lba_start = part_start; + part->ent.ent_lba_end = part_end; + part->ent.ent_attr = le64toh(ent->ent_attr); + for (ch = 0; ch < sizeof(ent->ent_name)/2; ch++) + part->ent.ent_name[ch] = le16toh(ent->ent_name[ch]); + + g_topology_lock(); + part->provider = g_new_providerf(gp, "%s%c%d", gp->name, + !memcmp(&uuid, &g_gpt_freebsd, sizeof(struct uuid)) + ? 's' : 'p', idx + 1); + part->provider->index = idx; + part->provider->private = part; /* Close the circle. */ + part->provider->mediasize = (part_end - part_start + 1) * + pp->sectorsize; + part->provider->sectorsize = pp->sectorsize; + part->provider->flags = pp->flags & G_PF_CANDELETE; + if (pp->stripesize > 0) { + part->provider->stripesize = pp->stripesize; + part->provider->stripeoffset = + (pp->stripeoffset + part->offset) % pp->stripesize; + } + g_error_provider(part->provider, 0); + g_topology_unlock(); + + if (bootverbose) { + printf("GEOM: %s: partition ", part->provider->name); + printf_uuid(&part->ent.ent_uuid); + printf(".\n"); + } + } } static int -g_gpt_start(struct bio *bp) +g_gpt_matched_hdrs(struct gpt_hdr *pri, struct gpt_hdr *sec) { + if (memcmp(&pri->hdr_uuid, &sec->hdr_uuid, sizeof(struct uuid)) != 0) + return (0); + return ((pri->hdr_revision == sec->hdr_revision && + pri->hdr_size == sec->hdr_size && + pri->hdr_lba_start == sec->hdr_lba_start && + pri->hdr_lba_end == sec->hdr_lba_end && + pri->hdr_entries == sec->hdr_entries && + pri->hdr_entsz == sec->hdr_entsz && + pri->hdr_crc_table == sec->hdr_crc_table) ? 1 : 0); +} + +static int +g_gpt_tbl_ok(struct gpt_hdr *hdr, char *tbl) +{ + size_t sz; + uint32_t crc; + + crc = hdr->hdr_crc_table; + sz = hdr->hdr_entries * hdr->hdr_entsz; + return ((crc32(tbl, sz) == crc) ? 1 : 0); +} + +static void +g_gpt_to_utf8(struct sbuf *sb, uint16_t *str, size_t len) +{ + u_int bo; + uint32_t ch; + uint16_t c; + + bo = BYTE_ORDER; + while (len > 0 && *str != 0) { + ch = (bo == BIG_ENDIAN) ? be16toh(*str) : le16toh(*str); + str++, len--; + if ((ch & 0xf800) == 0xd800) { + if (len > 0) { + c = (bo == BIG_ENDIAN) ? be16toh(*str) + : le16toh(*str); + str++, len--; + } else + c = 0xfffd; + if ((ch & 0x400) == 0 && (c & 0xfc00) == 0xdc00) { + ch = ((ch & 0x3ff) << 10) + (c & 0x3ff); + ch += 0x10000; + } else + ch = 0xfffd; + } else if (ch == 0xfffe) { /* BOM (U+FEFF) swapped. */ + bo = (bo == BIG_ENDIAN) ? LITTLE_ENDIAN : BIG_ENDIAN; + continue; + } else if (ch == 0xfeff) /* BOM (U+FEFF) unswapped. */ + continue; + + if (ch < 0x80) + sbuf_printf(sb, "%c", ch); + else if (ch < 0x800) + sbuf_printf(sb, "%c%c", 0xc0 | (ch >> 6), + 0x80 | (ch & 0x3f)); + else if (ch < 0x10000) + sbuf_printf(sb, "%c%c%c", 0xe0 | (ch >> 12), + 0x80 | ((ch >> 6) & 0x3f), 0x80 | (ch & 0x3f)); + else if (ch < 0x200000) + sbuf_printf(sb, "%c%c%c%c", 0xf0 | (ch >> 18), + 0x80 | ((ch >> 12) & 0x3f), + 0x80 | ((ch >> 6) & 0x3f), 0x80 | (ch & 0x3f)); + } +} + +static void +g_gpt_wither(struct g_geom *gp, int error) +{ + struct g_gpt_part *part; + struct g_gpt_softc *softc; + + softc = gp->softc; + if (softc != NULL) { + part = LIST_FIRST(&softc->parts); + while (part != NULL) { + LIST_REMOVE(part, parts); + g_free(part); + part = LIST_FIRST(&softc->parts); + } + g_free(softc); + gp->softc = NULL; + } + g_wither_geom(gp, error); +} + +/* + * Class methods. + */ + +static void +g_gpt_ctlreq(struct gctl_req *req, struct g_class *mp, const char *verb) +{ + /* XXX todo */ +} + +static int +g_gpt_destroy_geom(struct gctl_req *req, struct g_class *mp, + struct g_geom *gp) +{ + + G_GPT_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, gp->name)); + + g_topology_assert(); + + g_gpt_wither(gp, EINVAL); return (0); } +static struct g_geom * +g_gpt_taste(struct g_class *mp, struct g_provider *pp, int insist __unused) +{ + struct g_consumer *cp; + struct g_geom *gp; + struct g_gpt_softc *softc; + struct gpt_hdr *hdr; + void *buf; + off_t ofs; + size_t nbytes; + int error; + + G_GPT_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, pp->name)); + g_topology_assert(); + + /* + * Sanity-check the provider. Since the first sector on the provider + * must be a PMBR and a PMBR is 512 bytes large, the sector size must + * be at least 512 bytes. We also require that the sector size is a + * multiple of the GPT entry size (which is 128 bytes). + * Also, since the theoretical minimum number of sectors needed by + * GPT is 6, any medium that has less than 6 sectors is never going + * to hold a GPT. The number 6 comes from: + * 1 sector for the PMBR + * 2 sectors for the GPT headers (each 1 sector) + * 2 sectors for the GPT tables (each 1 sector) + * 1 sector for an actual partition + * It's better to catch this pathological case early than behaving + * pathologically later on by panicing... + */ + if (pp->sectorsize < 512 || + pp->sectorsize % sizeof(struct gpt_ent) != 0 || + pp->mediasize < 6 * pp->sectorsize) + return (NULL); + + /* + * We don't nest. That is, we disallow nesting a GPT inside a GPT + * partition. We check only for direct nesting. Indirect nesting is + * not easy to determine. If you want, you can therefore nest GPT + * partitions by putting a dummy GEOM in between them. But I didn't + * say that... + */ + if (pp->geom->class == &g_gpt_class) + return (NULL); + + /* + * Create a GEOM with consumer and hook it up to the provider. + * With that we become part of the topology. Optain read, write + * and exclusive access to the provider. + */ + gp = g_new_geomf(mp, "%s", pp->name); + softc = g_malloc(sizeof(struct g_gpt_softc), M_WAITOK | M_ZERO); + gp->softc = softc; + LIST_INIT(&softc->parts); + cp = g_new_consumer(gp); + error = g_attach(cp, pp); + if (error == 0) + error = g_access(cp, 1, 0, 0); + if (error != 0) { + g_gpt_wither(gp, error); + return (NULL); + } + + g_topology_unlock(); + + /* + * Read both the primary and secondary GPT headers. We have all + * the information at our fingertips that way to determine if + * there's a GPT, including whether recovery is appropriate. + */ + buf = g_read_data(cp, pp->sectorsize, pp->sectorsize, &error); + if (error != 0) + goto fail; + g_gpt_load_hdr(softc, pp, GPT_HDR_PRIMARY, buf); + g_free(buf); + + buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, + &error); + if (error != 0) + goto fail; + g_gpt_load_hdr(softc, pp, GPT_HDR_SECONDARY, buf); + g_free(buf); + + /* Bail out if there are no GPT headers at all. */ + if (softc->state[GPT_HDR_PRIMARY] == GPT_HDR_MISSING && + softc->state[GPT_HDR_SECONDARY] == GPT_HDR_MISSING) { + error = ENXIO; /* Device not configured for GPT. */ + goto fail; + } + + /* + * We have at least one GPT header (though that one may be corrupt + * or invalid). This disk supposedly has GPT in some shape or form. + * First check that there's a protective MBR. Complain if there + * is none and fail. + */ + if (!g_gpt_has_pmbr(cp, &error)) { + printf("GEOM: %s: GPT detected, but no protective MBR.\n", + pp->name); + error = ENXIO; + goto fail; + } + + /* + * Now, catch the non-recoverable case where there's no good GPT + * header at all. That is, unrecoverable by us. The user may able + * to fix it up with some magic. + */ + if (softc->state[GPT_HDR_PRIMARY] != GPT_HDR_OK && + softc->state[GPT_HDR_SECONDARY] != GPT_HDR_OK) { + printf("GEOM: %s: corrupt or invalid GPT detected.\n", + pp->name); + printf("GEOM: %s: GPT rejected -- may not be recoverable.\n", + pp->name); + error = EINVAL; /* No valid GPT header exists. */ + goto fail; + } + + /* + * Ok, at least one header is good. We can use the GPT. If there's + * a corrupt or invalid header, we'd like to user to know about it. + * Also catch the case where both headers appear to be good but are + * not mirroring each other. We only check superficially for that. + */ + if (softc->state[GPT_HDR_PRIMARY] != GPT_HDR_OK) { + printf("GEOM: %s: the primary GPT header is corrupt or " + "invalid.\n", pp->name); + printf("GEOM: %s: using the secondary instead -- recovery " + "strongly advised.\n", pp->name); + } else if (softc->state[GPT_HDR_SECONDARY] != GPT_HDR_OK) { + printf("GEOM: %s: the secondary GPT header is corrupt or " + "invalid.\n", pp->name); + printf("GEOM: %s: using the primary only -- recovery " + "suggested.\n", pp->name); + } else if (!g_gpt_matched_hdrs(softc->hdr + GPT_HDR_PRIMARY, + softc->hdr + GPT_HDR_SECONDARY)) { + printf("GEOM: %s: the primary and secondary GPT header do " + "not agree.\n", pp->name); + printf("GEOM: %s: GPT rejected -- recovery required.\n", + pp->name); + error = EINVAL; /* No consistent GPT exists. */ + goto fail; + } + + /* Always prefer the primary header. */ + hdr = (softc->state[GPT_HDR_PRIMARY] == GPT_HDR_OK) + ? softc->hdr + GPT_HDR_PRIMARY : softc->hdr + GPT_HDR_SECONDARY; + + /* + * Now that we've got a GPT header, we have to deal with the table + * itself. Again there's a primary table and a secondary table and + * either or both may be corrupt or invalid. Redundancy is nice, + * but it's a combinatorial pain in the butt. + */ + + nbytes = ((hdr->hdr_entries * hdr->hdr_entsz + pp->sectorsize - 1) / + pp->sectorsize) * pp->sectorsize; + + ofs = hdr->hdr_lba_table * pp->sectorsize; + buf = g_read_data(cp, ofs, nbytes, &error); + if (error != 0) + goto fail; + + /* + * If the table is corrupt, check if we can use the other one. + * Complain and bail if not. + */ + if (!g_gpt_tbl_ok(hdr, buf)) { + g_free(buf); + if (hdr != softc->hdr + GPT_HDR_PRIMARY || + softc->state[GPT_HDR_SECONDARY] != GPT_HDR_OK) { + printf("GEOM: %s: the GPT table is corrupt -- " + "may not be recoverable.\n", pp->name); + goto fail; + } + softc->state[GPT_HDR_PRIMARY] = GPT_HDR_CORRUPT; + hdr = softc->hdr + GPT_HDR_SECONDARY; + ofs = hdr->hdr_lba_table * pp->sectorsize; + buf = g_read_data(cp, ofs, nbytes, &error); + if (error != 0) + goto fail; + + if (!g_gpt_tbl_ok(hdr, buf)) { + g_free(buf); + printf("GEOM: %s: both primary and secondary GPT " + "tables are corrupt.\n", pp->name); + printf("GEOM: %s: GPT rejected -- may not be " + "recoverable.\n", pp->name); + goto fail; + } + printf("GEOM: %s: the primary GPT table is corrupt.\n", + pp->name); + printf("GEOM: %s: using the secondary table -- recovery " + "strongly advised.\n", pp->name); + } + + if (bootverbose) { + printf("GEOM: %s: GPT ", pp->name); + printf_uuid(&hdr->hdr_uuid); + printf(".\n"); + } + + g_gpt_load_tbl(gp, pp, hdr, buf); + g_free(buf); + g_topology_lock(); + g_access(cp, -1, 0, 0); + return (gp); + + fail: + g_topology_lock(); + g_access(cp, -1, 0, 0); + g_gpt_wither(gp, error); + return (NULL); +} + +/* + * Geom methods. + */ + +static int +g_gpt_access(struct g_provider *pp, int dr, int dw, int de) +{ + struct g_consumer *cp; + + G_GPT_TRACE((G_T_ACCESS, "%s(%s,%d,%d,%d)", __func__, pp->name, dr, + dw, de)); + + cp = LIST_FIRST(&pp->geom->consumer); + + /* We always gain write-exclusive access. */ + return (g_access(cp, dr, dw, dw + de)); +} + static void g_gpt_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp) { - struct g_slicer *gsp = gp->softc; - struct g_gpt_softc *gs = gsp->softc; - struct uuid uuid; + static char *status[5] = { + "unknown", "missing", "corrupt", "invalid", "ok" + }; + struct g_gpt_part *part; + struct g_gpt_softc *softc; + struct gpt_hdr *hdr; - g_slice_dumpconf(sb, indent, gp, cp, pp); + KASSERT(sb != NULL && gp != NULL, (__func__)); - if (pp != NULL) { - le_uuid_dec(&gs->part[pp->index]->ent_type, &uuid); - if (indent != NULL) - sbuf_printf(sb, "%s", indent); - else - sbuf_printf(sb, " ty "); - sbuf_printf_uuid(sb, &uuid); - if (indent != NULL) - sbuf_printf(sb, "\n"); + if (indent == NULL) { + KASSERT(cp == NULL && pp != NULL, (__func__)); + part = pp->private; + sbuf_printf(sb, " i %u o %ju ty ", pp->index, + (uintmax_t)part->offset); + sbuf_printf_uuid(sb, &part->ent.ent_type); + } else if (cp != NULL) { /* Consumer configuration. */ + KASSERT(pp == NULL, (__func__)); + /* none */ + } else if (pp != NULL) { /* Provider configuration. */ + part = pp->private; + sbuf_printf(sb, "%s%u\n", indent, pp->index); + sbuf_printf(sb, "%s", indent); + sbuf_printf_uuid(sb, &part->ent.ent_type); + sbuf_printf(sb, "\n"); + sbuf_printf(sb, "%s", indent); + sbuf_printf_uuid(sb, &part->ent.ent_uuid); + sbuf_printf(sb, "\n"); + sbuf_printf(sb, "%s%ju\n", indent, + (uintmax_t)part->offset); + sbuf_printf(sb, "%s%ju\n", indent, + (uintmax_t)pp->mediasize); + sbuf_printf(sb, "%s%ju\n", indent, + (uintmax_t)part->ent.ent_attr); + sbuf_printf(sb, "%s\n"); + } else { /* Geom configuration. */ + softc = gp->softc; + hdr = (softc->state[GPT_HDR_PRIMARY] == GPT_HDR_OK) + ? softc->hdr + GPT_HDR_PRIMARY + : softc->hdr + GPT_HDR_SECONDARY; + sbuf_printf(sb, "%s", indent); + sbuf_printf_uuid(sb, &hdr->hdr_uuid); + sbuf_printf(sb, "\n"); + sbuf_printf(sb, "%s%s\n", indent, + status[softc->state[GPT_HDR_PRIMARY]]); + sbuf_printf(sb, "%s%s\n", indent, + status[softc->state[GPT_HDR_SECONDARY]]); + sbuf_printf(sb, "%s%s\n", indent, + (hdr == softc->hdr + GPT_HDR_PRIMARY) ? "primary" : + "secondary"); + sbuf_printf(sb, "%s%u\n", indent, + hdr->hdr_revision); + sbuf_printf(sb, "%s%u\n", indent, + hdr->hdr_size); + sbuf_printf(sb, "%s%u\n", indent, + hdr->hdr_crc_self); + sbuf_printf(sb, "%s%ju\n", indent, + (uintmax_t)hdr->hdr_lba_self); + sbuf_printf(sb, "%s%ju\n", indent, + (uintmax_t)hdr->hdr_lba_alt); + sbuf_printf(sb, "%s%ju\n", indent, + (uintmax_t)hdr->hdr_lba_start); + sbuf_printf(sb, "%s%ju\n", indent, + (uintmax_t)hdr->hdr_lba_end); + sbuf_printf(sb, "%s%ju\n", indent, + (uintmax_t)hdr->hdr_lba_table); + sbuf_printf(sb, "%s%u\n", indent, + hdr->hdr_crc_table); + sbuf_printf(sb, "%s%u\n", indent, + hdr->hdr_entries); + sbuf_printf(sb, "%s%u\n", indent, + hdr->hdr_entsz); } } -static struct g_geom * -g_gpt_taste(struct g_class *mp, struct g_provider *pp, int insist __unused) +static void +g_gpt_orphan(struct g_consumer *cp) { - struct uuid tmp; - struct g_consumer *cp; - struct g_geom *gp; - struct g_gpt_softc *gs; - u_char *buf, *mbr; - struct gpt_ent *ent, *part; - struct gpt_hdr *hdr; - u_int i, secsz, tblsz; - int error, ps; - uint32_t entries, entsz; + struct g_provider *pp; - g_trace(G_T_TOPOLOGY, "g_gpt_taste(%s,%s)", mp->name, pp->name); + pp = cp->provider; + KASSERT(pp != NULL, (__func__)); + G_GPT_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, pp->name)); g_topology_assert(); - /* - * XXX: I don't like to hardcode a maximum number of slices, since - * it's wasting space most of the time and insufficient any time. - * It's easier for now... - */ - gp = g_slice_new(mp, GPT_MAX_SLICES, pp, &cp, &gs, sizeof(*gs), - g_gpt_start); - if (gp == NULL) - return (NULL); - - g_topology_unlock(); - - do { - mbr = NULL; - - secsz = cp->provider->sectorsize; - if (secsz < 512) - break; - - /* XXX: we need to get the media size as well. */ - - /* Read both the MBR sector and the GPT sector. */ - mbr = g_read_data(cp, 0, 2 * secsz, &error); - if (mbr == NULL || error != 0) - break; - - if (!is_pmbr(mbr)) - break; - - hdr = (void*)(mbr + secsz); - - /* - * XXX: if we don't have a GPT header at LBA 1, we should - * check if there's a backup GPT at the end of the medium. If - * we have a valid backup GPT, we should restore the primary - * GPT and claim this lunch. - */ - if (!is_gpt_hdr(hdr)) - break; - - entries = le32toh(hdr->hdr_entries); - entsz = le32toh(hdr->hdr_entsz); - tblsz = (entries * entsz + secsz - 1) & ~(secsz - 1); - buf = g_read_data(cp, le64toh(hdr->hdr_lba_table) * secsz, - tblsz, &error); - if (buf == NULL) - break; - - for (i = 0; i < entries; i++) { - struct uuid unused = GPT_ENT_TYPE_UNUSED; - struct uuid freebsd = GPT_ENT_TYPE_FREEBSD; - - if (i >= GPT_MAX_SLICES) - break; - ent = (void*)(buf + i * entsz); - le_uuid_dec(&ent->ent_type, &tmp); - if (!memcmp(&tmp, &unused, sizeof(unused))) - continue; - /* XXX: This memory leaks */ - part = gs->part[i] = g_malloc(entsz, M_WAITOK); - if (part == NULL) - break; - part->ent_type = tmp; - le_uuid_dec(&ent->ent_uuid, &part->ent_uuid); - part->ent_lba_start = le64toh(ent->ent_lba_start); - part->ent_lba_end = le64toh(ent->ent_lba_end); - part->ent_attr = le64toh(ent->ent_attr); - /* XXX do we need to byte-swap UNICODE-16? */ - bcopy(ent->ent_name, part->ent_name, - sizeof(part->ent_name)); - ps = (!memcmp(&tmp, &freebsd, sizeof(freebsd))) - ? 's' : 'p'; - g_topology_lock(); - (void)g_slice_config(gp, i, G_SLICE_CONFIG_SET, - part->ent_lba_start * secsz, - (1 + part->ent_lba_end - part->ent_lba_start) * - secsz, secsz, "%s%c%d", gp->name, ps, i + 1); - g_topology_unlock(); - } - g_free(buf); - } while (0); - - if (mbr != NULL) - g_free(mbr); - - g_topology_lock(); - g_access(cp, -1, 0, 0); - if (LIST_EMPTY(&gp->provider)) { - g_slice_spoiled(cp); - return (NULL); - } - return (gp); + KASSERT(pp->error != 0, (__func__)); + g_gpt_wither(cp->geom, pp->error); } -static struct g_class g_gpt_class = { - .name = "GPT", - .version = G_VERSION, - .taste = g_gpt_taste, - .dumpconf = g_gpt_dumpconf, -}; +static void +g_gpt_spoiled(struct g_consumer *cp) +{ -DECLARE_GEOM_CLASS(g_gpt_class, g_gpt); + G_GPT_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, cp->provider->name)); + g_topology_assert(); + + g_gpt_wither(cp->geom, ENXIO); +} + +static void +g_gpt_start(struct bio *bp) +{ + struct bio *bp2; + struct g_consumer *cp; + struct g_geom *gp; + struct g_gpt_part *part; + struct g_kerneldump *gkd; + struct g_provider *pp; + + pp = bp->bio_to; + gp = pp->geom; + part = pp->private; + cp = LIST_FIRST(&gp->consumer); + + G_GPT_TRACE((G_T_BIO, "%s: cmd=%d, provider=%s", __func__, bp->bio_cmd, + pp->name)); + + switch(bp->bio_cmd) { + case BIO_READ: + case BIO_WRITE: + case BIO_DELETE: + if (bp->bio_offset >= pp->mediasize) { + g_io_deliver(bp, EIO); + break; + } + bp2 = g_clone_bio(bp); + if (bp2 == NULL) { + g_io_deliver(bp, ENOMEM); + break; + } + if (bp2->bio_offset + bp2->bio_length > pp->mediasize) + bp2->bio_length = pp->mediasize - bp2->bio_offset; + bp2->bio_done = g_std_done; + bp2->bio_offset += part->offset; + g_io_request(bp2, cp); + break; + case BIO_GETATTR: + if (!strcmp("GEOM::kerneldump", bp->bio_attribute)) { + /* + * Refuse non-swap partitions to be used as kernel + * dumps. + */ + if (memcmp(&part->ent.ent_type, &g_gpt_freebsd_swap, + sizeof(struct uuid)) && memcmp(&part->ent.ent_type, + &g_gpt_linux_swap, sizeof(struct uuid))) { + g_io_deliver(bp, ENXIO); + break; + } + gkd = (struct g_kerneldump *)bp->bio_data; + if (gkd->offset >= pp->mediasize) { + g_io_deliver(bp, EIO); + break; + } + if (gkd->offset + gkd->length > pp->mediasize) + gkd->length = pp->mediasize - gkd->offset; + gkd->offset += part->offset; + /* FALLTHROUGH */ + } + bp2 = g_clone_bio(bp); + if (bp2 == NULL) { + g_io_deliver(bp, ENOMEM); + break; + } + bp2->bio_done = g_std_done; + g_io_request(bp2, cp); + break; + default: + g_io_deliver(bp, EOPNOTSUPP); + break; + } +}