diff options
author | brian <brian@FreeBSD.org> | 2010-09-20 22:04:59 +0000 |
---|---|---|
committer | brian <brian@FreeBSD.org> | 2010-09-20 22:04:59 +0000 |
commit | ee15e08fc2db4b1f70acb1e9fc0a86db93e24420 (patch) | |
tree | 0912f93b2421b4ac8a38168ee72c472769b87e3b /sbin/geom/class | |
parent | 6c139814a74b838866e8db1e0024d37f8067b685 (diff) | |
download | FreeBSD-src-ee15e08fc2db4b1f70acb1e9fc0a86db93e24420.zip FreeBSD-src-ee15e08fc2db4b1f70acb1e9fc0a86db93e24420.tar.gz |
Add a geli resize subcommand to resize encrypted filesystems prior
to growing the filesystem.
Refuse to attach providers where the metadata provider size is
wrong. This makes post-boot attaches behave consistently with
pre-boot attaches. Also refuse to restore metadata to a provider
of the wrong size without the new -f switch. The new -f switch
forces the metadata restoration despite the provider size, and
updates the provider size in the restored metadata to the correct
value.
Helped by: pjd
Reviewed by: pjd
Diffstat (limited to 'sbin/geom/class')
-rw-r--r-- | sbin/geom/class/eli/geli.8 | 48 | ||||
-rw-r--r-- | sbin/geom/class/eli/geom_eli.c | 144 |
2 files changed, 187 insertions, 5 deletions
diff --git a/sbin/geom/class/eli/geli.8 b/sbin/geom/class/eli/geli.8 index e61bbe1..581e103 100644 --- a/sbin/geom/class/eli/geli.8 +++ b/sbin/geom/class/eli/geli.8 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd August 29, 2008 +.Dd September 20, 2010 .Dt GELI 8 .Os .Sh NAME @@ -111,10 +111,15 @@ utility: .Ar file .Nm .Cm restore -.Op Fl v +.Op Fl fv .Ar file .Ar prov .Nm +.Cm resize +.Op Fl v +.Fl s Ar oldsize +.Ar prov +.Nm .Cm clear .Op Fl v .Ar prov ... @@ -464,6 +469,34 @@ If specified, all currently attached providers will be killed. Backup metadata from the given provider to the given file. .It Cm restore Restore metadata from the given file to the given provider. +.Bl -tag -width ".Fl f" +.It Fl f +Metadata contains the size of the provider to ensure that the correct +partition or slice is attached. +If an attempt is made to restore metadata to a provider that has a different +size, +.Nm +will refuse to restore the data unless the +.Fl f +switch is used. +If the partition or slice has been grown, the +.Cm resize +subcommand should be used rather than attempting to relocate the metadata +through +.Cm backup +and +.Cm restore . +.El +.It Cm resize +Inform +.Nm +that the provider has been resized. +The old metadata block is relocated to the correct position at the end of the +provider and the provider size is updated. +.Bl -tag -width ".Fl s Ar oldsize" +.It Fl s Ar oldsize +The size of the provider before it was resized. +.El .It Cm clear Clear metadata from the given providers. .It Cm dump @@ -665,6 +698,17 @@ geli: Cannot read metadata from /dev/da0: Invalid argument. # geli attach /dev/da0 Enter passphrase: .Ed +.Pp +If an encrypted filesystem is extended, it is necessary to relocate and +update the metadata: +.Bd -literal -offset indent +# gpart create -s GPT ada0 +# gpart add -s 1g -t freebsd-ufs -i 1 ada0 +# geli init -K keyfile -P ada0p1 +# gpart resize -s 2g -i 1 ada0 +# geli resize -s 1g ada0p1 +# geli attach -k keyfile -p ada0p1 +.Ed .Sh DATA AUTHENTICATION .Nm can verify data integrity when an authentication algorithm is specified. diff --git a/sbin/geom/class/eli/geom_eli.c b/sbin/geom/class/eli/geom_eli.c index 59c6be7..9c01652 100644 --- a/sbin/geom/class/eli/geom_eli.c +++ b/sbin/geom/class/eli/geom_eli.c @@ -66,6 +66,7 @@ static void eli_delkey(struct gctl_req *req); static void eli_kill(struct gctl_req *req); static void eli_backup(struct gctl_req *req); static void eli_restore(struct gctl_req *req); +static void eli_resize(struct gctl_req *req); static void eli_clear(struct gctl_req *req); static void eli_dump(struct gctl_req *req); @@ -86,7 +87,8 @@ static int eli_backup_create(struct gctl_req *req, const char *prov, * delkey [-afv] [-n keyno] prov * kill [-av] [prov ...] * backup [-v] prov file - * restore [-v] file prov + * restore [-fv] file prov + * resize [-v] -s oldsize prov * clear [-v] prov ... * dump [-v] prov ... */ @@ -197,8 +199,19 @@ struct g_command class_commands[] = { { "backup", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, "[-v] prov file" }, - { "restore", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, - "[-v] file prov" + { "restore", G_FLAG_VERBOSE, eli_main, + { + { 'f', "force", NULL, G_TYPE_BOOL }, + G_OPT_SENTINEL + }, + "[-fv] file prov" + }, + { "resize", G_FLAG_VERBOSE, eli_main, + { + { 's', "oldsize", NULL, G_TYPE_NUMBER }, + G_OPT_SENTINEL + }, + "[-v] -s oldsize prov" }, { "clear", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, "[-v] prov ..." @@ -264,6 +277,8 @@ eli_main(struct gctl_req *req, unsigned flags) eli_backup(req); else if (strcmp(name, "restore") == 0) eli_restore(req); + else if (strcmp(name, "resize") == 0) + eli_resize(req); else if (strcmp(name, "dump") == 0) eli_dump(req); else if (strcmp(name, "clear") == 0) @@ -683,6 +698,7 @@ eli_attach(struct gctl_req *req) struct g_eli_metadata md; unsigned char key[G_ELI_USERKEYLEN]; const char *prov; + off_t mediasize; int nargs; nargs = gctl_get_int(req, "nargs"); @@ -695,6 +711,12 @@ eli_attach(struct gctl_req *req) if (eli_metadata_read(req, prov, &md) == -1) return; + mediasize = g_get_mediasize(prov); + if (md.md_provsize != (uint64_t)mediasize) { + gctl_error(req, "Provider size mismatch."); + return; + } + if (eli_genkey(req, &md, key, 0) == NULL) { bzero(key, sizeof(key)); return; @@ -1212,6 +1234,17 @@ eli_restore(struct gctl_req *req) gctl_error(req, "MD5 hash mismatch: not a geli backup file?"); goto out; } + /* Check if the provider size has changed since we did the backup. */ + if (md.md_provsize != (uint64_t)mediasize) { + if (gctl_get_int(req, "force")) { + md.md_provsize = mediasize; + eli_metadata_encode(&md, sector); + } else { + gctl_error(req, "Provider size mismatch: " + "wrong backup file?"); + goto out; + } + } /* Write metadata from the provider. */ if (pwrite(provfd, sector, secsize, mediasize - secsize) != (ssize_t)secsize) { @@ -1230,6 +1263,111 @@ out: } static void +eli_resize(struct gctl_req *req) +{ + struct g_eli_metadata md; + const char *prov; + unsigned char *sector; + unsigned secsize; + off_t mediasize, oldsize; + int nargs, provfd; + + nargs = gctl_get_int(req, "nargs"); + if (nargs != 1) { + gctl_error(req, "Invalid number of arguments."); + return; + } + prov = gctl_get_ascii(req, "arg0"); + + provfd = -1; + sector = NULL; + secsize = 0; + + provfd = open(prov, O_RDWR); + if (provfd == -1 && errno == ENOENT && prov[0] != '/') { + char devprov[MAXPATHLEN]; + + snprintf(devprov, sizeof(devprov), "%s%s", _PATH_DEV, prov); + provfd = open(devprov, O_RDWR); + } + if (provfd == -1) { + gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno)); + goto out; + } + + mediasize = g_get_mediasize(prov); + secsize = g_get_sectorsize(prov); + if (mediasize == 0 || secsize == 0) { + gctl_error(req, "Cannot get information about %s: %s.", prov, + strerror(errno)); + goto out; + } + + sector = malloc(secsize); + if (sector == NULL) { + gctl_error(req, "Cannot allocate memory."); + goto out; + } + + oldsize = gctl_get_intmax(req, "oldsize"); + if (oldsize < 0 || oldsize > mediasize) { + gctl_error(req, "Invalid oldsize: Out of range."); + goto out; + } + + /* Read metadata from the 'oldsize' offset. */ + if (pread(provfd, sector, secsize, oldsize - secsize) != + (ssize_t)secsize) { + gctl_error(req, "Cannot read old metadata: %s.", + strerror(errno)); + goto out; + } + + /* Check if this sector contains geli metadata. */ + if (eli_metadata_decode(sector, &md) != 0) { + gctl_error(req, "MD5 hash mismatch: no metadata for oldsize."); + goto out; + } + + /* + * If the old metadata doesn't have a correct provider size, refuse + * to resize. + */ + if (md.md_provsize != (uint64_t)oldsize) { + gctl_error(req, "Provider size mismatch at oldsize."); + goto out; + } + + /* + * Update the old metadata with the current provider size and write + * it back to the correct place on the provider. + */ + md.md_provsize = mediasize; + eli_metadata_encode(&md, sector); + if (pwrite(provfd, sector, secsize, mediasize - secsize) != + (ssize_t)secsize) { + gctl_error(req, "Cannot write metadata: %s.", strerror(errno)); + goto out; + } + + /* Now trash the old metadata. */ + arc4rand(sector, secsize); + if (pwrite(provfd, sector, secsize, oldsize - secsize) != + (ssize_t)secsize) { + gctl_error(req, "Failed to clobber old metadata: %s.", + strerror(errno)); + goto out; + } +out: + if (provfd > 0) + close(provfd); + if (sector != NULL) { + bzero(sector, secsize); + free(sector); + } +} + +static void eli_clear(struct gctl_req *req) { const char *name; |