From 2f07cdf871b8fd7081cfbe0d3c965650ea135e12 Mon Sep 17 00:00:00 2001 From: Pawel Jakub Dawidek Date: Wed, 3 Apr 2019 23:57:37 +0000 Subject: [PATCH] Implement automatic online expansion of GELI providers - if the underlying provider grows, GELI will expand automatically and will move the metadata to the new location of the last sector. This functionality is turned on by default. It can be turned off with the -R flag, but it is not recommended - if the underlying provider grows and automatic expansion is turned off, it won't be possible to attach this provider again, as the metadata is no longer located in the last sector. If the automatic expansion is turned off and the underlying provider grows, GELI will only log a message with the previous size of the provider, so recovery can be easier. Obtained from: Fudo Security --- lib/geom/eli/geli.8 | 34 ++++++++++-- lib/geom/eli/geom_eli.c | 59 +++++++++++++++----- sys/geom/eli/g_eli.c | 98 +++++++++++++++++++++++++++++++--- sys/geom/eli/g_eli.h | 35 ++++++++---- sys/geom/eli/g_eli_ctl.c | 42 ++++++++++++++- sys/geom/eli/g_eli_key_cache.c | 48 ++++++++++++++++- 6 files changed, 279 insertions(+), 37 deletions(-) diff --git a/lib/geom/eli/geli.8 b/lib/geom/eli/geli.8 index cdbb1586f0f..876f4668786 100644 --- a/lib/geom/eli/geli.8 +++ b/lib/geom/eli/geli.8 @@ -1,4 +1,4 @@ -.\" Copyright (c) 2005-2011 Pawel Jakub Dawidek +.\" Copyright (c) 2005-2019 Pawel Jakub Dawidek .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd July 24, 2018 +.Dd April 3, 2019 .Dt GELI 8 .Os .Sh NAME @@ -51,7 +51,7 @@ utility: .Pp .Nm .Cm init -.Op Fl bdgPTv +.Op Fl bdgPRTv .Op Fl a Ar aalgo .Op Fl B Ar backupfile .Op Fl e Ar ealgo @@ -81,7 +81,7 @@ utility: .Cm detach .Nm .Cm onetime -.Op Fl dT +.Op Fl dRT .Op Fl a Ar aalgo .Op Fl e Ar ealgo .Op Fl l Ar keylen @@ -89,7 +89,7 @@ utility: .Ar prov .Nm .Cm configure -.Op Fl bBdDgGtT +.Op Fl bBdDgGrRtT .Ar prov ... .Nm .Cm setkey @@ -375,6 +375,18 @@ Change decrypted provider's sector size. Increasing the sector size allows increased performance, because encryption/decryption which requires an initialization vector is done per sector; fewer sectors means less computational work. +.It Fl R +Turn off automatic expansion. +By default, if the underlying provider grows, the encrypted provider will +grow automatically too. +The metadata will be moved to the new location. +If automatic expansion if turned off and the underlying provider changes +size, attaching encrypted provider will no longer be possible as the metadata +will no longer be located in the last sector. +In this case +.Nm GELI +will only log the previous size of the underlying provider, so metadata can +be found easier, if resize was done by mistake. .It Fl T Don't pass through .Dv BIO_DELETE @@ -506,6 +518,11 @@ Change decrypted provider's sector size. For more information, see the description of the .Cm init subcommand. +.It Fl R +Turn off automatic expansion. +For more information, see the description of the +.Cm init +subcommand. .It Fl T Disable TRIM/UNMAP passthru. For more information, see the description of the @@ -540,6 +557,13 @@ The boot loader prompts for the passphrase and loads from the encrypted partition. .It Fl G Deactivate booting from this encrypted root partition. +.It Fl r +Turn on automatic expansion. +For more information, see the description of the +.Cm init +subcommand. +.It Fl R +Turn off automatic expansion. .It Fl t Enable TRIM/UNMAP passthru. For more information, see the description of the diff --git a/lib/geom/eli/geom_eli.c b/lib/geom/eli/geom_eli.c index 2da4b56c2ed..2b08bf14a18 100644 --- a/lib/geom/eli/geom_eli.c +++ b/lib/geom/eli/geom_eli.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * - * Copyright (c) 2004-2010 Pawel Jakub Dawidek + * Copyright (c) 2004-2019 Pawel Jakub Dawidek * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -91,13 +91,13 @@ static int eli_backup_create(struct gctl_req *req, const char *prov, /* * Available commands: * - * init [-bdgPTv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov ... + * init [-bdgPRTv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov ... * label - alias for 'init' * attach [-Cdprv] [-n keyno] [-j passfile] [-k keyfile] prov ... * detach [-fl] prov ... * stop - alias for 'detach' - * onetime [-dT] [-a aalgo] [-e ealgo] [-l keylen] prov - * configure [-bBgGtT] prov ... + * onetime [-dRT] [-a aalgo] [-e ealgo] [-l keylen] prov + * configure [-bBgGrRtT] prov ... * setkey [-pPv] [-n keyno] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov * delkey [-afv] [-n keyno] prov * suspend [-v] -a | prov ... @@ -124,12 +124,13 @@ struct g_command class_commands[] = { { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, { 'l', "keylen", "0", G_TYPE_NUMBER }, { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL }, + { 'R', "noautoresize", NULL, G_TYPE_BOOL }, { 's', "sectorsize", "0", G_TYPE_NUMBER }, { 'T', "notrim", NULL, G_TYPE_BOOL }, { 'V', "mdversion", "-1", G_TYPE_NUMBER }, G_OPT_SENTINEL }, - "[-bdgPTv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov ..." + "[-bdgPRTv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov ..." }, { "label", G_FLAG_VERBOSE, eli_main, { @@ -144,6 +145,7 @@ struct g_command class_commands[] = { { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, { 'l', "keylen", "0", G_TYPE_NUMBER }, { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL }, + { 'R', "noautoresize", NULL, G_TYPE_BOOL }, { 's', "sectorsize", "0", G_TYPE_NUMBER }, { 'T', "notrim", NULL, G_TYPE_BOOL }, { 'V', "mdversion", "-1", G_TYPE_NUMBER }, @@ -186,11 +188,12 @@ struct g_command class_commands[] = { { 'd', "detach", NULL, G_TYPE_BOOL }, { 'e', "ealgo", GELI_ENC_ALGO, G_TYPE_STRING }, { 'l', "keylen", "0", G_TYPE_NUMBER }, + { 'R', "noautoresize", NULL, G_TYPE_BOOL }, { 's', "sectorsize", "0", G_TYPE_NUMBER }, { 'T', "notrim", NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, - "[-dT] [-a aalgo] [-e ealgo] [-l keylen] [-s sectorsize] prov" + "[-dRT] [-a aalgo] [-e ealgo] [-l keylen] [-s sectorsize] prov" }, { "configure", G_FLAG_VERBOSE, eli_main, { @@ -200,11 +203,13 @@ struct g_command class_commands[] = { { 'D', "nodisplaypass", NULL, G_TYPE_BOOL }, { 'g', "geliboot", NULL, G_TYPE_BOOL }, { 'G', "nogeliboot", NULL, G_TYPE_BOOL }, + { 'r', "autoresize", NULL, G_TYPE_BOOL }, + { 'R', "noautoresize", NULL, G_TYPE_BOOL }, { 't', "trim", NULL, G_TYPE_BOOL }, { 'T', "notrim", NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, - "[-bBdDgGtT] prov ..." + "[-bBdDgGrRtT] prov ..." }, { "setkey", G_FLAG_VERBOSE, eli_main, { @@ -728,7 +733,7 @@ eli_init(struct gctl_req *req) version = val; } md.md_version = version; - md.md_flags = 0; + md.md_flags = G_ELI_FLAG_AUTORESIZE; if (gctl_get_int(req, "boot")) md.md_flags |= G_ELI_FLAG_BOOT; if (gctl_get_int(req, "geliboot")) @@ -737,6 +742,8 @@ eli_init(struct gctl_req *req) md.md_flags |= G_ELI_FLAG_GELIDISPLAYPASS; if (gctl_get_int(req, "notrim")) md.md_flags |= G_ELI_FLAG_NODELETE; + if (gctl_get_int(req, "noautoresize")) + md.md_flags &= ~G_ELI_FLAG_AUTORESIZE; md.md_ealgo = CRYPTO_ALGORITHM_MIN - 1; str = gctl_get_ascii(req, "aalgo"); if (*str != '\0') { @@ -1095,7 +1102,7 @@ eli_attach(struct gctl_req *req) static void eli_configure_detached(struct gctl_req *req, const char *prov, int boot, - int geliboot, int displaypass, int trim) + int geliboot, int displaypass, int trim, int autoresize) { struct g_eli_metadata md; bool changed = 0; @@ -1160,6 +1167,20 @@ eli_configure_detached(struct gctl_req *req, const char *prov, int boot, changed = 1; } + if (autoresize == 1 && (md.md_flags & G_ELI_FLAG_AUTORESIZE)) { + if (verbose) + printf("AUTORESIZE flag already configured for %s.\n", prov); + } else if (autoresize == 0 && !(md.md_flags & G_ELI_FLAG_AUTORESIZE)) { + if (verbose) + printf("AUTORESIZE flag not configured for %s.\n", prov); + } else if (autoresize >= 0) { + if (autoresize) + md.md_flags |= G_ELI_FLAG_AUTORESIZE; + else + md.md_flags &= ~G_ELI_FLAG_AUTORESIZE; + changed = 1; + } + if (changed) eli_metadata_store(req, prov, &md); explicit_bzero(&md, sizeof(md)); @@ -1170,8 +1191,8 @@ eli_configure(struct gctl_req *req) { const char *prov; bool boot, noboot, geliboot, nogeliboot, displaypass, nodisplaypass; - bool trim, notrim; - int doboot, dogeliboot, dodisplaypass, dotrim; + bool autoresize, noautoresize, trim, notrim; + int doboot, dogeliboot, dodisplaypass, dotrim, doautoresize; int i, nargs; nargs = gctl_get_int(req, "nargs"); @@ -1188,6 +1209,8 @@ eli_configure(struct gctl_req *req) nodisplaypass = gctl_get_int(req, "nodisplaypass"); trim = gctl_get_int(req, "trim"); notrim = gctl_get_int(req, "notrim"); + autoresize = gctl_get_int(req, "autoresize"); + noautoresize = gctl_get_int(req, "noautoresize"); doboot = -1; if (boot && noboot) { @@ -1229,8 +1252,18 @@ eli_configure(struct gctl_req *req) else if (notrim) dotrim = 0; + doautoresize = -1; + if (autoresize && noautoresize) { + gctl_error(req, "Options -r and -R are mutually exclusive."); + return; + } + if (autoresize) + doautoresize = 1; + else if (noautoresize) + doautoresize = 0; + if (doboot == -1 && dogeliboot == -1 && dodisplaypass == -1 && - dotrim == -1) { + dotrim == -1 && doautoresize == -1) { gctl_error(req, "No option given."); return; } @@ -1242,7 +1275,7 @@ eli_configure(struct gctl_req *req) prov = gctl_get_ascii(req, "arg%d", i); if (!eli_is_attached(prov)) { eli_configure_detached(req, prov, doboot, dogeliboot, - dodisplaypass, dotrim); + dodisplaypass, dotrim, doautoresize); } } } diff --git a/sys/geom/eli/g_eli.c b/sys/geom/eli/g_eli.c index 6df1fdc2d24..04d05e464c5 100644 --- a/sys/geom/eli/g_eli.c +++ b/sys/geom/eli/g_eli.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * - * Copyright (c) 2005-2011 Pawel Jakub Dawidek + * Copyright (c) 2005-2019 Pawel Jakub Dawidek * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -151,6 +151,9 @@ EVENTHANDLER_DEFINE(mountroot, zero_intake_passcache, NULL, 0); static eventhandler_tag g_eli_pre_sync = NULL; +static int g_eli_read_metadata_offset(struct g_class *mp, struct g_provider *pp, + off_t offset, struct g_eli_metadata *md); + static int g_eli_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp); static void g_eli_init(struct g_class *mp); @@ -327,6 +330,79 @@ g_eli_orphan(struct g_consumer *cp) g_eli_destroy(sc, TRUE); } +static void +g_eli_resize(struct g_consumer *cp) +{ + struct g_eli_softc *sc; + struct g_provider *epp, *pp; + off_t oldsize; + + g_topology_assert(); + sc = cp->geom->softc; + if (sc == NULL) + return; + + if ((sc->sc_flags & G_ELI_FLAG_AUTORESIZE) == 0) { + G_ELI_DEBUG(0, "Autoresize is turned off, old size: %jd.", + (intmax_t)sc->sc_provsize); + return; + } + + pp = cp->provider; + + if ((sc->sc_flags & G_ELI_FLAG_ONETIME) == 0) { + struct g_eli_metadata md; + u_char *sector; + int error; + + sector = NULL; + + error = g_eli_read_metadata_offset(cp->geom->class, pp, + sc->sc_provsize - pp->sectorsize, &md); + if (error != 0) { + G_ELI_DEBUG(0, "Cannot read metadata from %s (error=%d).", + pp->name, error); + goto iofail; + } + + md.md_provsize = pp->mediasize; + + 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, + pp->sectorsize); + if (error != 0) { + G_ELI_DEBUG(0, "Cannot store metadata on %s (error=%d).", + pp->name, error); + goto iofail; + } + explicit_bzero(sector, pp->sectorsize); + error = g_write_data(cp, sc->sc_provsize - pp->sectorsize, + sector, pp->sectorsize); + if (error != 0) { + G_ELI_DEBUG(0, "Cannot clear old metadata from %s (error=%d).", + pp->name, error); + goto iofail; + } +iofail: + explicit_bzero(&md, sizeof(md)); + if (sector != NULL) { + explicit_bzero(sector, pp->sectorsize); + free(sector, M_ELI); + } + } + + oldsize = sc->sc_mediasize; + sc->sc_mediasize = eli_mediasize(sc, pp->mediasize, pp->sectorsize); + g_eli_key_resize(sc); + sc->sc_provsize = pp->mediasize; + + epp = LIST_FIRST(&sc->sc_geom->provider); + g_resize_provider(epp, sc->sc_mediasize); + G_ELI_DEBUG(0, "Device %s size changed from %jd to %jd.", epp->name, + (intmax_t)oldsize, (intmax_t)sc->sc_mediasize); +} + /* * BIO_READ: * G_ELI_START -> g_eli_crypto_read -> g_io_request -> g_eli_read_done -> g_eli_crypto_run -> g_eli_crypto_read_done -> g_io_deliver @@ -620,9 +696,9 @@ g_eli_worker(void *arg) } } -int -g_eli_read_metadata(struct g_class *mp, struct g_provider *pp, - struct g_eli_metadata *md) +static int +g_eli_read_metadata_offset(struct g_class *mp, struct g_provider *pp, + off_t offset, struct g_eli_metadata *md) { struct g_geom *gp; struct g_consumer *cp; @@ -649,8 +725,7 @@ g_eli_read_metadata(struct g_class *mp, struct g_provider *pp, if (error != 0) goto end; g_topology_unlock(); - buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, - &error); + buf = g_read_data(cp, offset, pp->sectorsize, &error); g_topology_lock(); if (buf == NULL) goto end; @@ -671,6 +746,15 @@ g_eli_read_metadata(struct g_class *mp, struct g_provider *pp, return (error); } +int +g_eli_read_metadata(struct g_class *mp, struct g_provider *pp, + struct g_eli_metadata *md) +{ + + return (g_eli_read_metadata_offset(mp, pp, + pp->mediasize - pp->sectorsize, md)); +} + /* * The function is called when we had last close on provider and user requested * to close it when this situation occur. @@ -756,6 +840,7 @@ g_eli_create(struct gctl_req *req, struct g_class *mp, struct g_provider *bpp, */ gp->spoiled = g_eli_orphan; gp->orphan = g_eli_orphan; + gp->resize = g_eli_resize; gp->dumpconf = g_eli_dumpconf; /* * If detach-on-last-close feature is not enabled and we don't operate @@ -1253,6 +1338,7 @@ g_eli_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, ADD_FLAG(G_ELI_FLAG_NODELETE, "NODELETE"); ADD_FLAG(G_ELI_FLAG_GELIBOOT, "GELIBOOT"); ADD_FLAG(G_ELI_FLAG_GELIDISPLAYPASS, "GELIDISPLAYPASS"); + ADD_FLAG(G_ELI_FLAG_AUTORESIZE, "AUTORESIZE"); #undef ADD_FLAG } sbuf_printf(sb, "\n"); diff --git a/sys/geom/eli/g_eli.h b/sys/geom/eli/g_eli.h index 5141081710a..36ab0b87649 100644 --- a/sys/geom/eli/g_eli.h +++ b/sys/geom/eli/g_eli.h @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * - * Copyright (c) 2005-2011 Pawel Jakub Dawidek + * Copyright (c) 2005-2019 Pawel Jakub Dawidek * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -104,6 +104,9 @@ #define G_ELI_FLAG_GELIBOOT 0x00000080 /* Hide passphrase length in GELIboot. */ #define G_ELI_FLAG_GELIDISPLAYPASS 0x00000100 +/* Expand provider automatically. */ +#define G_ELI_FLAG_AUTORESIZE 0x00000200 + /* RUNTIME FLAGS. */ /* Provider was open for writing. */ #define G_ELI_FLAG_WOPEN 0x00010000 @@ -211,6 +214,7 @@ struct g_eli_softc { int sc_inflight; off_t sc_mediasize; size_t sc_sectorsize; + off_t sc_provsize; u_int sc_bytes_per_sector; u_int sc_data_per_sector; #ifndef _KERNEL @@ -608,6 +612,23 @@ g_eli_hashlen(u_int algo) return (0); } +static __inline off_t +eli_mediasize(const struct g_eli_softc *sc, off_t mediasize, u_int sectorsize) +{ + + if ((sc->sc_flags & G_ELI_FLAG_ONETIME) == 0) { + mediasize -= sectorsize; + } + if ((sc->sc_flags & G_ELI_FLAG_AUTH) == 0) { + mediasize -= (mediasize % sc->sc_sectorsize); + } else { + mediasize /= sc->sc_bytes_per_sector; + mediasize *= sc->sc_sectorsize; + } + + return (mediasize); +} + static __inline void eli_metadata_softc(struct g_eli_softc *sc, const struct g_eli_metadata *md, u_int sectorsize, off_t mediasize) @@ -649,16 +670,9 @@ eli_metadata_softc(struct g_eli_softc *sc, const struct g_eli_metadata *md, (md->md_sectorsize - 1) / sc->sc_data_per_sector + 1; sc->sc_bytes_per_sector *= sectorsize; } + sc->sc_provsize = mediasize; sc->sc_sectorsize = md->md_sectorsize; - sc->sc_mediasize = mediasize; - if (!(sc->sc_flags & G_ELI_FLAG_ONETIME)) - sc->sc_mediasize -= sectorsize; - if (!(sc->sc_flags & G_ELI_FLAG_AUTH)) - sc->sc_mediasize -= (sc->sc_mediasize % sc->sc_sectorsize); - else { - sc->sc_mediasize /= sc->sc_bytes_per_sector; - sc->sc_mediasize *= sc->sc_sectorsize; - } + sc->sc_mediasize = eli_mediasize(sc, mediasize, sectorsize); sc->sc_ekeylen = md->md_keylen; } @@ -720,6 +734,7 @@ void g_eli_key_fill(struct g_eli_softc *sc, struct g_eli_key *key, #ifdef _KERNEL void g_eli_key_init(struct g_eli_softc *sc); void g_eli_key_destroy(struct g_eli_softc *sc); +void g_eli_key_resize(struct g_eli_softc *sc); uint8_t *g_eli_key_hold(struct g_eli_softc *sc, off_t offset, size_t blocksize); void g_eli_key_drop(struct g_eli_softc *sc, uint8_t *rawkey); #endif diff --git a/sys/geom/eli/g_eli_ctl.c b/sys/geom/eli/g_eli_ctl.c index 1c56b79c36a..b62ff5376f7 100644 --- a/sys/geom/eli/g_eli_ctl.c +++ b/sys/geom/eli/g_eli_ctl.c @@ -264,7 +264,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, *notrim; + int *nargs, *detach, *noautoresize, *notrim; g_topology_assert(); bzero(&md, sizeof(md)); @@ -282,10 +282,15 @@ g_eli_ctl_onetime(struct gctl_req *req, struct g_class *mp) strlcpy(md.md_magic, G_ELI_MAGIC, sizeof(md.md_magic)); md.md_version = G_ELI_VERSION; md.md_flags |= G_ELI_FLAG_ONETIME; + md.md_flags |= G_ELI_FLAG_AUTORESIZE; detach = gctl_get_paraml(req, "detach", sizeof(*detach)); if (detach != NULL && *detach) md.md_flags |= G_ELI_FLAG_WO_DETACH; + noautoresize = gctl_get_paraml(req, "noautoresize", + sizeof(*noautoresize)); + if (noautoresize != NULL && *noautoresize) + md.md_flags &= ~G_ELI_FLAG_AUTORESIZE; notrim = gctl_get_paraml(req, "notrim", sizeof(*notrim)); if (notrim != NULL && *notrim) md.md_flags |= G_ELI_FLAG_NODELETE; @@ -405,7 +410,7 @@ g_eli_ctl_configure(struct gctl_req *req, struct g_class *mp) const char *prov; u_char *sector; int *nargs, *boot, *noboot, *trim, *notrim, *geliboot, *nogeliboot; - int *displaypass, *nodisplaypass; + int *displaypass, *nodisplaypass, *autoresize, *noautoresize; int zero, error, changed; u_int i; @@ -476,6 +481,20 @@ g_eli_ctl_configure(struct gctl_req *req, struct g_class *mp) if (*displaypass || *nodisplaypass) changed = 1; + autoresize = gctl_get_paraml(req, "autoresize", sizeof(*autoresize)); + if (autoresize == NULL) + autoresize = &zero; + noautoresize = gctl_get_paraml(req, "noautoresize", + sizeof(*noautoresize)); + if (noautoresize == NULL) + noautoresize = &zero; + if (*autoresize && *noautoresize) { + gctl_error(req, "Options -r and -R are mutually exclusive."); + return; + } + if (*autoresize || *noautoresize) + changed = 1; + if (!changed) { gctl_error(req, "No option given."); return; @@ -545,6 +564,17 @@ g_eli_ctl_configure(struct gctl_req *req, struct g_class *mp) continue; } + if (*autoresize && (sc->sc_flags & G_ELI_FLAG_AUTORESIZE)) { + G_ELI_DEBUG(1, "AUTORESIZE flag already configured for %s.", + prov); + continue; + } else if (*noautoresize && + !(sc->sc_flags & G_ELI_FLAG_AUTORESIZE)) { + G_ELI_DEBUG(1, "AUTORESIZE flag not configured for %s.", + prov); + continue; + } + if (!(sc->sc_flags & G_ELI_FLAG_ONETIME)) { /* * ONETIME providers don't write metadata to @@ -596,6 +626,14 @@ g_eli_ctl_configure(struct gctl_req *req, struct g_class *mp) sc->sc_flags &= ~G_ELI_FLAG_GELIDISPLAYPASS; } + if (*autoresize) { + md.md_flags |= G_ELI_FLAG_AUTORESIZE; + sc->sc_flags |= G_ELI_FLAG_AUTORESIZE; + } else if (*noautoresize) { + md.md_flags &= ~G_ELI_FLAG_AUTORESIZE; + sc->sc_flags &= ~G_ELI_FLAG_AUTORESIZE; + } + if (sc->sc_flags & G_ELI_FLAG_ONETIME) { /* There's no metadata on disk so we are done here. */ continue; diff --git a/sys/geom/eli/g_eli_key_cache.c b/sys/geom/eli/g_eli_key_cache.c index 431c90f5d9f..db1b1a950a1 100644 --- a/sys/geom/eli/g_eli_key_cache.c +++ b/sys/geom/eli/g_eli_key_cache.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * - * Copyright (c) 2011 Pawel Jakub Dawidek + * Copyright (c) 2011-2019 Pawel Jakub Dawidek * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -252,6 +252,52 @@ g_eli_key_destroy(struct g_eli_softc *sc) mtx_unlock(&sc->sc_ekeys_lock); } +void +g_eli_key_resize(struct g_eli_softc *sc) +{ + uint64_t new_ekeys_total; + off_t mediasize; + size_t blocksize; + + if ((sc->sc_flags & G_ELI_FLAG_SINGLE_KEY) != 0) { + return; + } + + mtx_lock(&sc->sc_ekeys_lock); + + if ((sc->sc_flags & G_ELI_FLAG_AUTH) != 0) { + struct g_provider *pp; + + pp = LIST_FIRST(&sc->sc_geom->consumer)->provider; + mediasize = pp->mediasize; + blocksize = pp->sectorsize; + } else { + mediasize = sc->sc_mediasize; + blocksize = sc->sc_sectorsize; + } + new_ekeys_total = ((mediasize - 1) >> G_ELI_KEY_SHIFT) / blocksize + 1; + /* We only allow to grow. */ + KASSERT(new_ekeys_total >= sc->sc_ekeys_total, + ("new_ekeys_total=%ju < sc_ekeys_total=%ju", + (uintmax_t)new_ekeys_total, (uintmax_t)sc->sc_ekeys_total)); + if (new_ekeys_total <= g_eli_key_cache_limit) { + uint64_t keyno; + + for (keyno = sc->sc_ekeys_total; keyno < new_ekeys_total; + keyno++) { + (void)g_eli_key_allocate(sc, keyno); + } + KASSERT(new_ekeys_total == sc->sc_ekeys_allocated, + ("new_ekeys_total=%ju != sc_ekeys_allocated=%ju", + (uintmax_t)new_ekeys_total, + (uintmax_t)sc->sc_ekeys_allocated)); + } + + sc->sc_ekeys_total = new_ekeys_total; + + mtx_unlock(&sc->sc_ekeys_lock); +} + /* * Select encryption key. If G_ELI_FLAG_SINGLE_KEY is present we only have one * key available for all the data. If the flag is not present select the key