diff options
author | marcel <marcel@FreeBSD.org> | 2009-02-16 03:54:28 +0000 |
---|---|---|
committer | marcel <marcel@FreeBSD.org> | 2009-02-16 03:54:28 +0000 |
commit | ccc21e2d6192e62b2a2a6980f2748b0b0a2928e6 (patch) | |
tree | 471a5f8f0d2eebcbf55479c89fc27798fc52b884 /sys/geom/part | |
parent | b0997d1a78f9a42ec76c4b8c54a334622e41518a (diff) | |
download | FreeBSD-src-ccc21e2d6192e62b2a2a6980f2748b0b0a2928e6.zip FreeBSD-src-ccc21e2d6192e62b2a2a6980f2748b0b0a2928e6.tar.gz |
Add support to add, delete and modify logical partitions, as well
as to create and destroy the extended partitioning scheme. In
other words: full support.
Diffstat (limited to 'sys/geom/part')
-rw-r--r-- | sys/geom/part/g_part_ebr.c | 257 |
1 files changed, 251 insertions, 6 deletions
diff --git a/sys/geom/part/g_part_ebr.c b/sys/geom/part/g_part_ebr.c index 64534fe..0e65e6a 100644 --- a/sys/geom/part/g_part_ebr.c +++ b/sys/geom/part/g_part_ebr.c @@ -39,7 +39,6 @@ __FBSDID("$FreeBSD$"); #include <sys/mutex.h> #include <sys/queue.h> #include <sys/sbuf.h> -#include <sys/syscallsubr.h> #include <sys/systm.h> #include <geom/geom.h> #include <geom/part/g_part.h> @@ -68,6 +67,8 @@ static int g_part_ebr_modify(struct g_part_table *, struct g_part_entry *, struct g_part_parms *); static const char *g_part_ebr_name(struct g_part_table *, struct g_part_entry *, char *, size_t); +static int g_part_ebr_precheck(struct g_part_table *, enum g_part_ctl, + struct g_part_parms *); static int g_part_ebr_probe(struct g_part_table *, struct g_consumer *); static int g_part_ebr_read(struct g_part_table *, struct g_consumer *); static int g_part_ebr_setunset(struct g_part_table *, struct g_part_entry *, @@ -84,6 +85,7 @@ static kobj_method_t g_part_ebr_methods[] = { KOBJMETHOD(g_part_dumpto, g_part_ebr_dumpto), KOBJMETHOD(g_part_modify, g_part_ebr_modify), KOBJMETHOD(g_part_name, g_part_ebr_name), + KOBJMETHOD(g_part_precheck, g_part_ebr_precheck), KOBJMETHOD(g_part_probe, g_part_ebr_probe), KOBJMETHOD(g_part_read, g_part_ebr_read), KOBJMETHOD(g_part_setunset, g_part_ebr_setunset), @@ -102,6 +104,9 @@ static struct g_part_scheme g_part_ebr_scheme = { }; G_PART_SCHEME_DECLARE(g_part_ebr); +static void ebr_set_chs(struct g_part_table *, uint32_t, u_char *, u_char *, + u_char *); + static void ebr_entry_decode(const char *p, struct dos_partition *ent) { @@ -117,19 +122,140 @@ ebr_entry_decode(const char *p, struct dos_partition *ent) ent->dp_size = le32dec(p + 12); } +static void +ebr_entry_link(struct g_part_table *table, uint32_t start, uint32_t end, + u_char *buf) +{ + + buf[0] = 0 /* dp_flag */; + ebr_set_chs(table, start, &buf[3] /* dp_scyl */, &buf[1] /* dp_shd */, + &buf[2] /* dp_ssect */); + buf[4] = 5 /* dp_typ */; + ebr_set_chs(table, end, &buf[7] /* dp_ecyl */, &buf[5] /* dp_ehd */, + &buf[6] /* dp_esect */); + le32enc(buf + 8, start); + le32enc(buf + 12, end - start + 1); +} + +static int +ebr_parse_type(const char *type, u_char *dp_typ) +{ + const char *alias; + char *endp; + long lt; + + if (type[0] == '!') { + lt = strtol(type + 1, &endp, 0); + if (type[1] == '\0' || *endp != '\0' || lt <= 0 || lt >= 256) + return (EINVAL); + *dp_typ = (u_char)lt; + return (0); + } + alias = g_part_alias_name(G_PART_ALIAS_FREEBSD); + if (!strcasecmp(type, alias)) { + *dp_typ = DOSPTYP_386BSD; + return (0); + } + return (EINVAL); +} + +static void +ebr_set_chs(struct g_part_table *table, uint32_t lba, u_char *cylp, u_char *hdp, + u_char *secp) +{ + uint32_t cyl, hd, sec; + + sec = lba % table->gpt_sectors + 1; + lba /= table->gpt_sectors; + hd = lba % table->gpt_heads; + lba /= table->gpt_heads; + cyl = lba; + if (cyl > 1023) + sec = hd = cyl = ~0; + + *cylp = cyl & 0xff; + *hdp = hd & 0xff; + *secp = (sec & 0x3f) | ((cyl >> 2) & 0xc0); +} + static int g_part_ebr_add(struct g_part_table *basetable, struct g_part_entry *baseentry, struct g_part_parms *gpp) { + struct g_geom *gp; + struct g_provider *pp; + struct g_part_ebr_entry *entry; + uint32_t start, size, sectors; + + if (gpp->gpp_parms & G_PART_PARM_LABEL) + return (EINVAL); + + gp = basetable->gpt_gp; + pp = LIST_FIRST(&gp->consumer)->provider; + sectors = basetable->gpt_sectors; - return (ENOSYS); + entry = (struct g_part_ebr_entry *)baseentry; + + start = gpp->gpp_start; + size = gpp->gpp_size; + if (size < 2 * sectors) + return (EINVAL); + if (start % sectors) { + size = size - sectors + (start % sectors); + start = start - (start % sectors) + sectors; + } + if (size % sectors) + size = size - (size % sectors); + if (size < 2 * sectors) + return (EINVAL); + + if (baseentry->gpe_deleted) + bzero(&entry->ent, sizeof(entry->ent)); + + KASSERT(baseentry->gpe_start <= start, (__func__)); + KASSERT(baseentry->gpe_end >= start + size - 1, (__func__)); + baseentry->gpe_offset = (off_t)(start + sectors) * pp->sectorsize; + baseentry->gpe_start = start; + baseentry->gpe_end = start + size - 1; + entry->ent.dp_start = sectors; + entry->ent.dp_size = size - sectors; + ebr_set_chs(basetable, entry->ent.dp_start, &entry->ent.dp_scyl, + &entry->ent.dp_shd, &entry->ent.dp_ssect); + ebr_set_chs(basetable, baseentry->gpe_end, &entry->ent.dp_ecyl, + &entry->ent.dp_ehd, &entry->ent.dp_esect); + return (ebr_parse_type(gpp->gpp_type, &entry->ent.dp_typ)); } static int g_part_ebr_create(struct g_part_table *basetable, struct g_part_parms *gpp) { + char psn[8]; + struct g_consumer *cp; + struct g_provider *pp; + uint64_t msize; + int error; + + pp = gpp->gpp_provider; + + if (pp->sectorsize < EBRSIZE) + return (ENOSPC); + if (pp->sectorsize > 4096) + return (ENXIO); - return (ENOSYS); + /* Check that we have a parent and that it's a MBR. */ + if (basetable->gpt_depth == 0) + return (ENXIO); + cp = LIST_FIRST(&pp->consumers); + error = g_getattr("PART::scheme", cp, &psn); + if (error) + return (error); + if (strcmp(psn, "MBR")) + return (ENXIO); + + msize = pp->mediasize / pp->sectorsize; + basetable->gpt_first = 0; + basetable->gpt_last = msize - (msize % basetable->gpt_sectors) - 1; + return (0); } static int @@ -176,8 +302,15 @@ static int g_part_ebr_modify(struct g_part_table *basetable, struct g_part_entry *baseentry, struct g_part_parms *gpp) { + struct g_part_ebr_entry *entry; - return (ENOSYS); + if (gpp->gpp_parms & G_PART_PARM_LABEL) + return (EINVAL); + + entry = (struct g_part_ebr_entry *)baseentry; + if (gpp->gpp_parms & G_PART_PARM_TYPE) + return (ebr_parse_type(gpp->gpp_type, &entry->ent.dp_typ)); + return (0); } static const char * @@ -190,6 +323,23 @@ g_part_ebr_name(struct g_part_table *table, struct g_part_entry *entry, } static int +g_part_ebr_precheck(struct g_part_table *table, enum g_part_ctl req, + struct g_part_parms *gpp) +{ + + /* + * The index is a function of the start of the partition. + * This is not something the user can override, nor is it + * something the common code will do right. We can set the + * index now so that we get what we need. + */ + if (req == G_PART_CTL_ADD) + gpp->gpp_index = (gpp->gpp_start / table->gpt_sectors) + 1; + + return (0); +} + +static int g_part_ebr_probe(struct g_part_table *table, struct g_consumer *cp) { char psn[8]; @@ -312,8 +462,37 @@ static int g_part_ebr_setunset(struct g_part_table *table, struct g_part_entry *baseentry, const char *attrib, unsigned int set) { + struct g_part_entry *iter; + struct g_part_ebr_entry *entry; + int changed; + + if (strcasecmp(attrib, "active") != 0) + return (EINVAL); - return (ENOSYS); + /* Only one entry can have the active attribute. */ + LIST_FOREACH(iter, &table->gpt_entry, gpe_entry) { + if (iter->gpe_deleted) + continue; + changed = 0; + entry = (struct g_part_ebr_entry *)iter; + if (iter == baseentry) { + if (set && (entry->ent.dp_flag & 0x80) == 0) { + entry->ent.dp_flag |= 0x80; + changed = 1; + } else if (!set && (entry->ent.dp_flag & 0x80)) { + entry->ent.dp_flag &= ~0x80; + changed = 1; + } + } else { + if (set && (entry->ent.dp_flag & 0x80)) { + entry->ent.dp_flag &= ~0x80; + changed = 1; + } + } + if (changed && !iter->gpe_created) + iter->gpe_modified = 1; + } + return (0); } static const char * @@ -334,6 +513,72 @@ g_part_ebr_type(struct g_part_table *basetable, struct g_part_entry *baseentry, static int g_part_ebr_write(struct g_part_table *basetable, struct g_consumer *cp) { + struct g_provider *pp; + struct g_part_entry *baseentry, *next; + struct g_part_ebr_entry *entry; + u_char *buf; + u_char *p; + int error; + + pp = cp->provider; + buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO); + le16enc(buf + DOSMAGICOFFSET, DOSMAGIC); + + baseentry = LIST_FIRST(&basetable->gpt_entry); + while (baseentry != NULL && baseentry->gpe_deleted) + baseentry = LIST_NEXT(baseentry, gpe_entry); + + /* Wipe-out the the first EBR when there are no slices. */ + if (baseentry == NULL) { + error = g_write_data(cp, 0, buf, pp->sectorsize); + goto out; + } + + /* + * If the first partition is not in LBA 0, we need to + * put a "link" EBR in LBA 0. + */ + if (baseentry->gpe_start != 0) { + ebr_entry_link(basetable, (uint32_t)baseentry->gpe_start, + (uint32_t)baseentry->gpe_end, buf + DOSPARTOFF); + error = g_write_data(cp, 0, buf, pp->sectorsize); + if (error) + goto out; + } + + do { + entry = (struct g_part_ebr_entry *)baseentry; + + p = buf + DOSPARTOFF; + p[0] = entry->ent.dp_flag; + p[1] = entry->ent.dp_shd; + p[2] = entry->ent.dp_ssect; + p[3] = entry->ent.dp_scyl; + p[4] = entry->ent.dp_typ; + p[5] = entry->ent.dp_ehd; + p[6] = entry->ent.dp_esect; + p[7] = entry->ent.dp_ecyl; + le32enc(p + 8, entry->ent.dp_start); + le32enc(p + 12, entry->ent.dp_size); + + do { + next = LIST_NEXT(baseentry, gpe_entry); + } while (next != NULL && next->gpe_deleted); - return (ENOSYS); + p += DOSPARTSIZE; + if (next != NULL) + ebr_entry_link(basetable, (uint32_t)next->gpe_start, + (uint32_t)next->gpe_end, p); + else + bzero(p, DOSPARTSIZE); + + error = g_write_data(cp, baseentry->gpe_start * pp->sectorsize, + buf, pp->sectorsize); + + baseentry = next; + } while (!error && baseentry != NULL); + + out: + g_free(buf); + return (error); } |