mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-28 11:57:28 +00:00
1357 lines
37 KiB
C
1357 lines
37 KiB
C
/*-
|
|
* Copyright (c) 2002, 2005, 2006 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.
|
|
*
|
|
* 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/bio.h>
|
|
#include <sys/diskmbr.h>
|
|
#include <sys/endian.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>
|
|
|
|
CTASSERT(offsetof(struct gpt_hdr, padding) == 92);
|
|
CTASSERT(sizeof(struct gpt_ent) == 128);
|
|
|
|
#define G_GPT_TRACE(args) /* g_trace args */
|
|
|
|
/*
|
|
* 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,
|
|
};
|
|
|
|
DECLARE_GEOM_CLASS(g_gpt_class, g_gpt);
|
|
|
|
/*
|
|
* 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];
|
|
};
|
|
|
|
enum g_gpt_ctl {
|
|
G_GPT_CTL_NONE,
|
|
G_GPT_CTL_ADD,
|
|
G_GPT_CTL_CREATE,
|
|
G_GPT_CTL_DESTROY,
|
|
G_GPT_CTL_MODIFY,
|
|
G_GPT_CTL_RECOVER,
|
|
G_GPT_CTL_REMOVE
|
|
};
|
|
|
|
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 void g_gpt_wither(struct g_geom *, int);
|
|
|
|
static void
|
|
g_gpt_ctl_add(struct gctl_req *req, const char *flags, struct g_geom *gp,
|
|
struct uuid *type, uint64_t start, uint64_t end, long entry)
|
|
{
|
|
char buf[16];
|
|
struct g_provider *pp;
|
|
struct g_gpt_softc *softc;
|
|
struct g_gpt_part *last, *part;
|
|
u_int idx;
|
|
|
|
G_GPT_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
|
|
g_topology_assert();
|
|
|
|
pp = LIST_FIRST(&gp->consumer)->provider;
|
|
softc = gp->softc;
|
|
|
|
last = NULL;
|
|
idx = (entry > 0) ? (u_int)(entry - 1) : 0;
|
|
LIST_FOREACH(part, &softc->parts, parts) {
|
|
if (part->index == idx) {
|
|
idx = part->index + 1;
|
|
last = part;
|
|
}
|
|
if ((start >= part->ent.ent_lba_start &&
|
|
start <= part->ent.ent_lba_end) ||
|
|
(end >= part->ent.ent_lba_start &&
|
|
end <= part->ent.ent_lba_end) ||
|
|
(start < part->ent.ent_lba_start &&
|
|
end > part->ent.ent_lba_end)) {
|
|
gctl_error(req, "%d start/end %jd/%jd", ENOSPC,
|
|
(intmax_t)start, (intmax_t)end);
|
|
return;
|
|
}
|
|
}
|
|
if (entry > 0 && (long)idx != entry - 1) {
|
|
gctl_error(req, "%d entry %ld", EEXIST, entry);
|
|
return;
|
|
}
|
|
snprintf(buf, sizeof(buf), "%u", idx + 1);
|
|
gctl_set_param(req, "entry", buf, strlen(buf) + 1);
|
|
|
|
part = g_malloc(sizeof(struct g_gpt_part), M_WAITOK | M_ZERO);
|
|
part->index = idx;
|
|
part->offset = start * pp->sectorsize;
|
|
if (last == NULL)
|
|
LIST_INSERT_HEAD(&softc->parts, part, parts);
|
|
else
|
|
LIST_INSERT_AFTER(last, part, parts);
|
|
part->ent.ent_type = *type;
|
|
kern_uuidgen(&part->ent.ent_uuid, 1);
|
|
part->ent.ent_lba_start = start;
|
|
part->ent.ent_lba_end = end;
|
|
|
|
/* XXX ent_attr */
|
|
/* XXX ent_name */
|
|
|
|
part->provider = g_new_providerf(gp, "%s%c%d", gp->name,
|
|
!memcmp(type, &g_gpt_freebsd, sizeof(struct uuid)) ? 's' : 'p',
|
|
idx + 1);
|
|
part->provider->index = idx;
|
|
part->provider->private = part; /* Close the circle. */
|
|
part->provider->mediasize = (end - 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);
|
|
|
|
if (bootverbose) {
|
|
printf("GEOM: %s: partition ", part->provider->name);
|
|
printf_uuid(&part->ent.ent_uuid);
|
|
printf(".\n");
|
|
}
|
|
}
|
|
|
|
static struct g_geom *
|
|
g_gpt_ctl_create(struct gctl_req *req, const char *flags, struct g_class *mp,
|
|
struct g_provider *pp, uint32_t entries)
|
|
{
|
|
struct uuid uuid;
|
|
struct g_consumer *cp;
|
|
struct g_geom *gp;
|
|
struct g_gpt_softc *softc;
|
|
struct gpt_hdr *hdr;
|
|
uint64_t last;
|
|
size_t tblsz;
|
|
int error, i;
|
|
|
|
G_GPT_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, pp->name));
|
|
g_topology_assert();
|
|
|
|
tblsz = (entries * sizeof(struct gpt_ent) + pp->sectorsize - 1) /
|
|
pp->sectorsize;
|
|
|
|
/*
|
|
* Sanity-check the size of the provider. This test is very similar
|
|
* to the one in g_gpt_taste(). Here we want to make sure that the
|
|
* size of the provider is large enough to hold a GPT that has the
|
|
* requested number of entries, plus as many available sectors for
|
|
* partitions of minimal size. The latter test is not exactly needed
|
|
* but it helps keep the table size proportional to the media size.
|
|
* Thus, a GPT with 128 entries must at least have 128 sectors of
|
|
* usable partition space. Therefore, the absolute minimal size we
|
|
* allow is (1 + 2 * (1 + 32) + 128) = 195 sectors. This is more
|
|
* restrictive than what g_gpt_taste() requires.
|
|
*/
|
|
if (pp->sectorsize < 512 ||
|
|
pp->sectorsize % sizeof(struct gpt_ent) != 0 ||
|
|
pp->mediasize < (3 + 2 * tblsz + entries) * pp->sectorsize) {
|
|
gctl_error(req, "%d provider", ENOSPC);
|
|
return (NULL);
|
|
}
|
|
|
|
/* We don't nest. See also g_gpt_taste(). */
|
|
if (pp->geom->class == &g_gpt_class) {
|
|
gctl_error(req, "%d provider", ENODEV);
|
|
return (NULL);
|
|
}
|
|
|
|
/* Create a GEOM. */
|
|
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);
|
|
gctl_error(req, "%d geom '%s'", error, pp->name);
|
|
return (NULL);
|
|
}
|
|
|
|
last = (pp->mediasize / pp->sectorsize) - 1;
|
|
kern_uuidgen(&uuid, 1);
|
|
|
|
/* Construct an in-memory GPT. */
|
|
for (i = GPT_HDR_PRIMARY; i < GPT_HDR_COUNT; i++) {
|
|
hdr = softc->hdr + i;
|
|
bcopy(GPT_HDR_SIG, hdr->hdr_sig, sizeof(hdr->hdr_sig));
|
|
hdr->hdr_revision = GPT_HDR_REVISION;
|
|
hdr->hdr_size = offsetof(struct gpt_hdr, padding);
|
|
hdr->hdr_lba_self = (i == GPT_HDR_PRIMARY) ? 1 : last;
|
|
hdr->hdr_lba_alt = (i == GPT_HDR_PRIMARY) ? last : 1;
|
|
hdr->hdr_lba_start = 2 + tblsz;
|
|
hdr->hdr_lba_end = last - (1 + tblsz);
|
|
hdr->hdr_uuid = uuid;
|
|
hdr->hdr_lba_table = (i == GPT_HDR_PRIMARY) ? 2 : last - tblsz;
|
|
hdr->hdr_entries = entries;
|
|
hdr->hdr_entsz = sizeof(struct gpt_ent);
|
|
softc->state[i] = GPT_HDR_OK;
|
|
}
|
|
|
|
if (0)
|
|
goto fail;
|
|
|
|
if (bootverbose) {
|
|
printf("GEOM: %s: GPT ", pp->name);
|
|
printf_uuid(&softc->hdr[GPT_HDR_PRIMARY].hdr_uuid);
|
|
printf(".\n");
|
|
}
|
|
|
|
g_access(cp, -1, 0, 0);
|
|
return (gp);
|
|
|
|
fail:
|
|
g_access(cp, -1, 0, 0);
|
|
g_gpt_wither(gp, error);
|
|
gctl_error(req, "%d geom '%s'", error, pp->name);
|
|
return (NULL);
|
|
}
|
|
|
|
static void
|
|
g_gpt_ctl_destroy(struct gctl_req *req, const char *flags, struct g_geom *gp)
|
|
{
|
|
}
|
|
|
|
static void
|
|
g_gpt_ctl_modify(struct gctl_req *req, const char *flags, struct g_geom *gp,
|
|
long entry)
|
|
{
|
|
}
|
|
|
|
static void
|
|
g_gpt_ctl_recover(struct gctl_req *req, const char *flags, struct g_geom *gp)
|
|
{
|
|
}
|
|
|
|
static void
|
|
g_gpt_ctl_remove(struct gctl_req *req, const char *flags, struct g_geom *gp,
|
|
long entry)
|
|
{
|
|
struct g_provider *pp;
|
|
struct g_gpt_softc *softc;
|
|
struct g_gpt_part *part;
|
|
|
|
G_GPT_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
|
|
g_topology_assert();
|
|
|
|
softc = gp->softc;
|
|
|
|
LIST_FOREACH(part, &softc->parts, parts) {
|
|
if ((long)part->index == entry - 1)
|
|
break;
|
|
}
|
|
if (part == NULL) {
|
|
gctl_error(req, "%d entry %ld", ENOENT, entry);
|
|
return;
|
|
}
|
|
|
|
pp = part->provider;
|
|
if (pp->acr > 0 || pp->acw > 0 || pp->ace > 0) {
|
|
gctl_error(req, "%d", EBUSY);
|
|
return;
|
|
}
|
|
|
|
LIST_REMOVE(part, parts);
|
|
pp->private = NULL;
|
|
g_wither_provider(pp, ENXIO);
|
|
g_free(part);
|
|
}
|
|
|
|
static int
|
|
g_gpt_has_pmbr(struct g_consumer *cp, int *error)
|
|
{
|
|
char *buf;
|
|
uint8_t *typ;
|
|
uint64_t mediasize;
|
|
int i, pmbr, parts;
|
|
uint32_t dp_start, dp_size;
|
|
uint16_t magic;
|
|
|
|
buf = g_read_data(cp, 0L, cp->provider->sectorsize, error);
|
|
if (buf == NULL)
|
|
return (0);
|
|
|
|
pmbr = 0;
|
|
*error = 0;
|
|
|
|
magic = le16toh(*(uint16_t *)(uintptr_t)(buf + DOSMAGICOFFSET));
|
|
if (magic != DOSMAGIC)
|
|
goto out;
|
|
|
|
/*
|
|
* Check that there are at least one partition of type
|
|
* DOSPTYP_PMBR that covers the whole unit.
|
|
*/
|
|
parts = 0;
|
|
mediasize = cp->provider->mediasize / cp->provider->sectorsize;
|
|
for (i = 0; i < 4; i++) {
|
|
typ = buf + DOSPARTOFF + i * sizeof(struct dos_partition) +
|
|
offsetof(struct dos_partition, dp_typ);
|
|
if (*typ != 0)
|
|
parts++;
|
|
if (*typ != DOSPTYP_PMBR)
|
|
continue;
|
|
|
|
bcopy(buf + DOSPARTOFF + i * sizeof(struct dos_partition) +
|
|
offsetof(struct dos_partition, dp_start), &dp_start,
|
|
sizeof(dp_start));
|
|
if (le32toh(dp_start) != 1)
|
|
break;
|
|
|
|
bcopy(buf + DOSPARTOFF + i * sizeof(struct dos_partition) +
|
|
offsetof(struct dos_partition, dp_size), &dp_size,
|
|
sizeof(dp_size));
|
|
if (le32toh(dp_size) != ~0U &&
|
|
le32toh(dp_size) != mediasize - 1 &&
|
|
/* Catch old FreeBSD bug for backward compatibility. */
|
|
le32toh(dp_size) != mediasize)
|
|
break;
|
|
|
|
pmbr = 1;
|
|
}
|
|
|
|
/*
|
|
* Treat empty MBRs as PMBRs for increased flexibility. Note that an
|
|
* invalid entry of type DOSPTYP_PMBR counts towards the number of
|
|
* partitions and will prevent the MBR from being treated as a PMBR.
|
|
*/
|
|
if (!pmbr && parts == 0)
|
|
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_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)
|
|
{
|
|
struct uuid type;
|
|
struct g_geom *gp;
|
|
struct g_provider *pp;
|
|
struct g_gpt_softc *softc;
|
|
const char *flags;
|
|
char const *s;
|
|
uint64_t start, end;
|
|
long entry, entries;
|
|
enum g_gpt_ctl ctlreq;
|
|
int error;
|
|
|
|
G_GPT_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, verb));
|
|
g_topology_assert();
|
|
|
|
/*
|
|
* Improve error reporting by first checking if the verb is
|
|
* a valid one. It also allows us to make assumptions down-
|
|
* stream about the validity of the verb.
|
|
*/
|
|
ctlreq = G_GPT_CTL_NONE;
|
|
switch (*verb) {
|
|
case 'a':
|
|
if (!strcmp(verb, "add"))
|
|
ctlreq = G_GPT_CTL_ADD;
|
|
break;
|
|
case 'c':
|
|
if (!strcmp(verb, "create"))
|
|
ctlreq = G_GPT_CTL_CREATE;
|
|
break;
|
|
case 'd':
|
|
if (!strcmp(verb, "destroy"))
|
|
ctlreq = G_GPT_CTL_DESTROY;
|
|
break;
|
|
case 'm':
|
|
if (!strcmp(verb, "modify"))
|
|
ctlreq = G_GPT_CTL_MODIFY;
|
|
break;
|
|
case 'r':
|
|
if (!strcmp(verb, "recover"))
|
|
ctlreq = G_GPT_CTL_RECOVER;
|
|
else if (!strcmp(verb, "remove"))
|
|
ctlreq = G_GPT_CTL_REMOVE;
|
|
break;
|
|
}
|
|
if (ctlreq == G_GPT_CTL_NONE) {
|
|
gctl_error(req, "%d verb '%s'", EINVAL, verb);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* All verbs take an optional flags parameter. The flags parameter
|
|
* is a string with each letter an independent flag. Each verb has
|
|
* it's own set of valid flags and the meaning of the flags is
|
|
* specific to the verb. Typically the presence of a letter (=flag)
|
|
* in the string means true and the absence means false.
|
|
*/
|
|
s = gctl_get_asciiparam(req, "flags");
|
|
flags = (s == NULL) ? "" : s;
|
|
|
|
/*
|
|
* Only the create verb takes a provider parameter. Make this a
|
|
* special case so that more code sharing is possible for the
|
|
* common case.
|
|
*/
|
|
if (ctlreq == G_GPT_CTL_CREATE) {
|
|
/*
|
|
* Create a GPT on a pristine disk-like provider.
|
|
* Required parameters/attributes:
|
|
* provider
|
|
* Optional parameters/attributes:
|
|
* entries
|
|
*/
|
|
s = gctl_get_asciiparam(req, "provider");
|
|
if (s == NULL) {
|
|
gctl_error(req, "%d provider", ENOATTR);
|
|
return;
|
|
}
|
|
pp = g_provider_by_name(s);
|
|
if (pp == NULL) {
|
|
gctl_error(req, "%d provider '%s'", EINVAL, s);
|
|
return;
|
|
}
|
|
/* Check that there isn't already a GPT on the provider. */
|
|
LIST_FOREACH(gp, &mp->geom, geom) {
|
|
if (!strcmp(s, gp->name)) {
|
|
gctl_error(req, "%d geom '%s'", EEXIST, s);
|
|
return;
|
|
}
|
|
}
|
|
s = gctl_get_asciiparam(req, "entries");
|
|
if (s != NULL) {
|
|
entries = strtol(s, (char **)(uintptr_t)&s, 0);
|
|
if (entries < 128 || *s != '\0') {
|
|
gctl_error(req, "%d entries %ld", EINVAL,
|
|
entries);
|
|
return;
|
|
}
|
|
} else
|
|
entries = 128; /* Documented mininum */
|
|
gp = g_gpt_ctl_create(req, flags, mp, pp, entries);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* All but the create verb, which is handled above, operate on an
|
|
* existing GPT geom. The geom parameter is non-optional, so get
|
|
* it here first.
|
|
*/
|
|
s = gctl_get_asciiparam(req, "geom");
|
|
if (s == NULL) {
|
|
gctl_error(req, "%d geom", ENOATTR);
|
|
return;
|
|
}
|
|
/* Get the GPT geom with the given name. */
|
|
LIST_FOREACH(gp, &mp->geom, geom) {
|
|
if (!strcmp(s, gp->name))
|
|
break;
|
|
}
|
|
if (gp == NULL) {
|
|
gctl_error(req, "%d geom '%s'", EINVAL, s);
|
|
return;
|
|
}
|
|
softc = gp->softc;
|
|
|
|
/*
|
|
* Now handle the verbs that can operate on a downgraded or
|
|
* partially corrupted GPT. In particular these are the verbs
|
|
* that don't deal with the table entries. We implement the
|
|
* policy that all table entry related requests require a
|
|
* valid GPT.
|
|
*/
|
|
if (ctlreq == G_GPT_CTL_DESTROY) {
|
|
/*
|
|
* Destroy a GPT completely.
|
|
*/
|
|
g_gpt_ctl_destroy(req, flags, gp);
|
|
return;
|
|
}
|
|
if (ctlreq == G_GPT_CTL_RECOVER) {
|
|
/*
|
|
* Recover a downgraded GPT.
|
|
*/
|
|
g_gpt_ctl_recover(req, flags, gp);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Check that the GPT is complete and valid before we make changes
|
|
* to the table entries.
|
|
*/
|
|
if (softc->state[GPT_HDR_PRIMARY] != GPT_HDR_OK ||
|
|
softc->state[GPT_HDR_SECONDARY] != GPT_HDR_OK) {
|
|
gctl_error(req, "%d geom '%s'", ENXIO, s);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* The add verb is the only table entry related verb that doesn't
|
|
* require the entry parameter. All other verbs identify the table
|
|
* entry by the entry number. Handle the add here.
|
|
*/
|
|
if (ctlreq == G_GPT_CTL_ADD) {
|
|
/*
|
|
* Add a partition entry to a GPT.
|
|
* Required parameters/attributes:
|
|
* type
|
|
* start
|
|
* end
|
|
* Optional parameters/attributes:
|
|
* entry (read/write)
|
|
* label
|
|
*/
|
|
s = gctl_get_asciiparam(req, "type");
|
|
if (s == NULL) {
|
|
gctl_error(req, "%d type", ENOATTR);
|
|
return;
|
|
}
|
|
error = parse_uuid(s, &type);
|
|
if (error != 0) {
|
|
gctl_error(req, "%d type '%s'", error, s);
|
|
return;
|
|
}
|
|
s = gctl_get_asciiparam(req, "start");
|
|
if (s == NULL) {
|
|
gctl_error(req, "%d start", ENOATTR);
|
|
return;
|
|
}
|
|
start = strtoq(s, (char **)(uintptr_t)&s, 0);
|
|
if (start < softc->hdr[GPT_HDR_PRIMARY].hdr_lba_start ||
|
|
start > softc->hdr[GPT_HDR_PRIMARY].hdr_lba_end ||
|
|
*s != '\0') {
|
|
gctl_error(req, "%d start %jd", EINVAL,
|
|
(intmax_t)start);
|
|
return;
|
|
}
|
|
s = gctl_get_asciiparam(req, "end");
|
|
if (s == NULL) {
|
|
gctl_error(req, "%d end", ENOATTR);
|
|
return;
|
|
}
|
|
end = strtoq(s, (char **)(uintptr_t)&s, 0);
|
|
if (end < start ||
|
|
end > softc->hdr[GPT_HDR_PRIMARY].hdr_lba_end ||
|
|
*s != '\0') {
|
|
gctl_error(req, "%d end %jd", EINVAL,
|
|
(intmax_t)end);
|
|
return;
|
|
}
|
|
entry = 0;
|
|
s = gctl_get_asciiparam(req, "entry");
|
|
if (s != NULL && *s != '\0') {
|
|
entry = strtol(s, (char **)(uintptr_t)&s, 0);
|
|
if (*s != '\0' || entry <= 0 ||
|
|
entry > softc->hdr[GPT_HDR_PRIMARY].hdr_entries) {
|
|
gctl_error(req, "%d entry %ld", EINVAL, entry);
|
|
return;
|
|
}
|
|
}
|
|
g_gpt_ctl_add(req, flags, gp, &type, start, end, entry);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Get the table entry number. Entry numbers run from 1 to the
|
|
* number of entries in the table.
|
|
*/
|
|
s = gctl_get_asciiparam(req, "entry");
|
|
if (s == NULL) {
|
|
gctl_error(req, "%d entry", ENOATTR);
|
|
return;
|
|
}
|
|
entry = strtol(s, (char **)(uintptr_t)&s, 0);
|
|
if (*s != '\0' || entry <= 0 ||
|
|
entry > softc->hdr[GPT_HDR_PRIMARY].hdr_entries) {
|
|
gctl_error(req, "%d entry %ld", EINVAL, entry);
|
|
return;
|
|
}
|
|
|
|
if (ctlreq == G_GPT_CTL_MODIFY) {
|
|
/*
|
|
* Modify a partition entry.
|
|
*/
|
|
g_gpt_ctl_modify(req, flags, gp, entry);
|
|
return;
|
|
}
|
|
if (ctlreq == G_GPT_CTL_REMOVE) {
|
|
/*
|
|
* Remove a partition entry.
|
|
*/
|
|
g_gpt_ctl_remove(req, flags, gp, entry);
|
|
return;
|
|
}
|
|
}
|
|
|
|
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();
|
|
|
|
/*
|
|
* 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();
|
|
|
|
/*
|
|
* Now that we have access permissions, we can 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). Lastly, since the
|
|
* theoretical minimum number of sectors needed by GPT is 6, any
|
|
* medium that has less than 6 sectors is never going to be able
|
|
* 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)
|
|
goto fail;
|
|
|
|
/*
|
|
* 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 (buf == NULL)
|
|
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 (buf == NULL)
|
|
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)) {
|
|
if (error != 0)
|
|
goto fail;
|
|
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 (buf == NULL)
|
|
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 (buf == NULL)
|
|
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)
|
|
{
|
|
static char *status[5] = {
|
|
"unknown", "missing", "corrupt", "invalid", "ok"
|
|
};
|
|
struct g_gpt_part *part;
|
|
struct g_gpt_softc *softc;
|
|
struct gpt_hdr *hdr;
|
|
|
|
KASSERT(sb != NULL && gp != NULL, (__func__));
|
|
|
|
if (indent == NULL) {
|
|
KASSERT(cp == NULL && pp != NULL, (__func__));
|
|
part = pp->private;
|
|
if (part == NULL)
|
|
return;
|
|
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;
|
|
if (part == NULL)
|
|
return;
|
|
sbuf_printf(sb, "%s<index>%u</index>\n", indent, pp->index);
|
|
sbuf_printf(sb, "%s<type>", indent);
|
|
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 void
|
|
g_gpt_orphan(struct g_consumer *cp)
|
|
{
|
|
struct g_provider *pp;
|
|
|
|
pp = cp->provider;
|
|
KASSERT(pp != NULL, (__func__));
|
|
G_GPT_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, pp->name));
|
|
g_topology_assert();
|
|
|
|
KASSERT(pp->error != 0, (__func__));
|
|
g_gpt_wither(cp->geom, pp->error);
|
|
}
|
|
|
|
static void
|
|
g_gpt_spoiled(struct g_consumer *cp)
|
|
{
|
|
|
|
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;
|
|
cp = LIST_FIRST(&gp->consumer);
|
|
|
|
G_GPT_TRACE((G_T_BIO, "%s: cmd=%d, provider=%s", __func__, bp->bio_cmd,
|
|
pp->name));
|
|
|
|
part = pp->private;
|
|
if (part == NULL) {
|
|
g_io_deliver(bp, ENXIO);
|
|
return;
|
|
}
|
|
|
|
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 */
|
|
}
|
|
/* FALLTHROUGH */
|
|
case BIO_FLUSH:
|
|
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;
|
|
}
|
|
}
|