xref: /freebsd/sys/geom/bde/g_bde.c (revision 95ee2897)
119b5c7bcSPoul-Henning Kamp /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
33728855aSPedro F. Giffuni  *
419b5c7bcSPoul-Henning Kamp  * Copyright (c) 2002 Poul-Henning Kamp
519b5c7bcSPoul-Henning Kamp  * Copyright (c) 2002 Networks Associates Technology, Inc.
619b5c7bcSPoul-Henning Kamp  * All rights reserved.
719b5c7bcSPoul-Henning Kamp  *
819b5c7bcSPoul-Henning Kamp  * This software was developed for the FreeBSD Project by Poul-Henning Kamp
919b5c7bcSPoul-Henning Kamp  * and NAI Labs, the Security Research Division of Network Associates, Inc.
1019b5c7bcSPoul-Henning Kamp  * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
1119b5c7bcSPoul-Henning Kamp  * DARPA CHATS research program.
1219b5c7bcSPoul-Henning Kamp  *
1319b5c7bcSPoul-Henning Kamp  * Redistribution and use in source and binary forms, with or without
1419b5c7bcSPoul-Henning Kamp  * modification, are permitted provided that the following conditions
1519b5c7bcSPoul-Henning Kamp  * are met:
1619b5c7bcSPoul-Henning Kamp  * 1. Redistributions of source code must retain the above copyright
1719b5c7bcSPoul-Henning Kamp  *    notice, this list of conditions and the following disclaimer.
1819b5c7bcSPoul-Henning Kamp  * 2. Redistributions in binary form must reproduce the above copyright
1919b5c7bcSPoul-Henning Kamp  *    notice, this list of conditions and the following disclaimer in the
2019b5c7bcSPoul-Henning Kamp  *    documentation and/or other materials provided with the distribution.
2119b5c7bcSPoul-Henning Kamp  *
2219b5c7bcSPoul-Henning Kamp  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2319b5c7bcSPoul-Henning Kamp  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2419b5c7bcSPoul-Henning Kamp  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2519b5c7bcSPoul-Henning Kamp  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2619b5c7bcSPoul-Henning Kamp  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2719b5c7bcSPoul-Henning Kamp  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2819b5c7bcSPoul-Henning Kamp  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2919b5c7bcSPoul-Henning Kamp  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3019b5c7bcSPoul-Henning Kamp  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3119b5c7bcSPoul-Henning Kamp  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3219b5c7bcSPoul-Henning Kamp  * SUCH DAMAGE.
3319b5c7bcSPoul-Henning Kamp  *
3419b5c7bcSPoul-Henning Kamp  */
3519b5c7bcSPoul-Henning Kamp 
3619b5c7bcSPoul-Henning Kamp #include <sys/param.h>
3719b5c7bcSPoul-Henning Kamp #include <sys/bio.h>
3819b5c7bcSPoul-Henning Kamp #include <sys/lock.h>
3919b5c7bcSPoul-Henning Kamp #include <sys/mutex.h>
4019b5c7bcSPoul-Henning Kamp #include <sys/malloc.h>
4119b5c7bcSPoul-Henning Kamp #include <sys/systm.h>
4219b5c7bcSPoul-Henning Kamp #include <sys/kernel.h>
4319b5c7bcSPoul-Henning Kamp #include <sys/kthread.h>
44cb08c2ccSAlexander Leidinger #include <sys/sysctl.h>
4519b5c7bcSPoul-Henning Kamp 
4668527b3aSHajimu UMEMOTO #include <crypto/rijndael/rijndael-api-fst.h>
477a3f5d11SAllan Jude #include <crypto/sha2/sha512.h>
485afa4614SPoul-Henning Kamp #include <geom/geom.h>
495afa4614SPoul-Henning Kamp #include <geom/bde/g_bde.h>
5019b5c7bcSPoul-Henning Kamp #define BDE_CLASS_NAME "BDE"
5119b5c7bcSPoul-Henning Kamp 
52cb08c2ccSAlexander Leidinger FEATURE(geom_bde, "GEOM-based Disk Encryption");
53cb08c2ccSAlexander Leidinger 
5419b5c7bcSPoul-Henning Kamp static void
g_bde_start(struct bio * bp)5519b5c7bcSPoul-Henning Kamp g_bde_start(struct bio *bp)
5619b5c7bcSPoul-Henning Kamp {
5719b5c7bcSPoul-Henning Kamp 
5819b5c7bcSPoul-Henning Kamp 	switch (bp->bio_cmd) {
5919b5c7bcSPoul-Henning Kamp 	case BIO_DELETE:
6019b5c7bcSPoul-Henning Kamp 	case BIO_READ:
6119b5c7bcSPoul-Henning Kamp 	case BIO_WRITE:
6219b5c7bcSPoul-Henning Kamp 		g_bde_start1(bp);
6319b5c7bcSPoul-Henning Kamp 		break;
6419b5c7bcSPoul-Henning Kamp 	case BIO_GETATTR:
6519b5c7bcSPoul-Henning Kamp 		g_io_deliver(bp, EOPNOTSUPP);
6619b5c7bcSPoul-Henning Kamp 		break;
6719b5c7bcSPoul-Henning Kamp 	default:
6819b5c7bcSPoul-Henning Kamp 		g_io_deliver(bp, EOPNOTSUPP);
6919b5c7bcSPoul-Henning Kamp 		return;
7019b5c7bcSPoul-Henning Kamp 	}
7119b5c7bcSPoul-Henning Kamp 	return;
7219b5c7bcSPoul-Henning Kamp }
7319b5c7bcSPoul-Henning Kamp 
7419b5c7bcSPoul-Henning Kamp static void
g_bde_orphan(struct g_consumer * cp)7519b5c7bcSPoul-Henning Kamp g_bde_orphan(struct g_consumer *cp)
7619b5c7bcSPoul-Henning Kamp {
7719b5c7bcSPoul-Henning Kamp 	struct g_geom *gp;
7819b5c7bcSPoul-Henning Kamp 	struct g_provider *pp;
7919b5c7bcSPoul-Henning Kamp 	struct g_bde_softc *sc;
8019b5c7bcSPoul-Henning Kamp 
8119b5c7bcSPoul-Henning Kamp 	g_trace(G_T_TOPOLOGY, "g_bde_orphan(%p/%s)", cp, cp->provider->name);
8219b5c7bcSPoul-Henning Kamp 	g_topology_assert();
8319b5c7bcSPoul-Henning Kamp 
8419b5c7bcSPoul-Henning Kamp 	gp = cp->geom;
8519b5c7bcSPoul-Henning Kamp 	sc = gp->softc;
8619b5c7bcSPoul-Henning Kamp 	gp->flags |= G_GEOM_WITHER;
8719b5c7bcSPoul-Henning Kamp 	LIST_FOREACH(pp, &gp->provider, provider)
888b64f3caSAlexander Motin 		g_wither_provider(pp, ENXIO);
896572e5ffSJohn Baldwin 	explicit_bzero(sc, sizeof(struct g_bde_softc));	/* destroy evidence */
9019b5c7bcSPoul-Henning Kamp 	return;
9119b5c7bcSPoul-Henning Kamp }
9219b5c7bcSPoul-Henning Kamp 
9319b5c7bcSPoul-Henning Kamp static int
g_bde_access(struct g_provider * pp,int dr,int dw,int de)9419b5c7bcSPoul-Henning Kamp g_bde_access(struct g_provider *pp, int dr, int dw, int de)
9519b5c7bcSPoul-Henning Kamp {
9619b5c7bcSPoul-Henning Kamp 	struct g_geom *gp;
9719b5c7bcSPoul-Henning Kamp 	struct g_consumer *cp;
9819b5c7bcSPoul-Henning Kamp 
9919b5c7bcSPoul-Henning Kamp 	gp = pp->geom;
10019b5c7bcSPoul-Henning Kamp 	cp = LIST_FIRST(&gp->consumer);
10119b5c7bcSPoul-Henning Kamp 	if (cp->acr == 0 && cp->acw == 0 && cp->ace == 0) {
10219b5c7bcSPoul-Henning Kamp 		de++;
10319b5c7bcSPoul-Henning Kamp 		dr++;
10419b5c7bcSPoul-Henning Kamp 	}
10519b5c7bcSPoul-Henning Kamp 	/* ... and let go of it on last close */
10619b5c7bcSPoul-Henning Kamp 	if ((cp->acr + dr) == 0 && (cp->acw + dw) == 0 && (cp->ace + de) == 1) {
10719b5c7bcSPoul-Henning Kamp 		de--;
10819b5c7bcSPoul-Henning Kamp 		dr--;
10919b5c7bcSPoul-Henning Kamp 	}
110d2bae332SPoul-Henning Kamp 	return (g_access(cp, dr, dw, de));
11119b5c7bcSPoul-Henning Kamp }
11219b5c7bcSPoul-Henning Kamp 
11383d771deSPoul-Henning Kamp static void
g_bde_create_geom(struct gctl_req * req,struct g_class * mp,struct g_provider * pp)11429c331bfSPoul-Henning Kamp g_bde_create_geom(struct gctl_req *req, struct g_class *mp, struct g_provider *pp)
11529c331bfSPoul-Henning Kamp {
11629c331bfSPoul-Henning Kamp 	struct g_geom *gp;
11729c331bfSPoul-Henning Kamp 	struct g_consumer *cp;
11829c331bfSPoul-Henning Kamp 	struct g_bde_key *kp;
11929c331bfSPoul-Henning Kamp 	int error, i;
12029c331bfSPoul-Henning Kamp 	u_int sectorsize;
12129c331bfSPoul-Henning Kamp 	off_t mediasize;
12229c331bfSPoul-Henning Kamp 	struct g_bde_softc *sc;
12329c331bfSPoul-Henning Kamp 	void *pass;
12429c331bfSPoul-Henning Kamp 	void *key;
12529c331bfSPoul-Henning Kamp 
12629c331bfSPoul-Henning Kamp 	g_trace(G_T_TOPOLOGY, "g_bde_create_geom(%s, %s)", mp->name, pp->name);
12729c331bfSPoul-Henning Kamp 	g_topology_assert();
12829c331bfSPoul-Henning Kamp 	gp = NULL;
12929c331bfSPoul-Henning Kamp 
13029c331bfSPoul-Henning Kamp 	gp = g_new_geomf(mp, "%s.bde", pp->name);
13129c331bfSPoul-Henning Kamp 	cp = g_new_consumer(gp);
132d22ff249SEdward Tomasz Napierala 	error = g_attach(cp, pp);
133d22ff249SEdward Tomasz Napierala 	if (error != 0) {
134d22ff249SEdward Tomasz Napierala 		g_destroy_consumer(cp);
135d22ff249SEdward Tomasz Napierala 		g_destroy_geom(gp);
136d22ff249SEdward Tomasz Napierala 		gctl_error(req, "could not attach consumer");
137d22ff249SEdward Tomasz Napierala 		return;
138d22ff249SEdward Tomasz Napierala 	}
139d2bae332SPoul-Henning Kamp 	error = g_access(cp, 1, 1, 1);
14029c331bfSPoul-Henning Kamp 	if (error) {
14129c331bfSPoul-Henning Kamp 		g_detach(cp);
14229c331bfSPoul-Henning Kamp 		g_destroy_consumer(cp);
14329c331bfSPoul-Henning Kamp 		g_destroy_geom(gp);
14483d771deSPoul-Henning Kamp 		gctl_error(req, "could not access consumer");
145d091e630SPoul-Henning Kamp 		return;
14629c331bfSPoul-Henning Kamp 	}
147c0c393a2SPoul-Henning Kamp 	pass = NULL;
148c0c393a2SPoul-Henning Kamp 	key = NULL;
149c0c393a2SPoul-Henning Kamp 	do {
150c0c393a2SPoul-Henning Kamp 		pass = gctl_get_param(req, "pass", &i);
151c0c393a2SPoul-Henning Kamp 		if (pass == NULL || i != SHA512_DIGEST_LENGTH) {
15283d771deSPoul-Henning Kamp 			gctl_error(req, "No usable key presented");
153c0c393a2SPoul-Henning Kamp 			break;
154c0c393a2SPoul-Henning Kamp 		}
155c0c393a2SPoul-Henning Kamp 		key = gctl_get_param(req, "key", &i);
156c0c393a2SPoul-Henning Kamp 		if (key != NULL && i != 16) {
15783d771deSPoul-Henning Kamp 			gctl_error(req, "Invalid key presented");
158c0c393a2SPoul-Henning Kamp 			break;
159c0c393a2SPoul-Henning Kamp 		}
16029c331bfSPoul-Henning Kamp 		sectorsize = cp->provider->sectorsize;
16129c331bfSPoul-Henning Kamp 		mediasize = cp->provider->mediasize;
16229c331bfSPoul-Henning Kamp 		sc = g_malloc(sizeof(struct g_bde_softc), M_WAITOK | M_ZERO);
16329c331bfSPoul-Henning Kamp 		gp->softc = sc;
16429c331bfSPoul-Henning Kamp 		sc->geom = gp;
16529c331bfSPoul-Henning Kamp 		sc->consumer = cp;
16629c331bfSPoul-Henning Kamp 
16729c331bfSPoul-Henning Kamp 		error = g_bde_decrypt_lock(sc, pass, key,
16829c331bfSPoul-Henning Kamp 		    mediasize, sectorsize, NULL);
1696572e5ffSJohn Baldwin 		explicit_bzero(sc->sha2, sizeof sc->sha2);
17029c331bfSPoul-Henning Kamp 		if (error)
17129c331bfSPoul-Henning Kamp 			break;
17229c331bfSPoul-Henning Kamp 		kp = &sc->key;
17329c331bfSPoul-Henning Kamp 
17429c331bfSPoul-Henning Kamp 		/* Initialize helper-fields */
17529c331bfSPoul-Henning Kamp 		kp->keys_per_sector = kp->sectorsize / G_BDE_SKEYLEN;
17629c331bfSPoul-Henning Kamp 		kp->zone_cont = kp->keys_per_sector * kp->sectorsize;
17729c331bfSPoul-Henning Kamp 		kp->zone_width = kp->zone_cont + kp->sectorsize;
17829c331bfSPoul-Henning Kamp 		kp->media_width = kp->sectorN - kp->sector0 -
17929c331bfSPoul-Henning Kamp 		    G_BDE_MAXKEYS * kp->sectorsize;
18029c331bfSPoul-Henning Kamp 
18129c331bfSPoul-Henning Kamp 		/* Our external parameters */
18229c331bfSPoul-Henning Kamp 		sc->zone_cont = kp->zone_cont;
18329c331bfSPoul-Henning Kamp 		sc->mediasize = g_bde_max_sector(kp);
18429c331bfSPoul-Henning Kamp 		sc->sectorsize = kp->sectorsize;
18529c331bfSPoul-Henning Kamp 
18629c331bfSPoul-Henning Kamp 		TAILQ_INIT(&sc->freelist);
18729c331bfSPoul-Henning Kamp 		TAILQ_INIT(&sc->worklist);
18829c331bfSPoul-Henning Kamp 		mtx_init(&sc->worklist_mutex, "g_bde_worklist", NULL, MTX_DEF);
18929c331bfSPoul-Henning Kamp 		/* XXX: error check */
1903745c395SJulian Elischer 		kproc_create(g_bde_worker, gp, &sc->thread, 0, 0,
19129c331bfSPoul-Henning Kamp 			"g_bde %s", gp->name);
19202c62349SJaakko Heinonen 		pp = g_new_providerf(gp, "%s", gp->name);
19329c331bfSPoul-Henning Kamp 		pp->stripesize = kp->zone_cont;
19429c331bfSPoul-Henning Kamp 		pp->stripeoffset = 0;
19529c331bfSPoul-Henning Kamp 		pp->mediasize = sc->mediasize;
19629c331bfSPoul-Henning Kamp 		pp->sectorsize = sc->sectorsize;
19729c331bfSPoul-Henning Kamp 		g_error_provider(pp, 0);
19829c331bfSPoul-Henning Kamp 		break;
199c0c393a2SPoul-Henning Kamp 	} while (0);
20083d771deSPoul-Henning Kamp 	if (pass != NULL)
2016572e5ffSJohn Baldwin 		explicit_bzero(pass, SHA512_DIGEST_LENGTH);
20283d771deSPoul-Henning Kamp 	if (key != NULL)
2036572e5ffSJohn Baldwin 		explicit_bzero(key, 16);
20483d771deSPoul-Henning Kamp 	if (error == 0)
20583d771deSPoul-Henning Kamp 		return;
206d2bae332SPoul-Henning Kamp 	g_access(cp, -1, -1, -1);
20729c331bfSPoul-Henning Kamp 	g_detach(cp);
20829c331bfSPoul-Henning Kamp 	g_destroy_consumer(cp);
20929c331bfSPoul-Henning Kamp 	g_free(gp->softc);
21029c331bfSPoul-Henning Kamp 	g_destroy_geom(gp);
211cd15a010SPoul-Henning Kamp 	switch (error) {
212cd15a010SPoul-Henning Kamp 	case ENOENT:
213cd15a010SPoul-Henning Kamp 		gctl_error(req, "Lock was destroyed");
214cd15a010SPoul-Henning Kamp 		break;
215cd15a010SPoul-Henning Kamp 	case ESRCH:
216cd15a010SPoul-Henning Kamp 		gctl_error(req, "Lock was nuked");
217cd15a010SPoul-Henning Kamp 		break;
218cd15a010SPoul-Henning Kamp 	case EINVAL:
219cd15a010SPoul-Henning Kamp 		gctl_error(req, "Could not open lock");
220cd15a010SPoul-Henning Kamp 		break;
221cd15a010SPoul-Henning Kamp 	case ENOTDIR:
222cd15a010SPoul-Henning Kamp 		gctl_error(req, "Lock not found");
223cd15a010SPoul-Henning Kamp 		break;
224cd15a010SPoul-Henning Kamp 	default:
225cd15a010SPoul-Henning Kamp 		gctl_error(req, "Could not open lock (%d)", error);
226cd15a010SPoul-Henning Kamp 		break;
227cd15a010SPoul-Henning Kamp 	}
22883d771deSPoul-Henning Kamp 	return;
22929c331bfSPoul-Henning Kamp }
23029c331bfSPoul-Henning Kamp 
23129c331bfSPoul-Henning Kamp static int
g_bde_destroy_geom(struct gctl_req * req,struct g_class * mp,struct g_geom * gp)23229c331bfSPoul-Henning Kamp g_bde_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp)
23329c331bfSPoul-Henning Kamp {
23429c331bfSPoul-Henning Kamp 	struct g_consumer *cp;
23529c331bfSPoul-Henning Kamp 	struct g_provider *pp;
23629c331bfSPoul-Henning Kamp 	struct g_bde_softc *sc;
23729c331bfSPoul-Henning Kamp 
23829c331bfSPoul-Henning Kamp 	g_trace(G_T_TOPOLOGY, "g_bde_destroy_geom(%s, %s)", mp->name, gp->name);
23929c331bfSPoul-Henning Kamp 	g_topology_assert();
24029c331bfSPoul-Henning Kamp 	/*
24129c331bfSPoul-Henning Kamp 	 * Orderly detachment.
24229c331bfSPoul-Henning Kamp 	 */
24329c331bfSPoul-Henning Kamp 	KASSERT(gp != NULL, ("NULL geom"));
24429c331bfSPoul-Henning Kamp 	pp = LIST_FIRST(&gp->provider);
24529c331bfSPoul-Henning Kamp 	KASSERT(pp != NULL, ("NULL provider"));
24629c331bfSPoul-Henning Kamp 	if (pp->acr > 0 || pp->acw > 0 || pp->ace > 0)
24729c331bfSPoul-Henning Kamp 		return (EBUSY);
24829c331bfSPoul-Henning Kamp 	sc = gp->softc;
24929c331bfSPoul-Henning Kamp 	cp = LIST_FIRST(&gp->consumer);
25029c331bfSPoul-Henning Kamp 	KASSERT(cp != NULL, ("NULL consumer"));
25129c331bfSPoul-Henning Kamp 	sc->dead = 1;
25229c331bfSPoul-Henning Kamp 	wakeup(sc);
25328159144SPoul-Henning Kamp 	g_access(cp, -1, -1, -1);
25429c331bfSPoul-Henning Kamp 	g_detach(cp);
25529c331bfSPoul-Henning Kamp 	g_destroy_consumer(cp);
25629c331bfSPoul-Henning Kamp 	while (sc->dead != 2 && !LIST_EMPTY(&pp->consumers))
25729c331bfSPoul-Henning Kamp 		tsleep(sc, PRIBIO, "g_bdedie", hz);
25829c331bfSPoul-Henning Kamp 	mtx_destroy(&sc->worklist_mutex);
2596572e5ffSJohn Baldwin 	explicit_bzero(&sc->key, sizeof sc->key);
26029c331bfSPoul-Henning Kamp 	g_free(sc);
26183d771deSPoul-Henning Kamp 	g_wither_geom(gp, ENXIO);
26229c331bfSPoul-Henning Kamp 	return (0);
26329c331bfSPoul-Henning Kamp }
26429c331bfSPoul-Henning Kamp 
26583d771deSPoul-Henning Kamp static void
g_bde_ctlreq(struct gctl_req * req,struct g_class * mp,char const * verb)26683d771deSPoul-Henning Kamp g_bde_ctlreq(struct gctl_req *req, struct g_class *mp, char const *verb)
26783d771deSPoul-Henning Kamp {
26883d771deSPoul-Henning Kamp 	struct g_geom *gp;
26983d771deSPoul-Henning Kamp 	struct g_provider *pp;
27083d771deSPoul-Henning Kamp 
27183d771deSPoul-Henning Kamp 	if (!strcmp(verb, "create geom")) {
27283d771deSPoul-Henning Kamp 		pp = gctl_get_provider(req, "provider");
27383d771deSPoul-Henning Kamp 		if (pp != NULL)
27483d771deSPoul-Henning Kamp 			g_bde_create_geom(req, mp, pp);
27583d771deSPoul-Henning Kamp 	} else if (!strcmp(verb, "destroy geom")) {
27683d771deSPoul-Henning Kamp 		gp = gctl_get_geom(req, mp, "geom");
27783d771deSPoul-Henning Kamp 		if (gp != NULL)
27883d771deSPoul-Henning Kamp 			g_bde_destroy_geom(req, mp, gp);
27983d771deSPoul-Henning Kamp 	} else {
28083d771deSPoul-Henning Kamp 		gctl_error(req, "unknown verb");
28183d771deSPoul-Henning Kamp 	}
28283d771deSPoul-Henning Kamp }
28383d771deSPoul-Henning Kamp 
28419b5c7bcSPoul-Henning Kamp static struct g_class g_bde_class	= {
285c138fec0SPoul-Henning Kamp 	.name = BDE_CLASS_NAME,
2865721c9c7SPoul-Henning Kamp 	.version = G_VERSION,
28729c331bfSPoul-Henning Kamp 	.destroy_geom = g_bde_destroy_geom,
28883d771deSPoul-Henning Kamp 	.ctlreq = g_bde_ctlreq,
289650ee351SPoul-Henning Kamp 	.start = g_bde_start,
290650ee351SPoul-Henning Kamp 	.orphan = g_bde_orphan,
291650ee351SPoul-Henning Kamp 	.access = g_bde_access,
292650ee351SPoul-Henning Kamp 	.spoiled = g_std_spoiled,
29319b5c7bcSPoul-Henning Kamp };
29419b5c7bcSPoul-Henning Kamp 
29519b5c7bcSPoul-Henning Kamp DECLARE_GEOM_CLASS(g_bde_class, g_bde);
29674d6c131SKyle Evans MODULE_VERSION(geom_bde, 0);
297