1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-16 10:20:30 +00:00

Complete rewrite in preparation of adding support for control

requests. The following features have been added:
1. Extensive checking and validation of both the primary and
   secondary headers to protect against corrupted data and to
   take advantage of the redundancy to allow the GPT to be
   used in the face of recoverable corruption.
2. Dynamic data-structures to avoid hardcoding gratuitous
   table limits so as to support the creation of GPT tables
   of (as of yet) unspecified size.
3. Only allow kernel dumps to swap partitions to provide the
   necessary anti-footshooting measures. Linux swap partitions
   are allowed.
4. Complete dump of the GPT configuration, including labels.
5. Supports Byte Order Mark (U+FEFF) handling for big-endian,
   little-endian and mixed-endian partition names.
This commit is contained in:
Marcel Moolenaar 2005-09-17 07:05:17 +00:00
parent 2d48a58f9f
commit 233b044b9b
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=150240

View File

@ -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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/bio.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/diskmbr.h>
#include <sys/endian.h>
#include <sys/sbuf.h>
#include <sys/uuid.h>
#include <sys/gpt.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/queue.h>
#include <sys/sbuf.h>
#include <sys/systm.h>
#include <sys/uuid.h>
#include <geom/geom.h>
#include <geom/geom_slice.h>
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)
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<index>%u</index>\n", indent, pp->index);
sbuf_printf(sb, "%s<type>", indent);
else
sbuf_printf(sb, " ty ");
sbuf_printf_uuid(sb, &uuid);
if (indent != NULL)
sbuf_printf_uuid(sb, &part->ent.ent_type);
sbuf_printf(sb, "</type>\n");
sbuf_printf(sb, "%s<uuid>", indent);
sbuf_printf_uuid(sb, &part->ent.ent_uuid);
sbuf_printf(sb, "</uuid>\n");
sbuf_printf(sb, "%s<offset>%ju</offset>\n", indent,
(uintmax_t)part->offset);
sbuf_printf(sb, "%s<length>%ju</length>\n", indent,
(uintmax_t)pp->mediasize);
sbuf_printf(sb, "%s<attr>%ju</attr>\n", indent,
(uintmax_t)part->ent.ent_attr);
sbuf_printf(sb, "%s<label>", indent);
g_gpt_to_utf8(sb, part->ent.ent_name,
sizeof(part->ent.ent_name)/2);
sbuf_printf(sb, "</label>\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<uuid>", indent);
sbuf_printf_uuid(sb, &hdr->hdr_uuid);
sbuf_printf(sb, "</uuid>\n");
sbuf_printf(sb, "%s<primary>%s</primary>\n", indent,
status[softc->state[GPT_HDR_PRIMARY]]);
sbuf_printf(sb, "%s<secondary>%s</secondary>\n", indent,
status[softc->state[GPT_HDR_SECONDARY]]);
sbuf_printf(sb, "%s<selected>%s</selected>\n", indent,
(hdr == softc->hdr + GPT_HDR_PRIMARY) ? "primary" :
"secondary");
sbuf_printf(sb, "%s<revision>%u</revision>\n", indent,
hdr->hdr_revision);
sbuf_printf(sb, "%s<header_size>%u</header_size>\n", indent,
hdr->hdr_size);
sbuf_printf(sb, "%s<crc_self>%u</crc_self>\n", indent,
hdr->hdr_crc_self);
sbuf_printf(sb, "%s<lba_self>%ju</lba_self>\n", indent,
(uintmax_t)hdr->hdr_lba_self);
sbuf_printf(sb, "%s<lba_other>%ju</lba_other>\n", indent,
(uintmax_t)hdr->hdr_lba_alt);
sbuf_printf(sb, "%s<lba_start>%ju</lba_start>\n", indent,
(uintmax_t)hdr->hdr_lba_start);
sbuf_printf(sb, "%s<lba_end>%ju</lba_end>\n", indent,
(uintmax_t)hdr->hdr_lba_end);
sbuf_printf(sb, "%s<lba_table>%ju</lba_table>\n", indent,
(uintmax_t)hdr->hdr_lba_table);
sbuf_printf(sb, "%s<crc_table>%u</crc_table>\n", indent,
hdr->hdr_crc_table);
sbuf_printf(sb, "%s<entries>%u</entries>\n", indent,
hdr->hdr_entries);
sbuf_printf(sb, "%s<entry_size>%u</entry_size>\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;
}
}