aboutsummaryrefslogtreecommitdiff
path: root/sys/geom/eli
diff options
context:
space:
mode:
authorPawel Jakub Dawidek <pjd@FreeBSD.org>2015-08-08 09:51:38 +0000
committerPawel Jakub Dawidek <pjd@FreeBSD.org>2015-08-08 09:51:38 +0000
commit46e34470269793e5da58b001897e830a0f5de825 (patch)
tree3d81b92c9c23fdf0dff04d772f0a3ac5e68d5526 /sys/geom/eli
parent079672cb07e224e283a5d7d032e5b7090e1f4cc9 (diff)
downloadsrc-46e34470269793e5da58b001897e830a0f5de825.tar.gz
src-46e34470269793e5da58b001897e830a0f5de825.zip
Enable BIO_DELETE passthru in GELI, so TRIM/UNMAP can work as expected when
GELI is used on a SSD or inside virtual machine, so that guest can tell host that it is no longer using some of the storage. Enabling BIO_DELETE passthru comes with a small security consequence - an attacker can tell how much space is being really used on encrypted device and has less data no analyse then. This is why the -T option can be given to the init subcommand to turn off this behaviour and -t/T options for the configure subcommand can be used to adjust this setting later. PR: 198863 Submitted by: Matthew D. Fuller fullermd at over-yonder dot net This commit also includes a fix from Fabian Keil freebsd-listen at fabiankeil.de for 'configure' on onetime providers which is not strictly related, but is entangled in the same code, so would cause conflicts if separated out.
Notes
Notes: svn path=/head/; revision=286444
Diffstat (limited to 'sys/geom/eli')
-rw-r--r--sys/geom/eli/g_eli.c13
-rw-r--r--sys/geom/eli/g_eli.h2
-rw-r--r--sys/geom/eli/g_eli_ctl.c112
3 files changed, 92 insertions, 35 deletions
diff --git a/sys/geom/eli/g_eli.c b/sys/geom/eli/g_eli.c
index a09472052b48..14fc17bc2323 100644
--- a/sys/geom/eli/g_eli.c
+++ b/sys/geom/eli/g_eli.c
@@ -312,10 +312,15 @@ g_eli_start(struct bio *bp)
break;
case BIO_DELETE:
/*
- * We could eventually support BIO_DELETE request.
- * It could be done by overwritting requested sector with
- * random data g_eli_overwrites number of times.
+ * If the user hasn't set the NODELETE flag, we just pass
+ * it down the stack and let the layers beneath us do (or
+ * not) whatever they do with it. If they have, we
+ * reject it. A possible extension would be an
+ * additional flag to take it as a hint to shred the data
+ * with [multiple?] overwrites.
*/
+ if (!(sc->sc_flags & G_ELI_FLAG_NODELETE))
+ break;
default:
g_io_deliver(bp, EOPNOTSUPP);
return;
@@ -342,6 +347,7 @@ g_eli_start(struct bio *bp)
break;
case BIO_GETATTR:
case BIO_FLUSH:
+ case BIO_DELETE:
cbp->bio_done = g_std_done;
cp = LIST_FIRST(&sc->sc_geom->consumer);
cbp->bio_to = cp->provider;
@@ -1255,6 +1261,7 @@ g_eli_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
ADD_FLAG(G_ELI_FLAG_WOPEN, "W-OPEN");
ADD_FLAG(G_ELI_FLAG_DESTROY, "DESTROY");
ADD_FLAG(G_ELI_FLAG_RO, "READ-ONLY");
+ ADD_FLAG(G_ELI_FLAG_NODELETE, "NODELETE");
#undef ADD_FLAG
}
sbuf_printf(sb, "</Flags>\n");
diff --git a/sys/geom/eli/g_eli.h b/sys/geom/eli/g_eli.h
index 835a1b316326..36ef5551a191 100644
--- a/sys/geom/eli/g_eli.h
+++ b/sys/geom/eli/g_eli.h
@@ -94,6 +94,8 @@
#define G_ELI_FLAG_AUTH 0x00000010
/* Provider is read-only, we should deny all write attempts. */
#define G_ELI_FLAG_RO 0x00000020
+/* Don't pass through BIO_DELETE requests. */
+#define G_ELI_FLAG_NODELETE 0x00000040
/* RUNTIME FLAGS. */
/* Provider was open for writing. */
#define G_ELI_FLAG_WOPEN 0x00010000
diff --git a/sys/geom/eli/g_eli_ctl.c b/sys/geom/eli/g_eli_ctl.c
index 59aff9b684bd..9de7ec3d55cb 100644
--- a/sys/geom/eli/g_eli_ctl.c
+++ b/sys/geom/eli/g_eli_ctl.c
@@ -236,7 +236,7 @@ g_eli_ctl_onetime(struct gctl_req *req, struct g_class *mp)
const char *name;
intmax_t *keylen, *sectorsize;
u_char mkey[G_ELI_DATAIVKEYLEN];
- int *nargs, *detach;
+ int *nargs, *detach, *notrim;
g_topology_assert();
bzero(&md, sizeof(md));
@@ -251,17 +251,16 @@ g_eli_ctl_onetime(struct gctl_req *req, struct g_class *mp)
return;
}
- detach = gctl_get_paraml(req, "detach", sizeof(*detach));
- if (detach == NULL) {
- gctl_error(req, "No '%s' argument.", "detach");
- return;
- }
-
strlcpy(md.md_magic, G_ELI_MAGIC, sizeof(md.md_magic));
md.md_version = G_ELI_VERSION;
md.md_flags |= G_ELI_FLAG_ONETIME;
- if (*detach)
+
+ detach = gctl_get_paraml(req, "detach", sizeof(*detach));
+ if (detach != NULL && *detach)
md.md_flags |= G_ELI_FLAG_WO_DETACH;
+ notrim = gctl_get_paraml(req, "notrim", sizeof(*notrim));
+ if (notrim != NULL && *notrim)
+ md.md_flags |= G_ELI_FLAG_NODELETE;
md.md_ealgo = CRYPTO_ALGORITHM_MIN - 1;
name = gctl_get_asciiparam(req, "aalgo");
@@ -377,12 +376,15 @@ g_eli_ctl_configure(struct gctl_req *req, struct g_class *mp)
char param[16];
const char *prov;
u_char *sector;
- int *nargs, *boot, *noboot;
- int error;
+ int *nargs, *boot, *noboot, *trim, *notrim;
+ int zero, error, changed;
u_int i;
g_topology_assert();
+ changed = 0;
+ zero = 0;
+
nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
if (nargs == NULL) {
gctl_error(req, "No '%s' argument.", "nargs");
@@ -394,20 +396,32 @@ g_eli_ctl_configure(struct gctl_req *req, struct g_class *mp)
}
boot = gctl_get_paraml(req, "boot", sizeof(*boot));
- if (boot == NULL) {
- gctl_error(req, "No '%s' argument.", "boot");
- return;
- }
+ if (boot == NULL)
+ boot = &zero;
noboot = gctl_get_paraml(req, "noboot", sizeof(*noboot));
- if (noboot == NULL) {
- gctl_error(req, "No '%s' argument.", "noboot");
- return;
- }
+ if (noboot == NULL)
+ noboot = &zero;
if (*boot && *noboot) {
gctl_error(req, "Options -b and -B are mutually exclusive.");
return;
}
- if (!*boot && !*noboot) {
+ if (*boot || *noboot)
+ changed = 1;
+
+ trim = gctl_get_paraml(req, "trim", sizeof(*trim));
+ if (trim == NULL)
+ trim = &zero;
+ notrim = gctl_get_paraml(req, "notrim", sizeof(*notrim));
+ if (notrim == NULL)
+ notrim = &zero;
+ if (*trim && *notrim) {
+ gctl_error(req, "Options -t and -T are mutually exclusive.");
+ return;
+ }
+ if (*trim || *notrim)
+ changed = 1;
+
+ if (!changed) {
gctl_error(req, "No option given.");
return;
}
@@ -429,38 +443,72 @@ g_eli_ctl_configure(struct gctl_req *req, struct g_class *mp)
"provider %s.", prov);
continue;
}
+ if (sc->sc_flags & G_ELI_FLAG_RO) {
+ gctl_error(req, "Cannot change configuration of "
+ "read-only provider %s.", prov);
+ continue;
+ }
+
if (*boot && (sc->sc_flags & G_ELI_FLAG_BOOT)) {
G_ELI_DEBUG(1, "BOOT flag already configured for %s.",
prov);
continue;
- } else if (!*boot && !(sc->sc_flags & G_ELI_FLAG_BOOT)) {
+ } else if (*noboot && !(sc->sc_flags & G_ELI_FLAG_BOOT)) {
G_ELI_DEBUG(1, "BOOT flag not configured for %s.",
prov);
continue;
}
- if (sc->sc_flags & G_ELI_FLAG_RO) {
- gctl_error(req, "Cannot change configuration of "
- "read-only provider %s.", prov);
+
+ if (*notrim && (sc->sc_flags & G_ELI_FLAG_NODELETE)) {
+ G_ELI_DEBUG(1, "TRIM disable flag already configured for %s.",
+ prov);
continue;
- }
- cp = LIST_FIRST(&sc->sc_geom->consumer);
- pp = cp->provider;
- error = g_eli_read_metadata(mp, pp, &md);
- if (error != 0) {
- gctl_error(req,
- "Cannot read metadata from %s (error=%d).",
- prov, error);
+ } else if (*trim && !(sc->sc_flags & G_ELI_FLAG_NODELETE)) {
+ G_ELI_DEBUG(1, "TRIM disable flag not configured for %s.",
+ prov);
continue;
}
+ if (!(sc->sc_flags & G_ELI_FLAG_ONETIME)) {
+ /*
+ * ONETIME providers don't write metadata to
+ * disk, so don't try reading it. This means
+ * we're bit-flipping uninitialized memory in md
+ * below, but that's OK; we don't do anything
+ * with it later.
+ */
+ cp = LIST_FIRST(&sc->sc_geom->consumer);
+ pp = cp->provider;
+ error = g_eli_read_metadata(mp, pp, &md);
+ if (error != 0) {
+ gctl_error(req,
+ "Cannot read metadata from %s (error=%d).",
+ prov, error);
+ continue;
+ }
+ }
+
if (*boot) {
md.md_flags |= G_ELI_FLAG_BOOT;
sc->sc_flags |= G_ELI_FLAG_BOOT;
- } else {
+ } else if (*noboot) {
md.md_flags &= ~G_ELI_FLAG_BOOT;
sc->sc_flags &= ~G_ELI_FLAG_BOOT;
}
+ if (*notrim) {
+ md.md_flags |= G_ELI_FLAG_NODELETE;
+ sc->sc_flags |= G_ELI_FLAG_NODELETE;
+ } else if (*trim) {
+ md.md_flags &= ~G_ELI_FLAG_NODELETE;
+ sc->sc_flags &= ~G_ELI_FLAG_NODELETE;
+ }
+
+ if (sc->sc_flags & G_ELI_FLAG_ONETIME) {
+ /* There's no metadata on disk so we are done here. */
+ continue;
+ }
+
sector = malloc(pp->sectorsize, M_ELI, M_WAITOK | M_ZERO);
eli_metadata_encode(&md, sector);
error = g_write_data(cp, pp->mediasize - pp->sectorsize, sector,