From 75edb3acc1ec8c2b0e752a545e673e12f6222fcd Mon Sep 17 00:00:00 2001 From: ae Date: Thu, 24 Jun 2010 05:52:44 +0000 Subject: [PATCH] MFC r207094 (by marcel): Implement the resize verb and add support for resizing partitions for all schemes but EBR. MFC r207095 (by marcel): Implement the resize command for resizing partitions. Without new size, the partition in question is resized to fill all available space. Approved by: kib (mentor) git-svn-id: svn://svn.freebsd.org/base/stable/8@209497 ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f --- sbin/geom/class/part/geom_part.c | 102 +++++++++++++++++++++++++++++++ sbin/geom/class/part/gpart.8 | 37 ++++++++++- sys/geom/part/g_part.c | 85 ++++++++++++++++++++++++-- sys/geom/part/g_part_apm.c | 16 +++++ sys/geom/part/g_part_bsd.c | 16 +++++ sys/geom/part/g_part_gpt.c | 16 +++++ sys/geom/part/g_part_if.m | 14 +++++ sys/geom/part/g_part_mbr.c | 28 +++++++++ sys/geom/part/g_part_pc98.c | 28 +++++++++ sys/geom/part/g_part_vtoc8.c | 23 +++++++ 10 files changed, 359 insertions(+), 6 deletions(-) diff --git a/sbin/geom/class/part/geom_part.c b/sbin/geom/class/part/geom_part.c index 15c6bcebf..9db9bd8bd 100644 --- a/sbin/geom/class/part/geom_part.c +++ b/sbin/geom/class/part/geom_part.c @@ -147,6 +147,13 @@ struct g_command PUBSYM(class_commands)[] = { G_OPT_SENTINEL }, "geom", NULL }, + { "resize", 0, gpart_issue, { + { 's', "size", autofill, G_TYPE_ASCLBA }, + { 'i', index_param, NULL, G_TYPE_ASCNUM }, + { 'f', "flags", flags, G_TYPE_STRING }, + G_OPT_SENTINEL }, + "geom", NULL + }, G_CMD_SENTINEL }; @@ -256,6 +263,99 @@ fmtattrib(struct gprovider *pp) return (buf); } +static int +gpart_autofill_resize(struct gctl_req *req) +{ + struct gmesh mesh; + struct gclass *cp; + struct ggeom *gp; + struct gprovider *pp; + unsigned long long last, size, start, new_size; + unsigned long long lba, new_lba; + const char *s; + char *val; + int error, idx; + + s = gctl_get_ascii(req, "size"); + if (*s == '*') + new_size = (unsigned long long)atoll(s); + else + return (0); + + s = gctl_get_ascii(req, index_param); + idx = strtol(s, &val, 10); + if (idx < 1 || *s == '\0' || *val != '\0') + errx(EXIT_FAILURE, "invalid partition index"); + + error = geom_gettree(&mesh); + if (error) + return (error); + s = gctl_get_ascii(req, "class"); + if (s == NULL) + abort(); + cp = find_class(&mesh, s); + if (cp == NULL) + errx(EXIT_FAILURE, "Class %s not found.", s); + s = gctl_get_ascii(req, "geom"); + if (s == NULL) + abort(); + gp = find_geom(cp, s); + if (gp == NULL) + errx(EXIT_FAILURE, "No such geom: %s.", s); + last = atoll(find_geomcfg(gp, "last")); + + LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { + s = find_provcfg(pp, "index"); + if (s == NULL) + continue; + if (atoi(s) == idx) + break; + } + if (pp == NULL) + errx(EXIT_FAILURE, "invalid partition index"); + + s = find_provcfg(pp, "start"); + if (s == NULL) { + s = find_provcfg(pp, "offset"); + start = atoll(s) / pp->lg_sectorsize; + } else + start = atoll(s); + s = find_provcfg(pp, "end"); + if (s == NULL) { + s = find_provcfg(pp, "length"); + lba = start + atoll(s) / pp->lg_sectorsize; + } else + lba = atoll(s) + 1; + + if (lba > last) + return (ENOSPC); + size = lba - start; + pp = find_provider(gp, lba); + if (pp == NULL) + new_size = last - start + 1; + else { + s = find_provcfg(pp, "start"); + if (s == NULL) { + s = find_provcfg(pp, "offset"); + new_lba = atoll(s) / pp->lg_sectorsize; + } else + new_lba = atoll(s); + /* Is there any free space between current and + * next providers? + */ + if (new_lba > lba) + new_size = new_lba - start; + else + return (ENOSPC); + } + asprintf(&val, "%llu", new_size); + if (val == NULL) + return (ENOMEM); + gctl_change_param(req, "size", -1, val); + + return (0); +} + static int gpart_autofill(struct gctl_req *req) { @@ -271,6 +371,8 @@ gpart_autofill(struct gctl_req *req) int error, has_size, has_start; s = gctl_get_ascii(req, "verb"); + if (strcmp(s, "resize") == 0) + return gpart_autofill_resize(req); if (strcmp(s, "add") != 0) return (0); diff --git a/sbin/geom/class/part/gpart.8 b/sbin/geom/class/part/gpart.8 index 5f0f8792a..883069c1c 100644 --- a/sbin/geom/class/part/gpart.8 +++ b/sbin/geom/class/part/gpart.8 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd June 22, 2010 +.Dd June 24, 2010 .Dt GPART 8 .Os .Sh NAME @@ -123,6 +123,13 @@ utility: .Op Fl t Ar type .Op Fl f Ar flags .Ar geom +.\" ==== RESIZE ==== +.Nm +.Cm resize +.Fl i Ar index +.Op Fl s Ar size +.Op Fl f Ar flags +.Ar geom .\" ==== SET ==== .Nm .Cm set @@ -334,6 +341,32 @@ See the section entitled below for a discussion about its use. .El +.\" ==== RESIZE ==== +.It Cm resize +Resize a partition from geom +.Ar geom +and further identified by the +.Fl i Ar index +option. +New partition size is expressed in logical block +numbers and can be given by the +.Fl s Ar size +option. +If +.Fl s +option is ommited then new size is automatically calculated +to maximum available from given geom +.Ar geom . +.Pp +Additional options include: +.Bl -tag -width 10n +.It Fl f Ar flags +Additional operational flags. +See the section entitled +.Sx "OPERATIONAL FLAGS" +below for a discussion +about its use. +.El .\" ==== SET ==== .It Cm set Set the named attribute on the partition entry. @@ -450,7 +483,7 @@ The scheme-specific types are .Qq Li "!FreeBSD-ZFS" for APM, .Qq Li "!516e7cba-6ecf-11d6-8ff8-00022d09712b" - for GPT, and 0x0904 for VTOC8. +for GPT, and 0x0904 for VTOC8. .It Cm mbr A partition that is sub-partitioned by a master boot record (MBR). This type is known as diff --git a/sys/geom/part/g_part.c b/sys/geom/part/g_part.c index 689b15a6f..d1b830d96 100644 --- a/sys/geom/part/g_part.c +++ b/sys/geom/part/g_part.c @@ -968,9 +968,85 @@ g_part_ctl_recover(struct gctl_req *req, struct g_part_parms *gpp) static int g_part_ctl_resize(struct gctl_req *req, struct g_part_parms *gpp) { - gctl_error(req, "%d verb 'resize'", ENOSYS); - return (ENOSYS); -} + struct g_geom *gp; + struct g_provider *pp; + struct g_part_entry *pe, *entry; + struct g_part_table *table; + struct sbuf *sb; + quad_t end; + int error; + + gp = gpp->gpp_geom; + G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name)); + g_topology_assert(); + table = gp->softc; + + /* check gpp_index */ + LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) { + if (entry->gpe_deleted || entry->gpe_internal) + continue; + if (entry->gpe_index == gpp->gpp_index) + break; + } + if (entry == NULL) { + gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index); + return (ENOENT); + } + + /* check gpp_size */ + end = entry->gpe_start + gpp->gpp_size - 1; + if (gpp->gpp_size < 1 || end > table->gpt_last) { + gctl_error(req, "%d size '%jd'", EINVAL, + (intmax_t)gpp->gpp_size); + return (EINVAL); + } + + LIST_FOREACH(pe, &table->gpt_entry, gpe_entry) { + if (pe->gpe_deleted || pe->gpe_internal || pe == entry) + continue; + if (end >= pe->gpe_start && end <= pe->gpe_end) { + gctl_error(req, "%d end '%jd'", ENOSPC, + (intmax_t)end); + return (ENOSPC); + } + if (entry->gpe_start < pe->gpe_start && end > pe->gpe_end) { + gctl_error(req, "%d size '%jd'", ENOSPC, + (intmax_t)gpp->gpp_size); + return (ENOSPC); + } + } + + pp = entry->gpe_pp; + if ((g_debugflags & 16) == 0 && + (pp->acr > 0 || pp->acw > 0 || pp->ace > 0)) { + gctl_error(req, "%d", EBUSY); + return (EBUSY); + } + + error = G_PART_RESIZE(table, entry, gpp); + if (error) { + gctl_error(req, "%d", error); + return (error); + } + + if (!entry->gpe_created) + entry->gpe_modified = 1; + + /* update mediasize of changed provider */ + pp->mediasize = (entry->gpe_end - entry->gpe_start + 1) * + pp->sectorsize; + + /* Provide feedback if so requested. */ + if (gpp->gpp_parms & G_PART_PARM_OUTPUT) { + sb = sbuf_new_auto(); + G_PART_FULLNAME(table, entry, sb, gp->name); + sbuf_cat(sb, " resized\n"); + sbuf_finish(sb); + gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1); + sbuf_delete(sb); + } + return (0); +} static int g_part_ctl_setunset(struct gctl_req *req, struct g_part_parms *gpp, @@ -1206,7 +1282,8 @@ g_part_ctlreq(struct gctl_req *req, struct g_class *mp, const char *verb) mparms |= G_PART_PARM_GEOM; } else if (!strcmp(verb, "resize")) { ctlreq = G_PART_CTL_RESIZE; - mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX; + mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX | + G_PART_PARM_SIZE; } break; case 's': diff --git a/sys/geom/part/g_part_apm.c b/sys/geom/part/g_part_apm.c index 9f348295c..c73d7f6f0 100644 --- a/sys/geom/part/g_part_apm.c +++ b/sys/geom/part/g_part_apm.c @@ -74,6 +74,8 @@ static int g_part_apm_read(struct g_part_table *, struct g_consumer *); static const char *g_part_apm_type(struct g_part_table *, struct g_part_entry *, char *, size_t); static int g_part_apm_write(struct g_part_table *, struct g_consumer *); +static int g_part_apm_resize(struct g_part_table *, struct g_part_entry *, + struct g_part_parms *); static kobj_method_t g_part_apm_methods[] = { KOBJMETHOD(g_part_add, g_part_apm_add), @@ -82,6 +84,7 @@ static kobj_method_t g_part_apm_methods[] = { KOBJMETHOD(g_part_dumpconf, g_part_apm_dumpconf), KOBJMETHOD(g_part_dumpto, g_part_apm_dumpto), KOBJMETHOD(g_part_modify, g_part_apm_modify), + KOBJMETHOD(g_part_resize, g_part_apm_resize), KOBJMETHOD(g_part_name, g_part_apm_name), KOBJMETHOD(g_part_probe, g_part_apm_probe), KOBJMETHOD(g_part_read, g_part_apm_read), @@ -338,6 +341,19 @@ g_part_apm_modify(struct g_part_table *basetable, return (0); } +static int +g_part_apm_resize(struct g_part_table *basetable, + struct g_part_entry *baseentry, struct g_part_parms *gpp) +{ + struct g_part_apm_entry *entry; + + entry = (struct g_part_apm_entry *)baseentry; + baseentry->gpe_end = baseentry->gpe_start + gpp->gpp_size - 1; + entry->ent.ent_size = gpp->gpp_size; + + return (0); +} + static const char * g_part_apm_name(struct g_part_table *table, struct g_part_entry *baseentry, char *buf, size_t bufsz) diff --git a/sys/geom/part/g_part_bsd.c b/sys/geom/part/g_part_bsd.c index 82e6bcb3e..2fb47be86 100644 --- a/sys/geom/part/g_part_bsd.c +++ b/sys/geom/part/g_part_bsd.c @@ -73,6 +73,8 @@ static int g_part_bsd_read(struct g_part_table *, struct g_consumer *); static const char *g_part_bsd_type(struct g_part_table *, struct g_part_entry *, char *, size_t); static int g_part_bsd_write(struct g_part_table *, struct g_consumer *); +static int g_part_bsd_resize(struct g_part_table *, struct g_part_entry *, + struct g_part_parms *); static kobj_method_t g_part_bsd_methods[] = { KOBJMETHOD(g_part_add, g_part_bsd_add), @@ -82,6 +84,7 @@ static kobj_method_t g_part_bsd_methods[] = { KOBJMETHOD(g_part_dumpconf, g_part_bsd_dumpconf), KOBJMETHOD(g_part_dumpto, g_part_bsd_dumpto), KOBJMETHOD(g_part_modify, g_part_bsd_modify), + KOBJMETHOD(g_part_resize, g_part_bsd_resize), KOBJMETHOD(g_part_name, g_part_bsd_name), KOBJMETHOD(g_part_probe, g_part_bsd_probe), KOBJMETHOD(g_part_read, g_part_bsd_read), @@ -296,6 +299,19 @@ g_part_bsd_modify(struct g_part_table *basetable, return (0); } +static int +g_part_bsd_resize(struct g_part_table *basetable, + struct g_part_entry *baseentry, struct g_part_parms *gpp) +{ + struct g_part_bsd_entry *entry; + + entry = (struct g_part_bsd_entry *)baseentry; + baseentry->gpe_end = baseentry->gpe_start + gpp->gpp_size - 1; + entry->part.p_size = gpp->gpp_size; + + return (0); +} + static const char * g_part_bsd_name(struct g_part_table *table, struct g_part_entry *baseentry, char *buf, size_t bufsz) diff --git a/sys/geom/part/g_part_gpt.c b/sys/geom/part/g_part_gpt.c index 7347ca5e5..17da88392 100644 --- a/sys/geom/part/g_part_gpt.c +++ b/sys/geom/part/g_part_gpt.c @@ -103,6 +103,8 @@ static int g_part_gpt_read(struct g_part_table *, struct g_consumer *); static const char *g_part_gpt_type(struct g_part_table *, struct g_part_entry *, char *, size_t); static int g_part_gpt_write(struct g_part_table *, struct g_consumer *); +static int g_part_gpt_resize(struct g_part_table *, struct g_part_entry *, + struct g_part_parms *); static kobj_method_t g_part_gpt_methods[] = { KOBJMETHOD(g_part_add, g_part_gpt_add), @@ -112,6 +114,7 @@ static kobj_method_t g_part_gpt_methods[] = { KOBJMETHOD(g_part_dumpconf, g_part_gpt_dumpconf), KOBJMETHOD(g_part_dumpto, g_part_gpt_dumpto), KOBJMETHOD(g_part_modify, g_part_gpt_modify), + KOBJMETHOD(g_part_resize, g_part_gpt_resize), KOBJMETHOD(g_part_name, g_part_gpt_name), KOBJMETHOD(g_part_probe, g_part_gpt_probe), KOBJMETHOD(g_part_read, g_part_gpt_read), @@ -532,6 +535,19 @@ g_part_gpt_modify(struct g_part_table *basetable, return (0); } +static int +g_part_gpt_resize(struct g_part_table *basetable, + struct g_part_entry *baseentry, struct g_part_parms *gpp) +{ + struct g_part_gpt_entry *entry; + entry = (struct g_part_gpt_entry *)baseentry; + + baseentry->gpe_end = baseentry->gpe_start + gpp->gpp_size - 1; + entry->ent.ent_lba_end = baseentry->gpe_end; + + return (0); +} + static const char * g_part_gpt_name(struct g_part_table *table, struct g_part_entry *baseentry, char *buf, size_t bufsz) diff --git a/sys/geom/part/g_part_if.m b/sys/geom/part/g_part_if.m index 8252f0cad..04440fef1 100644 --- a/sys/geom/part/g_part_if.m +++ b/sys/geom/part/g_part_if.m @@ -58,6 +58,13 @@ CODE { { return (0); } + + static int + default_resize(struct g_part_table *t __unused, + struct g_part_entry *e __unused, struct g_part_parms *p __unused) + { + return (ENOSYS); + } }; # add() - scheme specific processing for the add verb. @@ -114,6 +121,13 @@ METHOD int modify { struct g_part_parms *gpp; }; +# resize() - scheme specific processing for the resize verb. +METHOD int resize { + struct g_part_table *table; + struct g_part_entry *entry; + struct g_part_parms *gpp; +} DEFAULT default_resize; + # name() - return the name of the given partition entry. # Typical names are "p1", "s0" or "c". METHOD const char * name { diff --git a/sys/geom/part/g_part_mbr.c b/sys/geom/part/g_part_mbr.c index 72d0ecbfe..8b5ba2779 100644 --- a/sys/geom/part/g_part_mbr.c +++ b/sys/geom/part/g_part_mbr.c @@ -76,6 +76,8 @@ static int g_part_mbr_setunset(struct g_part_table *, struct g_part_entry *, static const char *g_part_mbr_type(struct g_part_table *, struct g_part_entry *, char *, size_t); static int g_part_mbr_write(struct g_part_table *, struct g_consumer *); +static int g_part_mbr_resize(struct g_part_table *, struct g_part_entry *, + struct g_part_parms *); static kobj_method_t g_part_mbr_methods[] = { KOBJMETHOD(g_part_add, g_part_mbr_add), @@ -85,6 +87,7 @@ static kobj_method_t g_part_mbr_methods[] = { KOBJMETHOD(g_part_dumpconf, g_part_mbr_dumpconf), KOBJMETHOD(g_part_dumpto, g_part_mbr_dumpto), KOBJMETHOD(g_part_modify, g_part_mbr_modify), + KOBJMETHOD(g_part_resize, g_part_mbr_resize), KOBJMETHOD(g_part_name, g_part_mbr_name), KOBJMETHOD(g_part_probe, g_part_mbr_probe), KOBJMETHOD(g_part_read, g_part_mbr_read), @@ -302,6 +305,31 @@ g_part_mbr_modify(struct g_part_table *basetable, return (0); } +static int +g_part_mbr_resize(struct g_part_table *basetable, + struct g_part_entry *baseentry, struct g_part_parms *gpp) +{ + struct g_part_mbr_entry *entry; + uint32_t size, sectors; + + sectors = basetable->gpt_sectors; + size = gpp->gpp_size; + + if (size < sectors) + return (EINVAL); + if (size % sectors) + size = size - (size % sectors); + if (size < sectors) + return (EINVAL); + + entry = (struct g_part_mbr_entry *)baseentry; + baseentry->gpe_end = baseentry->gpe_start + size - 1; + entry->ent.dp_size = size; + mbr_set_chs(basetable, baseentry->gpe_end, &entry->ent.dp_ecyl, + &entry->ent.dp_ehd, &entry->ent.dp_esect); + return (0); +} + static const char * g_part_mbr_name(struct g_part_table *table, struct g_part_entry *baseentry, char *buf, size_t bufsz) diff --git a/sys/geom/part/g_part_pc98.c b/sys/geom/part/g_part_pc98.c index 8857f9b98..ab83662a4 100644 --- a/sys/geom/part/g_part_pc98.c +++ b/sys/geom/part/g_part_pc98.c @@ -77,6 +77,8 @@ static int g_part_pc98_setunset(struct g_part_table *, struct g_part_entry *, static const char *g_part_pc98_type(struct g_part_table *, struct g_part_entry *, char *, size_t); static int g_part_pc98_write(struct g_part_table *, struct g_consumer *); +static int g_part_pc98_resize(struct g_part_table *, struct g_part_entry *, + struct g_part_parms *); static kobj_method_t g_part_pc98_methods[] = { KOBJMETHOD(g_part_add, g_part_pc98_add), @@ -86,6 +88,7 @@ static kobj_method_t g_part_pc98_methods[] = { KOBJMETHOD(g_part_dumpconf, g_part_pc98_dumpconf), KOBJMETHOD(g_part_dumpto, g_part_pc98_dumpto), KOBJMETHOD(g_part_modify, g_part_pc98_modify), + KOBJMETHOD(g_part_resize, g_part_pc98_resize), KOBJMETHOD(g_part_name, g_part_pc98_name), KOBJMETHOD(g_part_probe, g_part_pc98_probe), KOBJMETHOD(g_part_read, g_part_pc98_read), @@ -308,6 +311,31 @@ g_part_pc98_modify(struct g_part_table *basetable, return (0); } +static int +g_part_pc98_resize(struct g_part_table *basetable, + struct g_part_entry *baseentry, struct g_part_parms *gpp) +{ + struct g_part_pc98_entry *entry; + uint32_t size, cyl; + + cyl = basetable->gpt_heads * basetable->gpt_sectors; + size = gpp->gpp_size; + + if (size < cyl) + return (EINVAL); + if (size % cyl) + size = size - (size % cyl); + if (size < cyl) + return (EINVAL); + + entry = (struct g_part_pc98_entry *)baseentry; + baseentry->gpe_end = baseentry->gpe_start + size - 1; + pc98_set_chs(basetable, baseentry->gpe_end, &entry->ent.dp_ecyl, + &entry->ent.dp_ehd, &entry->ent.dp_esect); + + return (0); +} + static const char * g_part_pc98_name(struct g_part_table *table, struct g_part_entry *baseentry, char *buf, size_t bufsz) diff --git a/sys/geom/part/g_part_vtoc8.c b/sys/geom/part/g_part_vtoc8.c index 0f9c283c2..e8a77a4bd 100644 --- a/sys/geom/part/g_part_vtoc8.c +++ b/sys/geom/part/g_part_vtoc8.c @@ -67,6 +67,8 @@ static int g_part_vtoc8_read(struct g_part_table *, struct g_consumer *); static const char *g_part_vtoc8_type(struct g_part_table *, struct g_part_entry *, char *, size_t); static int g_part_vtoc8_write(struct g_part_table *, struct g_consumer *); +static int g_part_vtoc8_resize(struct g_part_table *, struct g_part_entry *, + struct g_part_parms *); static kobj_method_t g_part_vtoc8_methods[] = { KOBJMETHOD(g_part_add, g_part_vtoc8_add), @@ -75,6 +77,7 @@ static kobj_method_t g_part_vtoc8_methods[] = { KOBJMETHOD(g_part_dumpconf, g_part_vtoc8_dumpconf), KOBJMETHOD(g_part_dumpto, g_part_vtoc8_dumpto), KOBJMETHOD(g_part_modify, g_part_vtoc8_modify), + KOBJMETHOD(g_part_resize, g_part_vtoc8_resize), KOBJMETHOD(g_part_name, g_part_vtoc8_name), KOBJMETHOD(g_part_probe, g_part_vtoc8_probe), KOBJMETHOD(g_part_read, g_part_vtoc8_read), @@ -296,6 +299,26 @@ g_part_vtoc8_modify(struct g_part_table *basetable, return (0); } +static int +g_part_vtoc8_resize(struct g_part_table *basetable, + struct g_part_entry *entry, struct g_part_parms *gpp) +{ + struct g_part_vtoc8_table *table; + uint64_t size; + + table = (struct g_part_vtoc8_table *)basetable; + size = gpp->gpp_size; + if (size % table->secpercyl) + size = size - (size % table->secpercyl); + if (size < table->secpercyl) + return (EINVAL); + + entry->gpe_end = entry->gpe_start + size - 1; + be32enc(&table->vtoc.map[entry->gpe_index - 1].nblks, size); + + return (0); +} + static const char * g_part_vtoc8_name(struct g_part_table *table, struct g_part_entry *baseentry, char *buf, size_t bufsz) -- 2.45.0