[patch] resize and recover support for GPART GPT scheme (was: Re: OCE and GPT)

Andrey V. Elsukov bu7cher at yandex.ru
Thu Apr 22 11:41:46 UTC 2010


On 21.04.2010 17:59, Andriy Gapon wrote:
> I also think that this recovery mechanism is needed.
> In short:
> recover - re-create secondary table based on primary table
> reinit - relocate secondary table to a new position and update offsets in both
> tables accordingly

I implemented 'recover' verb. I changed detection algoritm in GPT scheme.
Now when primary GPT header is valid reading of second header will be
done from alternateLBA offset (which read from GPT header).
When primary header is invalid reading of second header will be from the
last medium's LBA.

And now the following scenario works:
====================================================================
# dd if=/dev/zero of=./d.img bs=1m count=100
100+0 records in
100+0 records out
104857600 bytes transferred in 2.895854 secs (36209560 bytes/sec)
# mdconfig -f d.img
md0
# gpart create -s gpt md0
md0 created
# gpart add -t freebsd-zfs md0
md0p1 added
# gpart show md0
=>    34  204733  md0  GPT  (100M)
       34  204733    1  freebsd-zfs  (100M)

# mdconfig -du 0
# dd if=/dev/zero of=./d.img bs=1m count=50 seek=100
50+0 records in
50+0 records out
52428800 bytes transferred in 1.175911 secs (44585689 bytes/sec)
# ls -lh d.img
-rw-r--r--  1 root  wheel   150M 22 апр 14:56 d.img
# mdconfig -f d.img
md0
# dmesg | tail -2
GEOM: md0: the secondary GPT table is corrupt or invalid.
GEOM: md0: using the primary only -- recovery suggested.
# gpart show md0
=>    34  204733  md0  GPT  (150M)
       34  204733    1  freebsd-zfs  (100M)

# gpart recover md0
md0 recovered
# gpart show md0
=>    34  307133  md0  GPT  (150M)
       34  204733    1  freebsd-zfs  (100M)
   204767  102400       - free -  (50M)

# gpart resize -i 1 md0
md0p1 resized
# gpart show md0
=>    34  307133  md0  GPT  (150M)
       34  307133    1  freebsd-zfs  (150M)
====================================================================

There are several things that can be do where i need suggestions.
1. What code should do when user doing `gpart recover` for scheme
that doesn't need recovering?
2. Probably there are needed some checks before changing metadata in
g_part_gpt_recover method.

So, patch attached and comments are welcome.

-- 
WBR, Andrey V. Elsukov
-------------- next part --------------
Index: gpart.8
===================================================================
--- gpart.8	(revision 205576)
+++ gpart.8	(working copy)
@@ -120,6 +120,18 @@ utility:
 .Op Fl t Ar type
 .Op Fl f Ar flags
 .Ar geom
+.\" ==== RECOVER ====
+.Nm
+.Cm recover
+.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
@@ -325,6 +337,45 @@ See the section entitled
 below for a discussion
 about its use.
 .El
+.\" ==== RECOVER ====
+.It Cm recover
+Recover a corrupted metadata on geom
+.Ar geom .
+Currently recovering implemented only for GPT scheme.
+.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
+.\" ==== 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.
Index: geom_part.c
===================================================================
--- geom_part.c	(revision 205576)
+++ geom_part.c	(working copy)
@@ -112,6 +112,18 @@ struct g_command PUBSYM(class_commands)[] = {
 		G_OPT_SENTINEL },
 	  "geom", NULL
 	},
+	{ "recover", 0, gpart_issue, {
+		{ 'f', "flags", flags, G_TYPE_STRING },
+		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
+	},
 	{ "set", 0, gpart_issue, {
 		{ 'a', "attrib", NULL, G_TYPE_STRING },
 		{ 'i', index_param, NULL, G_TYPE_ASCNUM },
@@ -243,6 +255,99 @@ fmtattrib(struct gprovider *pp)
 }
 
 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)
 {
 	struct gmesh mesh;
@@ -257,6 +362,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);
 
Index: g_part_pc98.c
===================================================================
--- g_part_pc98.c	(revision 204945)
+++ g_part_pc98.c	(working copy)
@@ -77,6 +77,8 @@ static int g_part_pc98_setunset(struct g_part_tabl
 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)
Index: g_part_vtoc8.c
===================================================================
--- g_part_vtoc8.c	(revision 204945)
+++ g_part_vtoc8.c	(working copy)
@@ -67,6 +67,8 @@ static int g_part_vtoc8_read(struct g_part_table *
 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),
@@ -294,6 +297,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)
Index: g_part_bsd.c
===================================================================
--- g_part_bsd.c	(revision 204945)
+++ g_part_bsd.c	(working copy)
@@ -73,6 +73,8 @@ static int g_part_bsd_read(struct g_part_table *,
 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),
@@ -288,6 +291,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)
Index: g_part_if.m
===================================================================
--- g_part_if.m	(revision 204945)
+++ g_part_if.m	(working copy)
@@ -58,6 +58,20 @@ CODE {
 	{
 		return (0);
 	}
+
+	static int
+	default_recover(struct g_part_table *t __unused,
+	    struct g_consumer *c __unused)
+	{
+		return (ENOSYS);
+	}
+
+	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.
@@ -149,6 +163,19 @@ METHOD int read {
 	struct g_consumer *cp;
 };
 
+# recover() - scheme specific processing for the recover verb.
+METHOD int recover {
+	struct g_part_table *table;
+	struct g_consumer *cp;
+} DEFAULT default_recover;
+
+# 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;
+
 # setunset() - set or unset partition entry attributes.
 METHOD int setunset {
 	struct g_part_table *table;
Index: g_part_gpt.c
===================================================================
--- g_part_gpt.c	(revision 204945)
+++ g_part_gpt.c	(working copy)
@@ -100,6 +100,9 @@ static const char *g_part_gpt_name(struct g_part_t
     char *, size_t);
 static int g_part_gpt_probe(struct g_part_table *, struct g_consumer *);
 static int g_part_gpt_read(struct g_part_table *, struct g_consumer *);
+static int g_part_gpt_recover(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 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 *);
@@ -115,6 +118,8 @@ static kobj_method_t g_part_gpt_methods[] = {
 	KOBJMETHOD(g_part_name,		g_part_gpt_name),
 	KOBJMETHOD(g_part_probe,	g_part_gpt_probe),
 	KOBJMETHOD(g_part_read,		g_part_gpt_read),
+	KOBJMETHOD(g_part_recover,	g_part_gpt_recover),
+	KOBJMETHOD(g_part_resize,	g_part_gpt_resize),
 	KOBJMETHOD(g_part_type,		g_part_gpt_type),
 	KOBJMETHOD(g_part_write,	g_part_gpt_write),
 	{ 0, 0 }
@@ -164,7 +169,7 @@ static struct uuid gpt_uuid_unused = GPT_ENT_TYPE_
 
 static struct g_part_uuid_alias {
 	struct uuid *uuid;
-	int alias;		
+	int alias;
 } gpt_uuid_alias_match[] = {
 	{ &gpt_uuid_apple_boot,		G_PART_ALIAS_APPLE_BOOT },
 	{ &gpt_uuid_apple_hfs,		G_PART_ALIAS_APPLE_HFS },
@@ -211,7 +216,14 @@ gpt_read_hdr(struct g_part_gpt_table *table, struc
 
 	pp = cp->provider;
 	last = (pp->mediasize / pp->sectorsize) - 1;
-	table->lba[elt] = (elt == GPT_ELT_PRIHDR) ? 1 : last;
+	if (elt == GPT_ELT_SECHDR) {
+		/* When the primary header is valid look for secondary
+		 * header at AlternateLBA. Otherwise - at last medium's LBA.
+		 */
+		if (table->state[GPT_ELT_PRIHDR] != GPT_STATE_OK)
+			table->lba[elt] = last;
+	} else
+		table->lba[elt] = 1;
 	table->state[elt] = GPT_STATE_MISSING;
 	buf = g_read_data(cp, table->lba[elt] * pp->sectorsize, pp->sectorsize,
 	    &error);
@@ -238,12 +250,15 @@ gpt_read_hdr(struct g_part_gpt_table *table, struc
 
 	table->state[elt] = GPT_STATE_INVALID;
 	hdr->hdr_revision = le32toh(buf->hdr_revision);
-	if (hdr->hdr_revision < 0x00010000)
+	if (hdr->hdr_revision < GPT_HDR_REVISION)
 		goto fail;
 	hdr->hdr_lba_self = le64toh(buf->hdr_lba_self);
 	if (hdr->hdr_lba_self != table->lba[elt])
 		goto fail;
 	hdr->hdr_lba_alt = le64toh(buf->hdr_lba_alt);
+	if (hdr->hdr_lba_alt == hdr->hdr_lba_self ||
+	    hdr->hdr_lba_alt > last)
+		goto fail;
 
 	/* Check the managed area. */
 	hdr->hdr_lba_start = le64toh(buf->hdr_lba_start);
@@ -277,6 +292,10 @@ gpt_read_hdr(struct g_part_gpt_table *table, struc
 	le_uuid_dec(&buf->hdr_uuid, &hdr->hdr_uuid);
 	hdr->hdr_crc_table = le32toh(buf->hdr_crc_table);
 
+	/* save LBA for secondary header */
+	if (elt == GPT_ELT_PRIHDR)
+		table->lba[GPT_ELT_SECHDR] = hdr->hdr_lba_alt;
+
 	g_free(buf);
 	return (hdr);
 
@@ -550,6 +569,50 @@ 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 int
+g_part_gpt_recover(struct g_part_table *basetable, struct g_consumer *cp)
+{
+	struct g_part_gpt_table *table;
+	struct g_provider *pp;
+	size_t tblsz;
+	quad_t last;
+
+	pp = cp->provider;
+	table = (struct g_part_gpt_table *)basetable;
+	last = (pp->mediasize / pp->sectorsize) - 1;
+	tblsz = (basetable->gpt_entries * sizeof(struct gpt_ent) +
+	    pp->sectorsize - 1) / pp->sectorsize;
+
+	table->lba[GPT_ELT_PRIHDR] = 1;
+	table->lba[GPT_ELT_PRITBL] = 2;
+	table->lba[GPT_ELT_SECHDR] = last;
+	table->lba[GPT_ELT_SECTBL] = last - tblsz;
+	table->state[GPT_ELT_PRIHDR] = GPT_STATE_OK;
+	table->state[GPT_ELT_PRITBL] = GPT_STATE_OK;
+	table->state[GPT_ELT_SECHDR] = GPT_STATE_OK;
+	table->state[GPT_ELT_SECTBL] = GPT_STATE_OK;
+
+	table->hdr->hdr_lba_start = 2 + tblsz;
+	table->hdr->hdr_lba_end = last - tblsz - 1;
+	basetable->gpt_first = table->hdr->hdr_lba_start;
+	basetable->gpt_last = table->hdr->hdr_lba_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)
@@ -630,10 +693,12 @@ g_part_gpt_read(struct g_part_table *basetable, st
 	struct g_part_gpt_table *table;
 	struct g_part_gpt_entry *entry;
 	u_char *buf;
+	quad_t last;
 	int error, index;
 
 	table = (struct g_part_gpt_table *)basetable;
 	pp = cp->provider;
+	last = (pp->mediasize / pp->sectorsize) - 1;
 
 	/* Read the PMBR */
 	buf = g_read_data(cp, 0, pp->sectorsize, &error);
@@ -703,7 +768,8 @@ g_part_gpt_read(struct g_part_table *basetable, st
 		if (pritbl != NULL)
 			g_free(pritbl);
 	} else {
-		if (table->state[GPT_ELT_SECTBL] != GPT_STATE_OK) {
+		if (table->state[GPT_ELT_SECTBL] != GPT_STATE_OK ||
+		    table->lba[GPT_ELT_SECHDR] != last) {
 			printf("GEOM: %s: the secondary GPT table is corrupt "
 			    "or invalid.\n", pp->name);
 			printf("GEOM: %s: using the primary only -- recovery "
Index: g_part_apm.c
===================================================================
--- g_part_apm.c	(revision 204945)
+++ g_part_apm.c	(working copy)
@@ -74,6 +74,8 @@ static int g_part_apm_read(struct g_part_table *,
 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),
@@ -318,6 +321,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)
Index: g_part.c
===================================================================
--- g_part.c	(revision 204945)
+++ g_part.c	(working copy)
@@ -959,22 +959,124 @@ g_part_ctl_move(struct gctl_req *req, struct g_par
 {
 	gctl_error(req, "%d verb 'move'", ENOSYS);
 	return (ENOSYS);
-} 
+}
 
 static int
 g_part_ctl_recover(struct gctl_req *req, struct g_part_parms *gpp)
 {
-	gctl_error(req, "%d verb 'recover'", ENOSYS);
-	return (ENOSYS);
+	struct g_geom *gp;
+	struct g_consumer *cp;
+	struct g_part_table *table;
+	struct sbuf *sb;
+	int error;
+
+	gp = gpp->gpp_geom;
+	G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
+	g_topology_assert();
+
+	cp = LIST_FIRST(&gp->consumer);
+	table = gp->softc;
+
+	error = G_PART_RECOVER(table, cp);
+	if (error) {
+		gctl_error(req, "%d", error);
+		return (error);
+	}
+
+	/* Provide feedback if so requested. */
+	if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
+		sb = sbuf_new_auto();
+		sbuf_printf(sb, "%s recovered\n", gp->name);
+		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_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,
     unsigned int set)
@@ -1194,7 +1296,8 @@ g_part_ctlreq(struct gctl_req *req, struct g_class
 			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':
Index: g_part_mbr.c
===================================================================
--- g_part_mbr.c	(revision 204945)
+++ g_part_mbr.c	(working copy)
@@ -76,6 +76,8 @@ static int g_part_mbr_setunset(struct g_part_table
 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)


More information about the freebsd-geom mailing list