summaryrefslogtreecommitdiffstats
path: root/sbin
diff options
context:
space:
mode:
authorbrian <brian@FreeBSD.org>2010-09-20 22:04:59 +0000
committerbrian <brian@FreeBSD.org>2010-09-20 22:04:59 +0000
commitee15e08fc2db4b1f70acb1e9fc0a86db93e24420 (patch)
tree0912f93b2421b4ac8a38168ee72c472769b87e3b /sbin
parent6c139814a74b838866e8db1e0024d37f8067b685 (diff)
downloadFreeBSD-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')
-rw-r--r--sbin/geom/class/eli/geli.848
-rw-r--r--sbin/geom/class/eli/geom_eli.c144
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;
OpenPOWER on IntegriCloud