2015-01-02 22:42:05 +03:00
|
|
|
/* $NetBSD: cgd.c,v 1.96 2015/01/02 19:42:06 christos Exp $ */
|
2002-10-04 22:22:35 +04:00
|
|
|
|
|
|
|
/*-
|
|
|
|
* Copyright (c) 2002 The NetBSD Foundation, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
|
|
* by Roland C. Dowdeswell.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
2015-01-02 22:42:05 +03:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: cgd.c,v 1.96 2015/01/02 19:42:06 christos Exp $");
|
2002-10-04 22:22:35 +04:00
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/proc.h>
|
|
|
|
#include <sys/errno.h>
|
|
|
|
#include <sys/buf.h>
|
2004-10-28 11:07:35 +04:00
|
|
|
#include <sys/bufq.h>
|
2002-10-04 22:22:35 +04:00
|
|
|
#include <sys/malloc.h>
|
2011-06-21 10:23:38 +04:00
|
|
|
#include <sys/module.h>
|
2002-10-04 22:22:35 +04:00
|
|
|
#include <sys/pool.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/device.h>
|
|
|
|
#include <sys/disk.h>
|
|
|
|
#include <sys/disklabel.h>
|
|
|
|
#include <sys/fcntl.h>
|
2010-11-19 09:44:33 +03:00
|
|
|
#include <sys/namei.h> /* for pathbuf */
|
2002-10-04 22:22:35 +04:00
|
|
|
#include <sys/vnode.h>
|
|
|
|
#include <sys/conf.h>
|
2009-11-10 23:05:50 +03:00
|
|
|
#include <sys/syslog.h>
|
2002-10-04 22:22:35 +04:00
|
|
|
|
|
|
|
#include <dev/dkvar.h>
|
|
|
|
#include <dev/cgdvar.h>
|
|
|
|
|
2014-06-14 11:39:00 +04:00
|
|
|
#include <miscfs/specfs/specdev.h> /* for v_rdev */
|
|
|
|
|
2002-10-04 22:22:35 +04:00
|
|
|
/* Entry Point Functions */
|
|
|
|
|
|
|
|
void cgdattach(int);
|
|
|
|
|
2004-08-23 09:37:42 +04:00
|
|
|
static dev_type_open(cgdopen);
|
|
|
|
static dev_type_close(cgdclose);
|
|
|
|
static dev_type_read(cgdread);
|
|
|
|
static dev_type_write(cgdwrite);
|
|
|
|
static dev_type_ioctl(cgdioctl);
|
|
|
|
static dev_type_strategy(cgdstrategy);
|
|
|
|
static dev_type_dump(cgddump);
|
|
|
|
static dev_type_size(cgdsize);
|
2002-10-04 22:22:35 +04:00
|
|
|
|
|
|
|
const struct bdevsw cgd_bdevsw = {
|
2014-03-16 09:20:22 +04:00
|
|
|
.d_open = cgdopen,
|
|
|
|
.d_close = cgdclose,
|
|
|
|
.d_strategy = cgdstrategy,
|
|
|
|
.d_ioctl = cgdioctl,
|
|
|
|
.d_dump = cgddump,
|
|
|
|
.d_psize = cgdsize,
|
2014-07-25 12:02:18 +04:00
|
|
|
.d_discard = nodiscard,
|
2014-03-16 09:20:22 +04:00
|
|
|
.d_flag = D_DISK
|
2002-10-04 22:22:35 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
const struct cdevsw cgd_cdevsw = {
|
2014-03-16 09:20:22 +04:00
|
|
|
.d_open = cgdopen,
|
|
|
|
.d_close = cgdclose,
|
|
|
|
.d_read = cgdread,
|
|
|
|
.d_write = cgdwrite,
|
|
|
|
.d_ioctl = cgdioctl,
|
|
|
|
.d_stop = nostop,
|
|
|
|
.d_tty = notty,
|
|
|
|
.d_poll = nopoll,
|
|
|
|
.d_mmap = nommap,
|
|
|
|
.d_kqfilter = nokqfilter,
|
2014-07-25 12:10:31 +04:00
|
|
|
.d_discard = nodiscard,
|
2014-03-16 09:20:22 +04:00
|
|
|
.d_flag = D_DISK
|
2002-10-04 22:22:35 +04:00
|
|
|
};
|
|
|
|
|
2010-01-13 00:08:08 +03:00
|
|
|
static int cgd_match(device_t, cfdata_t, void *);
|
|
|
|
static void cgd_attach(device_t, device_t, void *);
|
|
|
|
static int cgd_detach(device_t, int);
|
|
|
|
static struct cgd_softc *cgd_spawn(int);
|
|
|
|
static int cgd_destroy(device_t);
|
|
|
|
|
2002-10-04 22:22:35 +04:00
|
|
|
/* Internal Functions */
|
|
|
|
|
2014-05-25 23:23:49 +04:00
|
|
|
static void cgdstart(struct dk_softc *);
|
2002-10-04 22:22:35 +04:00
|
|
|
static void cgdiodone(struct buf *);
|
|
|
|
|
2005-12-11 15:16:03 +03:00
|
|
|
static int cgd_ioctl_set(struct cgd_softc *, void *, struct lwp *);
|
2010-01-13 00:08:08 +03:00
|
|
|
static int cgd_ioctl_clr(struct cgd_softc *, struct lwp *);
|
2012-12-05 06:23:20 +04:00
|
|
|
static int cgd_ioctl_get(dev_t, void *, struct lwp *);
|
2005-06-29 00:23:02 +04:00
|
|
|
static int cgdinit(struct cgd_softc *, const char *, struct vnode *,
|
2005-12-11 15:16:03 +03:00
|
|
|
struct lwp *);
|
2007-03-04 08:59:00 +03:00
|
|
|
static void cgd_cipher(struct cgd_softc *, void *, void *,
|
2002-10-04 22:22:35 +04:00
|
|
|
size_t, daddr_t, size_t, int);
|
|
|
|
|
|
|
|
/* Pseudo-disk Interface */
|
|
|
|
|
|
|
|
static struct dk_intf the_dkintf = {
|
2015-01-02 22:42:05 +03:00
|
|
|
DKTYPE_CGD,
|
2002-10-04 22:22:35 +04:00
|
|
|
"cgd",
|
|
|
|
cgdopen,
|
|
|
|
cgdclose,
|
|
|
|
cgdstrategy,
|
|
|
|
cgdstart,
|
|
|
|
};
|
|
|
|
static struct dk_intf *di = &the_dkintf;
|
|
|
|
|
2005-08-20 16:03:52 +04:00
|
|
|
static struct dkdriver cgddkdriver = {
|
|
|
|
.d_strategy = cgdstrategy,
|
|
|
|
.d_minphys = minphys,
|
|
|
|
};
|
|
|
|
|
2010-01-13 00:08:08 +03:00
|
|
|
CFATTACH_DECL3_NEW(cgd, sizeof(struct cgd_softc),
|
|
|
|
cgd_match, cgd_attach, cgd_detach, NULL, NULL, NULL, DVF_DETACH_SHUTDOWN);
|
|
|
|
extern struct cfdriver cgd_cd;
|
|
|
|
|
2002-10-04 22:22:35 +04:00
|
|
|
/* DIAGNOSTIC and DEBUG definitions */
|
|
|
|
|
|
|
|
#if defined(CGDDEBUG) && !defined(DEBUG)
|
|
|
|
#define DEBUG
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
int cgddebug = 0;
|
|
|
|
|
|
|
|
#define CGDB_FOLLOW 0x1
|
|
|
|
#define CGDB_IO 0x2
|
|
|
|
#define CGDB_CRYPTO 0x4
|
|
|
|
|
|
|
|
#define IFDEBUG(x,y) if (cgddebug & (x)) y
|
|
|
|
#define DPRINTF(x,y) IFDEBUG(x, printf y)
|
|
|
|
#define DPRINTF_FOLLOW(y) DPRINTF(CGDB_FOLLOW, y)
|
|
|
|
|
2005-05-31 23:20:37 +04:00
|
|
|
static void hexprint(const char *, void *, int);
|
2002-10-04 22:22:35 +04:00
|
|
|
|
|
|
|
#else
|
|
|
|
#define IFDEBUG(x,y)
|
|
|
|
#define DPRINTF(x,y)
|
|
|
|
#define DPRINTF_FOLLOW(y)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
2005-02-27 03:26:58 +03:00
|
|
|
#define DIAGPANIC(x) panic x
|
2002-10-04 22:22:35 +04:00
|
|
|
#define DIAGCONDPANIC(x,y) if (x) panic y
|
|
|
|
#else
|
|
|
|
#define DIAGPANIC(x)
|
|
|
|
#define DIAGCONDPANIC(x,y)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Global variables */
|
|
|
|
|
|
|
|
/* Utility Functions */
|
|
|
|
|
|
|
|
#define CGDUNIT(x) DISKUNIT(x)
|
|
|
|
#define GETCGD_SOFTC(_cs, x) if (!((_cs) = getcgd_softc(x))) return ENXIO
|
|
|
|
|
2010-01-13 00:08:08 +03:00
|
|
|
/* The code */
|
|
|
|
|
2002-10-04 22:22:35 +04:00
|
|
|
static struct cgd_softc *
|
|
|
|
getcgd_softc(dev_t dev)
|
|
|
|
{
|
|
|
|
int unit = CGDUNIT(dev);
|
2010-01-13 00:08:08 +03:00
|
|
|
struct cgd_softc *sc;
|
2002-10-04 22:22:35 +04:00
|
|
|
|
2009-01-11 12:51:38 +03:00
|
|
|
DPRINTF_FOLLOW(("getcgd_softc(0x%"PRIx64"): unit = %d\n", dev, unit));
|
2010-01-13 00:08:08 +03:00
|
|
|
|
|
|
|
sc = device_lookup_private(&cgd_cd, unit);
|
|
|
|
if (sc == NULL)
|
|
|
|
sc = cgd_spawn(unit);
|
|
|
|
return sc;
|
2002-10-04 22:22:35 +04:00
|
|
|
}
|
|
|
|
|
2010-01-13 00:08:08 +03:00
|
|
|
static int
|
|
|
|
cgd_match(device_t self, cfdata_t cfdata, void *aux)
|
|
|
|
{
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
2002-10-04 22:22:35 +04:00
|
|
|
|
|
|
|
static void
|
2010-01-13 00:08:08 +03:00
|
|
|
cgd_attach(device_t parent, device_t self, void *aux)
|
2002-10-04 22:22:35 +04:00
|
|
|
{
|
2010-01-13 00:08:08 +03:00
|
|
|
struct cgd_softc *sc = device_private(self);
|
2002-10-04 22:22:35 +04:00
|
|
|
|
2014-03-18 19:44:37 +04:00
|
|
|
mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_BIO);
|
2012-05-25 14:53:46 +04:00
|
|
|
dk_sc_init(&sc->sc_dksc, device_xname(self));
|
|
|
|
sc->sc_dksc.sc_dev = self;
|
2010-01-13 00:08:08 +03:00
|
|
|
disk_init(&sc->sc_dksc.sc_dkdev, sc->sc_dksc.sc_xname, &cgddkdriver);
|
2010-02-11 21:24:48 +03:00
|
|
|
|
|
|
|
if (!pmf_device_register(self, NULL, NULL))
|
|
|
|
aprint_error_dev(self, "unable to register power management hooks\n");
|
2010-01-13 00:08:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
cgd_detach(device_t self, int flags)
|
|
|
|
{
|
2010-01-20 21:55:17 +03:00
|
|
|
int ret;
|
|
|
|
const int pmask = 1 << RAW_PART;
|
2010-01-13 00:08:08 +03:00
|
|
|
struct cgd_softc *sc = device_private(self);
|
2010-01-20 21:55:17 +03:00
|
|
|
struct dk_softc *dksc = &sc->sc_dksc;
|
2010-01-13 00:08:08 +03:00
|
|
|
|
2010-01-20 21:55:17 +03:00
|
|
|
if (DK_BUSY(dksc, pmask))
|
|
|
|
return EBUSY;
|
2010-01-13 00:08:08 +03:00
|
|
|
|
2010-01-20 21:55:17 +03:00
|
|
|
if ((dksc->sc_flags & DKF_INITED) != 0 &&
|
|
|
|
(ret = cgd_ioctl_clr(sc, curlwp)) != 0)
|
|
|
|
return ret;
|
2010-01-13 00:08:08 +03:00
|
|
|
|
2010-01-20 21:55:17 +03:00
|
|
|
disk_destroy(&dksc->sc_dkdev);
|
2014-05-25 23:15:50 +04:00
|
|
|
mutex_destroy(&sc->sc_lock);
|
2010-01-20 21:55:17 +03:00
|
|
|
|
|
|
|
return 0;
|
2002-10-04 22:22:35 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cgdattach(int num)
|
|
|
|
{
|
2010-01-13 00:08:08 +03:00
|
|
|
int error;
|
2002-10-04 22:22:35 +04:00
|
|
|
|
2010-01-13 00:08:08 +03:00
|
|
|
error = config_cfattach_attach(cgd_cd.cd_name, &cgd_ca);
|
|
|
|
if (error != 0)
|
|
|
|
aprint_error("%s: unable to register cfattach\n",
|
|
|
|
cgd_cd.cd_name);
|
|
|
|
}
|
2002-10-04 22:22:35 +04:00
|
|
|
|
2010-01-13 00:08:08 +03:00
|
|
|
static struct cgd_softc *
|
|
|
|
cgd_spawn(int unit)
|
|
|
|
{
|
|
|
|
cfdata_t cf;
|
2002-10-04 22:22:35 +04:00
|
|
|
|
2010-01-13 00:08:08 +03:00
|
|
|
cf = malloc(sizeof(*cf), M_DEVBUF, M_WAITOK);
|
|
|
|
cf->cf_name = cgd_cd.cd_name;
|
|
|
|
cf->cf_atname = cgd_cd.cd_name;
|
|
|
|
cf->cf_unit = unit;
|
|
|
|
cf->cf_fstate = FSTATE_STAR;
|
|
|
|
|
|
|
|
return device_private(config_attach_pseudo(cf));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
cgd_destroy(device_t dev)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
cfdata_t cf;
|
|
|
|
|
|
|
|
cf = device_cfdata(dev);
|
|
|
|
error = config_detach(dev, DETACH_QUIET);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
free(cf, M_DEVBUF);
|
|
|
|
return 0;
|
2002-10-04 22:22:35 +04:00
|
|
|
}
|
|
|
|
|
2004-08-23 09:37:42 +04:00
|
|
|
static int
|
2005-12-11 15:16:03 +03:00
|
|
|
cgdopen(dev_t dev, int flags, int fmt, struct lwp *l)
|
2002-10-04 22:22:35 +04:00
|
|
|
{
|
|
|
|
struct cgd_softc *cs;
|
|
|
|
|
2009-01-11 12:51:38 +03:00
|
|
|
DPRINTF_FOLLOW(("cgdopen(0x%"PRIx64", %d)\n", dev, flags));
|
2002-10-04 22:22:35 +04:00
|
|
|
GETCGD_SOFTC(cs, dev);
|
2005-12-11 15:16:03 +03:00
|
|
|
return dk_open(di, &cs->sc_dksc, dev, flags, fmt, l);
|
2002-10-04 22:22:35 +04:00
|
|
|
}
|
|
|
|
|
2004-08-23 09:37:42 +04:00
|
|
|
static int
|
2005-12-11 15:16:03 +03:00
|
|
|
cgdclose(dev_t dev, int flags, int fmt, struct lwp *l)
|
2002-10-04 22:22:35 +04:00
|
|
|
{
|
2010-01-13 00:08:08 +03:00
|
|
|
int error;
|
2002-10-04 22:22:35 +04:00
|
|
|
struct cgd_softc *cs;
|
2010-01-13 00:08:08 +03:00
|
|
|
struct dk_softc *dksc;
|
2002-10-04 22:22:35 +04:00
|
|
|
|
2009-01-11 12:51:38 +03:00
|
|
|
DPRINTF_FOLLOW(("cgdclose(0x%"PRIx64", %d)\n", dev, flags));
|
2002-10-04 22:22:35 +04:00
|
|
|
GETCGD_SOFTC(cs, dev);
|
2010-01-13 00:08:08 +03:00
|
|
|
dksc = &cs->sc_dksc;
|
|
|
|
if ((error = dk_close(di, dksc, dev, flags, fmt, l)) != 0)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
if ((dksc->sc_flags & DKF_INITED) == 0) {
|
2012-05-25 14:53:46 +04:00
|
|
|
if ((error = cgd_destroy(cs->sc_dksc.sc_dev)) != 0) {
|
|
|
|
aprint_error_dev(dksc->sc_dev,
|
2010-01-13 00:08:08 +03:00
|
|
|
"unable to detach instance\n");
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
2002-10-04 22:22:35 +04:00
|
|
|
}
|
|
|
|
|
2004-08-23 09:37:42 +04:00
|
|
|
static void
|
2002-10-04 22:22:35 +04:00
|
|
|
cgdstrategy(struct buf *bp)
|
|
|
|
{
|
|
|
|
struct cgd_softc *cs = getcgd_softc(bp->b_dev);
|
|
|
|
|
|
|
|
DPRINTF_FOLLOW(("cgdstrategy(%p): b_bcount = %ld\n", bp,
|
|
|
|
(long)bp->b_bcount));
|
2011-05-20 00:34:13 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Reject unaligned writes. We can encrypt and decrypt only
|
|
|
|
* complete disk sectors, and we let the ciphers require their
|
|
|
|
* buffers to be aligned to 32-bit boundaries.
|
|
|
|
*/
|
|
|
|
if (bp->b_blkno < 0 ||
|
|
|
|
(bp->b_bcount % DEV_BSIZE) != 0 ||
|
|
|
|
((uintptr_t)bp->b_data & 3) != 0) {
|
|
|
|
bp->b_error = EINVAL;
|
|
|
|
bp->b_resid = bp->b_bcount;
|
|
|
|
biodone(bp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2002-10-04 22:22:35 +04:00
|
|
|
/* XXXrcd: Should we test for (cs != NULL)? */
|
|
|
|
dk_strategy(di, &cs->sc_dksc, bp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2004-08-23 09:37:42 +04:00
|
|
|
static int
|
2002-10-04 22:22:35 +04:00
|
|
|
cgdsize(dev_t dev)
|
|
|
|
{
|
|
|
|
struct cgd_softc *cs = getcgd_softc(dev);
|
|
|
|
|
2009-01-11 12:51:38 +03:00
|
|
|
DPRINTF_FOLLOW(("cgdsize(0x%"PRIx64")\n", dev));
|
2002-10-04 22:22:35 +04:00
|
|
|
if (!cs)
|
|
|
|
return -1;
|
|
|
|
return dk_size(di, &cs->sc_dksc, dev);
|
|
|
|
}
|
|
|
|
|
2004-03-28 03:23:06 +04:00
|
|
|
/*
|
|
|
|
* cgd_{get,put}data are functions that deal with getting a buffer
|
|
|
|
* for the new encrypted data. We have a buffer per device so that
|
|
|
|
* we can ensure that we can always have a transaction in flight.
|
|
|
|
* We use this buffer first so that we have one less piece of
|
|
|
|
* malloc'ed data at any given point.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void *
|
|
|
|
cgd_getdata(struct dk_softc *dksc, unsigned long size)
|
|
|
|
{
|
2012-05-25 14:53:46 +04:00
|
|
|
struct cgd_softc *cs = (struct cgd_softc *)dksc;
|
2007-03-04 08:59:00 +03:00
|
|
|
void * data = NULL;
|
2004-03-28 03:23:06 +04:00
|
|
|
|
2014-03-18 19:44:37 +04:00
|
|
|
mutex_enter(&cs->sc_lock);
|
2004-03-28 03:23:06 +04:00
|
|
|
if (cs->sc_data_used == 0) {
|
|
|
|
cs->sc_data_used = 1;
|
|
|
|
data = cs->sc_data;
|
|
|
|
}
|
2014-03-18 19:44:37 +04:00
|
|
|
mutex_exit(&cs->sc_lock);
|
2004-03-28 03:23:06 +04:00
|
|
|
|
|
|
|
if (data)
|
|
|
|
return data;
|
|
|
|
|
|
|
|
return malloc(size, M_DEVBUF, M_NOWAIT);
|
|
|
|
}
|
|
|
|
|
2002-10-04 22:22:35 +04:00
|
|
|
static void
|
2007-03-04 08:59:00 +03:00
|
|
|
cgd_putdata(struct dk_softc *dksc, void *data)
|
2004-03-28 03:23:06 +04:00
|
|
|
{
|
2012-05-25 14:53:46 +04:00
|
|
|
struct cgd_softc *cs = (struct cgd_softc *)dksc;
|
2004-03-28 03:23:06 +04:00
|
|
|
|
|
|
|
if (data == cs->sc_data) {
|
2014-03-18 19:44:37 +04:00
|
|
|
mutex_enter(&cs->sc_lock);
|
2004-03-28 03:23:06 +04:00
|
|
|
cs->sc_data_used = 0;
|
2014-03-18 19:44:37 +04:00
|
|
|
mutex_exit(&cs->sc_lock);
|
2004-03-28 03:23:06 +04:00
|
|
|
} else {
|
|
|
|
free(data, M_DEVBUF);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-25 23:23:49 +04:00
|
|
|
static void
|
|
|
|
cgdstart(struct dk_softc *dksc)
|
2002-10-04 22:22:35 +04:00
|
|
|
{
|
2012-05-25 14:53:46 +04:00
|
|
|
struct cgd_softc *cs = (struct cgd_softc *)dksc;
|
2014-05-25 23:23:49 +04:00
|
|
|
struct buf *bp, *nbp;
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
struct buf *qbp;
|
|
|
|
#endif
|
2007-03-04 08:59:00 +03:00
|
|
|
void * addr;
|
|
|
|
void * newaddr;
|
2002-10-04 22:22:35 +04:00
|
|
|
daddr_t bn;
|
2008-01-02 14:48:20 +03:00
|
|
|
struct vnode *vp;
|
2002-10-04 22:22:35 +04:00
|
|
|
|
2014-05-25 23:23:49 +04:00
|
|
|
while ((bp = bufq_peek(dksc->sc_bufq)) != NULL) {
|
2002-10-04 22:22:35 +04:00
|
|
|
|
2014-05-25 23:23:49 +04:00
|
|
|
DPRINTF_FOLLOW(("cgdstart(%p, %p)\n", dksc, bp));
|
|
|
|
disk_busy(&dksc->sc_dkdev);
|
2002-10-04 22:22:35 +04:00
|
|
|
|
2014-05-25 23:23:49 +04:00
|
|
|
bn = bp->b_rawblkno;
|
2004-03-28 03:23:06 +04:00
|
|
|
|
2014-05-25 23:23:49 +04:00
|
|
|
/*
|
|
|
|
* We attempt to allocate all of our resources up front, so that
|
|
|
|
* we can fail quickly if they are unavailable.
|
|
|
|
*/
|
|
|
|
nbp = getiobuf(cs->sc_tvn, false);
|
|
|
|
if (nbp == NULL) {
|
2004-03-28 03:23:06 +04:00
|
|
|
disk_unbusy(&dksc->sc_dkdev, 0, (bp->b_flags & B_READ));
|
2014-05-25 23:23:49 +04:00
|
|
|
break;
|
2004-03-28 03:23:06 +04:00
|
|
|
}
|
2002-10-04 22:22:35 +04:00
|
|
|
|
2014-05-25 23:23:49 +04:00
|
|
|
/*
|
|
|
|
* If we are writing, then we need to encrypt the outgoing
|
|
|
|
* block into a new block of memory.
|
|
|
|
*/
|
|
|
|
newaddr = addr = bp->b_data;
|
|
|
|
if ((bp->b_flags & B_READ) == 0) {
|
|
|
|
newaddr = cgd_getdata(dksc, bp->b_bcount);
|
|
|
|
if (!newaddr) {
|
|
|
|
putiobuf(nbp);
|
|
|
|
disk_unbusy(&dksc->sc_dkdev, 0, (bp->b_flags & B_READ));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
cgd_cipher(cs, newaddr, addr, bp->b_bcount, bn,
|
|
|
|
DEV_BSIZE, CGD_CIPHER_ENCRYPT);
|
|
|
|
}
|
|
|
|
/* we now have all needed resources to process this buf */
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
qbp = bufq_get(dksc->sc_bufq);
|
|
|
|
KASSERT(bp == qbp);
|
|
|
|
#else
|
|
|
|
(void)bufq_get(dksc->sc_bufq);
|
|
|
|
#endif
|
|
|
|
nbp->b_data = newaddr;
|
|
|
|
nbp->b_flags = bp->b_flags;
|
|
|
|
nbp->b_oflags = bp->b_oflags;
|
|
|
|
nbp->b_cflags = bp->b_cflags;
|
|
|
|
nbp->b_iodone = cgdiodone;
|
|
|
|
nbp->b_proc = bp->b_proc;
|
|
|
|
nbp->b_blkno = bn;
|
|
|
|
nbp->b_bcount = bp->b_bcount;
|
|
|
|
nbp->b_private = bp;
|
|
|
|
|
|
|
|
BIO_COPYPRIO(nbp, bp);
|
|
|
|
|
|
|
|
if ((nbp->b_flags & B_READ) == 0) {
|
|
|
|
vp = nbp->b_vp;
|
|
|
|
mutex_enter(vp->v_interlock);
|
|
|
|
vp->v_numoutput++;
|
|
|
|
mutex_exit(vp->v_interlock);
|
|
|
|
}
|
|
|
|
VOP_STRATEGY(cs->sc_tvn, nbp);
|
2004-07-19 17:46:23 +04:00
|
|
|
}
|
2002-10-04 22:22:35 +04:00
|
|
|
}
|
|
|
|
|
2004-08-23 09:37:42 +04:00
|
|
|
static void
|
2004-07-19 17:46:23 +04:00
|
|
|
cgdiodone(struct buf *nbp)
|
2002-10-04 22:22:35 +04:00
|
|
|
{
|
2004-07-19 17:46:23 +04:00
|
|
|
struct buf *obp = nbp->b_private;
|
|
|
|
struct cgd_softc *cs = getcgd_softc(obp->b_dev);
|
2002-10-04 22:22:35 +04:00
|
|
|
struct dk_softc *dksc = &cs->sc_dksc;
|
2010-01-23 21:31:04 +03:00
|
|
|
int s;
|
2005-02-27 03:26:58 +03:00
|
|
|
|
2004-07-19 17:46:23 +04:00
|
|
|
KDASSERT(cs);
|
2002-10-04 22:22:35 +04:00
|
|
|
|
2004-07-19 17:46:23 +04:00
|
|
|
DPRINTF_FOLLOW(("cgdiodone(%p)\n", nbp));
|
2004-10-04 15:12:09 +04:00
|
|
|
DPRINTF(CGDB_IO, ("cgdiodone: bp %p bcount %d resid %d\n",
|
2002-10-04 22:22:35 +04:00
|
|
|
obp, obp->b_bcount, obp->b_resid));
|
2009-01-11 12:51:38 +03:00
|
|
|
DPRINTF(CGDB_IO, (" dev 0x%"PRIx64", nbp %p bn %" PRId64 " addr %p bcnt %d\n",
|
2004-07-19 17:46:23 +04:00
|
|
|
nbp->b_dev, nbp, nbp->b_blkno, nbp->b_data,
|
|
|
|
nbp->b_bcount));
|
2007-07-29 16:50:17 +04:00
|
|
|
if (nbp->b_error != 0) {
|
|
|
|
obp->b_error = nbp->b_error;
|
2009-11-10 23:05:50 +03:00
|
|
|
DPRINTF(CGDB_IO, ("%s: error %d\n", dksc->sc_xname,
|
|
|
|
obp->b_error));
|
2002-10-04 22:22:35 +04:00
|
|
|
}
|
|
|
|
|
2004-03-28 03:23:06 +04:00
|
|
|
/* Perform the decryption if we are reading.
|
2002-10-04 22:22:35 +04:00
|
|
|
*
|
|
|
|
* Note: use the blocknumber from nbp, since it is what
|
|
|
|
* we used to encrypt the blocks.
|
|
|
|
*/
|
|
|
|
|
2004-03-28 03:23:06 +04:00
|
|
|
if (nbp->b_flags & B_READ)
|
2002-10-04 22:22:35 +04:00
|
|
|
cgd_cipher(cs, obp->b_data, obp->b_data, obp->b_bcount,
|
|
|
|
nbp->b_blkno, DEV_BSIZE, CGD_CIPHER_DECRYPT);
|
|
|
|
|
2004-03-28 03:23:06 +04:00
|
|
|
/* If we allocated memory, free it now... */
|
2002-10-04 22:22:35 +04:00
|
|
|
if (nbp->b_data != obp->b_data)
|
2004-03-28 03:23:06 +04:00
|
|
|
cgd_putdata(dksc, nbp->b_data);
|
2002-10-04 22:22:35 +04:00
|
|
|
|
2006-01-04 13:13:05 +03:00
|
|
|
putiobuf(nbp);
|
2002-10-04 22:22:35 +04:00
|
|
|
|
|
|
|
/* Request is complete for whatever reason */
|
|
|
|
obp->b_resid = 0;
|
2007-07-29 16:50:17 +04:00
|
|
|
if (obp->b_error != 0)
|
2002-10-04 22:22:35 +04:00
|
|
|
obp->b_resid = obp->b_bcount;
|
2010-01-23 21:31:04 +03:00
|
|
|
s = splbio();
|
2002-11-01 14:31:50 +03:00
|
|
|
disk_unbusy(&dksc->sc_dkdev, obp->b_bcount - obp->b_resid,
|
|
|
|
(obp->b_flags & B_READ));
|
2002-10-04 22:22:35 +04:00
|
|
|
biodone(obp);
|
2014-05-25 23:23:49 +04:00
|
|
|
cgdstart(dksc);
|
2010-01-23 21:31:04 +03:00
|
|
|
splx(s);
|
2002-10-04 22:22:35 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* XXX: we should probably put these into dksubr.c, mostly */
|
2004-08-23 09:37:42 +04:00
|
|
|
static int
|
2006-11-16 04:32:37 +03:00
|
|
|
cgdread(dev_t dev, struct uio *uio, int flags)
|
2002-10-04 22:22:35 +04:00
|
|
|
{
|
|
|
|
struct cgd_softc *cs;
|
|
|
|
struct dk_softc *dksc;
|
|
|
|
|
2009-01-11 12:51:38 +03:00
|
|
|
DPRINTF_FOLLOW(("cgdread(0x%llx, %p, %d)\n",
|
|
|
|
(unsigned long long)dev, uio, flags));
|
2002-10-04 22:22:35 +04:00
|
|
|
GETCGD_SOFTC(cs, dev);
|
|
|
|
dksc = &cs->sc_dksc;
|
|
|
|
if ((dksc->sc_flags & DKF_INITED) == 0)
|
|
|
|
return ENXIO;
|
|
|
|
return physio(cgdstrategy, NULL, dev, B_READ, minphys, uio);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* XXX: we should probably put these into dksubr.c, mostly */
|
2004-08-23 09:37:42 +04:00
|
|
|
static int
|
2006-11-16 04:32:37 +03:00
|
|
|
cgdwrite(dev_t dev, struct uio *uio, int flags)
|
2002-10-04 22:22:35 +04:00
|
|
|
{
|
|
|
|
struct cgd_softc *cs;
|
|
|
|
struct dk_softc *dksc;
|
|
|
|
|
2009-01-11 12:51:38 +03:00
|
|
|
DPRINTF_FOLLOW(("cgdwrite(0x%"PRIx64", %p, %d)\n", dev, uio, flags));
|
2002-10-04 22:22:35 +04:00
|
|
|
GETCGD_SOFTC(cs, dev);
|
|
|
|
dksc = &cs->sc_dksc;
|
|
|
|
if ((dksc->sc_flags & DKF_INITED) == 0)
|
|
|
|
return ENXIO;
|
|
|
|
return physio(cgdstrategy, NULL, dev, B_WRITE, minphys, uio);
|
|
|
|
}
|
|
|
|
|
2004-08-23 09:37:42 +04:00
|
|
|
static int
|
2007-03-04 08:59:00 +03:00
|
|
|
cgdioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
|
2002-10-04 22:22:35 +04:00
|
|
|
{
|
|
|
|
struct cgd_softc *cs;
|
|
|
|
struct dk_softc *dksc;
|
|
|
|
int part = DISKPART(dev);
|
|
|
|
int pmask = 1 << part;
|
|
|
|
|
2009-01-11 12:51:38 +03:00
|
|
|
DPRINTF_FOLLOW(("cgdioctl(0x%"PRIx64", %ld, %p, %d, %p)\n",
|
2005-12-11 15:16:03 +03:00
|
|
|
dev, cmd, data, flag, l));
|
2012-12-05 06:23:20 +04:00
|
|
|
|
2002-10-04 22:22:35 +04:00
|
|
|
switch (cmd) {
|
2014-12-30 23:18:44 +03:00
|
|
|
case CGDIOCGET:
|
|
|
|
return cgd_ioctl_get(dev, data, l);
|
2002-10-04 22:22:35 +04:00
|
|
|
case CGDIOCSET:
|
|
|
|
case CGDIOCCLR:
|
|
|
|
if ((flag & FWRITE) == 0)
|
|
|
|
return EBADF;
|
2012-12-05 06:23:20 +04:00
|
|
|
/* FALLTHROUGH */
|
|
|
|
default:
|
|
|
|
GETCGD_SOFTC(cs, dev);
|
|
|
|
dksc = &cs->sc_dksc;
|
|
|
|
break;
|
2002-10-04 22:22:35 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case CGDIOCSET:
|
|
|
|
if (dksc->sc_flags & DKF_INITED)
|
2010-01-20 22:00:47 +03:00
|
|
|
return EBUSY;
|
|
|
|
return cgd_ioctl_set(cs, data, l);
|
2002-10-04 22:22:35 +04:00
|
|
|
case CGDIOCCLR:
|
2010-01-13 00:08:08 +03:00
|
|
|
if (DK_BUSY(&cs->sc_dksc, pmask))
|
2010-01-20 22:00:47 +03:00
|
|
|
return EBUSY;
|
|
|
|
return cgd_ioctl_clr(cs, l);
|
2009-03-14 20:56:47 +03:00
|
|
|
case DIOCCACHESYNC:
|
|
|
|
/*
|
|
|
|
* XXX Do we really need to care about having a writable
|
|
|
|
* file descriptor here?
|
|
|
|
*/
|
|
|
|
if ((flag & FWRITE) == 0)
|
|
|
|
return (EBADF);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We pass this call down to the underlying disk.
|
|
|
|
*/
|
2010-01-20 22:00:47 +03:00
|
|
|
return VOP_IOCTL(cs->sc_tvn, cmd, data, flag, l->l_cred);
|
2002-10-04 22:22:35 +04:00
|
|
|
default:
|
2010-01-20 22:00:47 +03:00
|
|
|
return dk_ioctl(di, dksc, dev, cmd, data, flag, l);
|
2014-12-30 23:18:44 +03:00
|
|
|
case CGDIOCGET:
|
|
|
|
KASSERT(0);
|
|
|
|
return EINVAL;
|
2002-10-04 22:22:35 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-08-23 09:37:42 +04:00
|
|
|
static int
|
2007-03-04 08:59:00 +03:00
|
|
|
cgddump(dev_t dev, daddr_t blkno, void *va, size_t size)
|
2002-10-04 22:22:35 +04:00
|
|
|
{
|
|
|
|
struct cgd_softc *cs;
|
|
|
|
|
2009-01-11 12:51:38 +03:00
|
|
|
DPRINTF_FOLLOW(("cgddump(0x%"PRIx64", %" PRId64 ", %p, %lu)\n",
|
|
|
|
dev, blkno, va, (unsigned long)size));
|
2002-10-04 22:22:35 +04:00
|
|
|
GETCGD_SOFTC(cs, dev);
|
|
|
|
return dk_dump(di, &cs->sc_dksc, dev, blkno, va, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXXrcd:
|
|
|
|
* for now we hardcode the maximum key length.
|
|
|
|
*/
|
|
|
|
#define MAX_KEYSIZE 1024
|
|
|
|
|
2008-09-12 20:51:54 +04:00
|
|
|
static const struct {
|
|
|
|
const char *n;
|
|
|
|
int v;
|
|
|
|
int d;
|
|
|
|
} encblkno[] = {
|
|
|
|
{ "encblkno", CGD_CIPHER_CBC_ENCBLKNO8, 1 },
|
|
|
|
{ "encblkno8", CGD_CIPHER_CBC_ENCBLKNO8, 1 },
|
|
|
|
{ "encblkno1", CGD_CIPHER_CBC_ENCBLKNO1, 8 },
|
|
|
|
};
|
|
|
|
|
2002-10-04 22:22:35 +04:00
|
|
|
/* ARGSUSED */
|
|
|
|
static int
|
2005-12-11 15:16:03 +03:00
|
|
|
cgd_ioctl_set(struct cgd_softc *cs, void *data, struct lwp *l)
|
2002-10-04 22:22:35 +04:00
|
|
|
{
|
|
|
|
struct cgd_ioctl *ci = data;
|
|
|
|
struct vnode *vp;
|
|
|
|
int ret;
|
2008-09-12 20:51:54 +04:00
|
|
|
size_t i;
|
2007-01-19 21:59:59 +03:00
|
|
|
size_t keybytes; /* key length in bytes */
|
2005-06-29 00:23:02 +04:00
|
|
|
const char *cp;
|
2010-11-19 09:44:33 +03:00
|
|
|
struct pathbuf *pb;
|
2006-06-20 07:20:44 +04:00
|
|
|
char *inbuf;
|
2013-05-30 03:25:39 +04:00
|
|
|
struct dk_softc *dksc = &cs->sc_dksc;
|
2002-10-04 22:22:35 +04:00
|
|
|
|
|
|
|
cp = ci->ci_disk;
|
2010-11-19 09:44:33 +03:00
|
|
|
|
|
|
|
ret = pathbuf_copyin(ci->ci_disk, &pb);
|
|
|
|
if (ret != 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
ret = dk_lookup(pb, l, &vp);
|
|
|
|
pathbuf_destroy(pb);
|
|
|
|
if (ret != 0) {
|
2002-10-04 22:22:35 +04:00
|
|
|
return ret;
|
2010-11-19 09:44:33 +03:00
|
|
|
}
|
2002-10-04 22:22:35 +04:00
|
|
|
|
2006-06-20 07:20:44 +04:00
|
|
|
inbuf = malloc(MAX_KEYSIZE, M_TEMP, M_WAITOK);
|
|
|
|
|
2005-12-11 15:16:03 +03:00
|
|
|
if ((ret = cgdinit(cs, cp, vp, l)) != 0)
|
2002-10-04 22:22:35 +04:00
|
|
|
goto bail;
|
|
|
|
|
2006-06-20 07:20:44 +04:00
|
|
|
(void)memset(inbuf, 0, MAX_KEYSIZE);
|
2002-10-04 22:22:35 +04:00
|
|
|
ret = copyinstr(ci->ci_alg, inbuf, 256, NULL);
|
|
|
|
if (ret)
|
|
|
|
goto bail;
|
|
|
|
cs->sc_cfuncs = cryptfuncs_find(inbuf);
|
|
|
|
if (!cs->sc_cfuncs) {
|
|
|
|
ret = EINVAL;
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
2007-01-19 21:59:59 +03:00
|
|
|
(void)memset(inbuf, 0, MAX_KEYSIZE);
|
2006-06-20 07:20:44 +04:00
|
|
|
ret = copyinstr(ci->ci_ivmethod, inbuf, MAX_KEYSIZE, NULL);
|
2002-10-04 22:22:35 +04:00
|
|
|
if (ret)
|
|
|
|
goto bail;
|
2008-09-12 20:51:54 +04:00
|
|
|
|
|
|
|
for (i = 0; i < __arraycount(encblkno); i++)
|
|
|
|
if (strcmp(encblkno[i].n, inbuf) == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (i == __arraycount(encblkno)) {
|
2002-10-04 22:22:35 +04:00
|
|
|
ret = EINVAL;
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
Fix a longstanding bug in key-handling for the blowfish cipher.
This is an incompatible change, and will break all existing cgd images
encrypted with blowfish. Users will need to dump their data before
booting a kernel with this change, and recreate cgd's and restore data
afterwards.
I believe this affects a very small number of users other than myself;
indeed after several alert mails in an attempt to find them, only 2
such users have come forward. They have both agreed the requirement
for backwards compatibility does not warrant the effort nor the mess
in the code. This code does exist, if it should later prove to be
needed, but will not be in the tree.
Further, by the nature of the issue, I have strong reasons to believe
that, even if they missed these mails, there would be few other users
of blowfish who update their systems with any regularity; any such
users would have tripped over the problem in the same way I did when
it was first found over a year ago.
The problem stems from two issues with the underlying blowfish
encryption routines used by cgd:
- they take key length arguments counted in bytes, rather than bits
like all the opther ciphers.
- they silently truncate any keys longer than an internal limit,
rather than returning an error (which would have exposed the
previous discrepancy immediately).
As a result, the kernel reads too much data as the key from cgdconfig,
and then truncates most of it. This can easily be demonstrated/tested.
Currently, Blowfish users will find that if they mis-enter the cgd
passphrase on the first attempt, when validation fails and cgdconfig
prompts for the passphrase again, the cgd will not correctly configure
even when given a correct passphrase.
2004-03-18 13:42:08 +03:00
|
|
|
keybytes = ci->ci_keylen / 8 + 1;
|
|
|
|
if (keybytes > MAX_KEYSIZE) {
|
2002-10-04 22:22:35 +04:00
|
|
|
ret = EINVAL;
|
|
|
|
goto bail;
|
|
|
|
}
|
2008-09-12 20:51:54 +04:00
|
|
|
|
2006-06-20 07:20:44 +04:00
|
|
|
(void)memset(inbuf, 0, MAX_KEYSIZE);
|
Fix a longstanding bug in key-handling for the blowfish cipher.
This is an incompatible change, and will break all existing cgd images
encrypted with blowfish. Users will need to dump their data before
booting a kernel with this change, and recreate cgd's and restore data
afterwards.
I believe this affects a very small number of users other than myself;
indeed after several alert mails in an attempt to find them, only 2
such users have come forward. They have both agreed the requirement
for backwards compatibility does not warrant the effort nor the mess
in the code. This code does exist, if it should later prove to be
needed, but will not be in the tree.
Further, by the nature of the issue, I have strong reasons to believe
that, even if they missed these mails, there would be few other users
of blowfish who update their systems with any regularity; any such
users would have tripped over the problem in the same way I did when
it was first found over a year ago.
The problem stems from two issues with the underlying blowfish
encryption routines used by cgd:
- they take key length arguments counted in bytes, rather than bits
like all the opther ciphers.
- they silently truncate any keys longer than an internal limit,
rather than returning an error (which would have exposed the
previous discrepancy immediately).
As a result, the kernel reads too much data as the key from cgdconfig,
and then truncates most of it. This can easily be demonstrated/tested.
Currently, Blowfish users will find that if they mis-enter the cgd
passphrase on the first attempt, when validation fails and cgdconfig
prompts for the passphrase again, the cgd will not correctly configure
even when given a correct passphrase.
2004-03-18 13:42:08 +03:00
|
|
|
ret = copyin(ci->ci_key, inbuf, keybytes);
|
2002-10-04 22:22:35 +04:00
|
|
|
if (ret)
|
|
|
|
goto bail;
|
|
|
|
|
|
|
|
cs->sc_cdata.cf_blocksize = ci->ci_blocksize;
|
2008-09-12 20:51:54 +04:00
|
|
|
cs->sc_cdata.cf_mode = encblkno[i].v;
|
2012-12-05 06:23:20 +04:00
|
|
|
cs->sc_cdata.cf_keylen = ci->ci_keylen;
|
2002-10-04 22:22:35 +04:00
|
|
|
cs->sc_cdata.cf_priv = cs->sc_cfuncs->cf_init(ci->ci_keylen, inbuf,
|
|
|
|
&cs->sc_cdata.cf_blocksize);
|
2009-11-10 23:05:50 +03:00
|
|
|
if (cs->sc_cdata.cf_blocksize > CGD_MAXBLOCKSIZE) {
|
|
|
|
log(LOG_WARNING, "cgd: Disallowed cipher with blocksize %zu > %u\n",
|
2009-11-10 23:24:30 +03:00
|
|
|
cs->sc_cdata.cf_blocksize, CGD_MAXBLOCKSIZE);
|
2009-11-10 23:05:50 +03:00
|
|
|
cs->sc_cdata.cf_priv = NULL;
|
|
|
|
}
|
2012-12-05 06:23:20 +04:00
|
|
|
|
2008-09-12 20:51:54 +04:00
|
|
|
/*
|
|
|
|
* The blocksize is supposed to be in bytes. Unfortunately originally
|
|
|
|
* it was expressed in bits. For compatibility we maintain encblkno
|
|
|
|
* and encblkno8.
|
|
|
|
*/
|
|
|
|
cs->sc_cdata.cf_blocksize /= encblkno[i].d;
|
2006-06-20 07:20:44 +04:00
|
|
|
(void)memset(inbuf, 0, MAX_KEYSIZE);
|
2002-10-04 22:22:35 +04:00
|
|
|
if (!cs->sc_cdata.cf_priv) {
|
|
|
|
ret = EINVAL; /* XXX is this the right error? */
|
|
|
|
goto bail;
|
|
|
|
}
|
2006-06-20 07:20:44 +04:00
|
|
|
free(inbuf, M_TEMP);
|
2002-10-04 22:22:35 +04:00
|
|
|
|
2013-05-30 03:25:39 +04:00
|
|
|
bufq_alloc(&dksc->sc_bufq, "fcfs", 0);
|
2004-03-28 03:23:06 +04:00
|
|
|
|
|
|
|
cs->sc_data = malloc(MAXPHYS, M_DEVBUF, M_WAITOK);
|
|
|
|
cs->sc_data_used = 0;
|
|
|
|
|
2013-05-30 03:25:39 +04:00
|
|
|
dksc->sc_flags |= DKF_INITED;
|
2002-10-04 22:22:35 +04:00
|
|
|
|
2013-05-30 03:25:39 +04:00
|
|
|
disk_set_info(dksc->sc_dev, &dksc->sc_dkdev, NULL);
|
2012-05-25 14:53:46 +04:00
|
|
|
|
2002-10-04 22:22:35 +04:00
|
|
|
/* Attach the disk. */
|
2013-05-30 03:25:39 +04:00
|
|
|
disk_attach(&dksc->sc_dkdev);
|
2002-10-04 22:22:35 +04:00
|
|
|
|
|
|
|
/* Try and read the disklabel. */
|
2013-05-30 03:25:39 +04:00
|
|
|
dk_getdisklabel(di, dksc, 0 /* XXX ? (cause of PR 41704) */);
|
2002-10-04 22:22:35 +04:00
|
|
|
|
2005-08-20 16:03:52 +04:00
|
|
|
/* Discover wedges on this disk. */
|
2013-05-30 03:25:39 +04:00
|
|
|
dkwedge_discover(&dksc->sc_dkdev);
|
2005-08-20 16:03:52 +04:00
|
|
|
|
2002-10-04 22:22:35 +04:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
bail:
|
2006-06-20 07:20:44 +04:00
|
|
|
free(inbuf, M_TEMP);
|
2008-03-22 00:54:58 +03:00
|
|
|
(void)vn_close(vp, FREAD|FWRITE, l->l_cred);
|
2002-10-04 22:22:35 +04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ARGSUSED */
|
|
|
|
static int
|
2010-01-13 00:08:08 +03:00
|
|
|
cgd_ioctl_clr(struct cgd_softc *cs, struct lwp *l)
|
2002-10-04 22:22:35 +04:00
|
|
|
{
|
2004-03-28 03:23:06 +04:00
|
|
|
int s;
|
2013-05-30 03:25:39 +04:00
|
|
|
struct dk_softc *dksc = &cs->sc_dksc;
|
2010-01-13 00:08:08 +03:00
|
|
|
|
|
|
|
if ((dksc->sc_flags & DKF_INITED) == 0)
|
|
|
|
return ENXIO;
|
2004-03-28 03:23:06 +04:00
|
|
|
|
2005-08-20 16:03:52 +04:00
|
|
|
/* Delete all of our wedges. */
|
2013-05-30 03:25:39 +04:00
|
|
|
dkwedge_delall(&dksc->sc_dkdev);
|
2005-08-20 16:03:52 +04:00
|
|
|
|
2004-03-28 03:23:06 +04:00
|
|
|
/* Kill off any queued buffers. */
|
|
|
|
s = splbio();
|
2013-05-30 03:25:39 +04:00
|
|
|
bufq_drain(dksc->sc_bufq);
|
2004-03-28 03:23:06 +04:00
|
|
|
splx(s);
|
2013-05-30 03:25:39 +04:00
|
|
|
bufq_free(dksc->sc_bufq);
|
2002-10-04 22:22:35 +04:00
|
|
|
|
2008-03-22 00:54:58 +03:00
|
|
|
(void)vn_close(cs->sc_tvn, FREAD|FWRITE, l->l_cred);
|
2002-10-04 22:22:35 +04:00
|
|
|
cs->sc_cfuncs->cf_destroy(cs->sc_cdata.cf_priv);
|
|
|
|
free(cs->sc_tpath, M_DEVBUF);
|
2004-03-28 03:23:06 +04:00
|
|
|
free(cs->sc_data, M_DEVBUF);
|
|
|
|
cs->sc_data_used = 0;
|
2013-05-30 03:25:39 +04:00
|
|
|
dksc->sc_flags &= ~DKF_INITED;
|
|
|
|
disk_detach(&dksc->sc_dkdev);
|
2002-10-04 22:22:35 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-05 06:23:20 +04:00
|
|
|
static int
|
|
|
|
cgd_ioctl_get(dev_t dev, void *data, struct lwp *l)
|
|
|
|
{
|
2013-05-30 12:28:13 +04:00
|
|
|
struct cgd_softc *cs = getcgd_softc(dev);
|
2012-12-05 06:23:20 +04:00
|
|
|
struct cgd_user *cgu;
|
|
|
|
int unit;
|
2013-05-30 03:25:39 +04:00
|
|
|
struct dk_softc *dksc = &cs->sc_dksc;
|
2012-12-05 06:23:20 +04:00
|
|
|
|
|
|
|
unit = CGDUNIT(dev);
|
|
|
|
cgu = (struct cgd_user *)data;
|
|
|
|
|
|
|
|
DPRINTF_FOLLOW(("cgd_ioctl_get(0x%"PRIx64", %d, %p, %p)\n",
|
|
|
|
dev, unit, data, l));
|
|
|
|
|
|
|
|
if (cgu->cgu_unit == -1)
|
|
|
|
cgu->cgu_unit = unit;
|
|
|
|
|
|
|
|
if (cgu->cgu_unit < 0)
|
|
|
|
return EINVAL; /* XXX: should this be ENXIO? */
|
|
|
|
|
|
|
|
cs = device_lookup_private(&cgd_cd, unit);
|
2013-05-30 03:25:39 +04:00
|
|
|
if (cs == NULL || (dksc->sc_flags & DKF_INITED) == 0) {
|
2012-12-05 06:23:20 +04:00
|
|
|
cgu->cgu_dev = 0;
|
|
|
|
cgu->cgu_alg[0] = '\0';
|
|
|
|
cgu->cgu_blocksize = 0;
|
|
|
|
cgu->cgu_mode = 0;
|
|
|
|
cgu->cgu_keylen = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
cgu->cgu_dev = cs->sc_tdev;
|
|
|
|
strlcpy(cgu->cgu_alg, cs->sc_cfuncs->cf_name,
|
|
|
|
sizeof(cgu->cgu_alg));
|
|
|
|
cgu->cgu_blocksize = cs->sc_cdata.cf_blocksize;
|
|
|
|
cgu->cgu_mode = cs->sc_cdata.cf_mode;
|
|
|
|
cgu->cgu_keylen = cs->sc_cdata.cf_keylen;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-10-04 22:22:35 +04:00
|
|
|
static int
|
2005-06-29 00:23:02 +04:00
|
|
|
cgdinit(struct cgd_softc *cs, const char *cpath, struct vnode *vp,
|
2005-12-11 15:16:03 +03:00
|
|
|
struct lwp *l)
|
2002-10-04 22:22:35 +04:00
|
|
|
{
|
2013-05-30 03:25:39 +04:00
|
|
|
struct disk_geom *dg;
|
2002-10-04 22:22:35 +04:00
|
|
|
int ret;
|
2006-06-20 07:20:44 +04:00
|
|
|
char *tmppath;
|
2011-11-14 03:03:24 +04:00
|
|
|
uint64_t psize;
|
|
|
|
unsigned secsize;
|
2013-05-30 03:25:39 +04:00
|
|
|
struct dk_softc *dksc = &cs->sc_dksc;
|
2002-10-04 22:22:35 +04:00
|
|
|
|
|
|
|
cs->sc_tvn = vp;
|
2006-06-20 07:20:44 +04:00
|
|
|
cs->sc_tpath = NULL;
|
2002-10-04 22:22:35 +04:00
|
|
|
|
2006-06-20 07:20:44 +04:00
|
|
|
tmppath = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
|
2002-10-04 22:22:35 +04:00
|
|
|
ret = copyinstr(cpath, tmppath, MAXPATHLEN, &cs->sc_tpathlen);
|
|
|
|
if (ret)
|
|
|
|
goto bail;
|
|
|
|
cs->sc_tpath = malloc(cs->sc_tpathlen, M_DEVBUF, M_WAITOK);
|
|
|
|
memcpy(cs->sc_tpath, tmppath, cs->sc_tpathlen);
|
|
|
|
|
2014-06-14 11:39:00 +04:00
|
|
|
cs->sc_tdev = vp->v_rdev;
|
2002-10-04 22:22:35 +04:00
|
|
|
|
2011-11-14 03:03:24 +04:00
|
|
|
if ((ret = getdisksize(vp, &psize, &secsize)) != 0)
|
2002-10-04 22:22:35 +04:00
|
|
|
goto bail;
|
|
|
|
|
2011-11-14 03:03:24 +04:00
|
|
|
if (psize == 0) {
|
2002-10-04 22:22:35 +04:00
|
|
|
ret = ENODEV;
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX here we should probe the underlying device. If we
|
|
|
|
* are accessing a partition of type RAW_PART, then
|
|
|
|
* we should populate our initial geometry with the
|
|
|
|
* geometry that we discover from the device.
|
|
|
|
*/
|
2013-05-30 03:25:39 +04:00
|
|
|
dg = &dksc->sc_dkdev.dk_geom;
|
|
|
|
memset(dg, 0, sizeof(*dg));
|
|
|
|
dg->dg_secperunit = psize;
|
|
|
|
// XXX: Inherit?
|
|
|
|
dg->dg_secsize = DEV_BSIZE;
|
|
|
|
dg->dg_ntracks = 1;
|
|
|
|
dg->dg_nsectors = 1024 * (1024 / dg->dg_secsize);
|
|
|
|
dg->dg_ncylinders = dg->dg_secperunit / dg->dg_nsectors;
|
2002-10-04 22:22:35 +04:00
|
|
|
|
|
|
|
bail:
|
2006-06-20 07:20:44 +04:00
|
|
|
free(tmppath, M_TEMP);
|
2002-10-04 22:22:35 +04:00
|
|
|
if (ret && cs->sc_tpath)
|
|
|
|
free(cs->sc_tpath, M_DEVBUF);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Our generic cipher entry point. This takes care of the
|
|
|
|
* IV mode and passes off the work to the specific cipher.
|
|
|
|
* We implement here the IV method ``encrypted block
|
|
|
|
* number''.
|
2005-02-27 03:26:58 +03:00
|
|
|
*
|
2002-10-04 22:22:35 +04:00
|
|
|
* For the encryption case, we accomplish this by setting
|
|
|
|
* up a struct uio where the first iovec of the source is
|
|
|
|
* the blocknumber and the first iovec of the dest is a
|
|
|
|
* sink. We then call the cipher with an IV of zero, and
|
|
|
|
* the right thing happens.
|
2005-02-27 03:26:58 +03:00
|
|
|
*
|
2002-10-04 22:22:35 +04:00
|
|
|
* For the decryption case, we use the same basic mechanism
|
|
|
|
* for symmetry, but we encrypt the block number in the
|
|
|
|
* first iovec.
|
|
|
|
*
|
|
|
|
* We mainly do this to avoid requiring the definition of
|
|
|
|
* an ECB mode.
|
|
|
|
*
|
|
|
|
* XXXrcd: for now we rely on our own crypto framework defined
|
|
|
|
* in dev/cgd_crypto.c. This will change when we
|
|
|
|
* get a generic kernel crypto framework.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
2005-05-31 06:50:59 +04:00
|
|
|
blkno2blkno_buf(char *sbuf, daddr_t blkno)
|
2002-10-04 22:22:35 +04:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Set up the blkno in blkno_buf, here we do not care much
|
|
|
|
* about the final layout of the information as long as we
|
|
|
|
* can guarantee that each sector will have a different IV
|
|
|
|
* and that the endianness of the machine will not affect
|
|
|
|
* the representation that we have chosen.
|
|
|
|
*
|
|
|
|
* We choose this representation, because it does not rely
|
|
|
|
* on the size of buf (which is the blocksize of the cipher),
|
|
|
|
* but allows daddr_t to grow without breaking existing
|
|
|
|
* disks.
|
|
|
|
*
|
|
|
|
* Note that blkno2blkno_buf does not take a size as input,
|
|
|
|
* and hence must be called on a pre-zeroed buffer of length
|
|
|
|
* greater than or equal to sizeof(daddr_t).
|
|
|
|
*/
|
|
|
|
for (i=0; i < sizeof(daddr_t); i++) {
|
2005-05-31 06:50:59 +04:00
|
|
|
*sbuf++ = blkno & 0xff;
|
2002-10-04 22:22:35 +04:00
|
|
|
blkno >>= 8;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2007-03-04 08:59:00 +03:00
|
|
|
cgd_cipher(struct cgd_softc *cs, void *dstv, void *srcv,
|
|
|
|
size_t len, daddr_t blkno, size_t secsize, int dir)
|
2002-10-04 22:22:35 +04:00
|
|
|
{
|
2007-03-04 08:59:00 +03:00
|
|
|
char *dst = dstv;
|
|
|
|
char *src = srcv;
|
2002-10-04 22:22:35 +04:00
|
|
|
cfunc_cipher *cipher = cs->sc_cfuncs->cf_cipher;
|
|
|
|
struct uio dstuio;
|
|
|
|
struct uio srcuio;
|
|
|
|
struct iovec dstiov[2];
|
|
|
|
struct iovec srciov[2];
|
2006-12-01 18:52:55 +03:00
|
|
|
size_t blocksize = cs->sc_cdata.cf_blocksize;
|
2009-11-10 23:05:50 +03:00
|
|
|
char sink[CGD_MAXBLOCKSIZE];
|
|
|
|
char zero_iv[CGD_MAXBLOCKSIZE];
|
|
|
|
char blkno_buf[CGD_MAXBLOCKSIZE];
|
2002-10-04 22:22:35 +04:00
|
|
|
|
|
|
|
DPRINTF_FOLLOW(("cgd_cipher() dir=%d\n", dir));
|
|
|
|
|
2005-02-27 03:26:58 +03:00
|
|
|
DIAGCONDPANIC(len % blocksize != 0,
|
2002-10-04 22:22:35 +04:00
|
|
|
("cgd_cipher: len %% blocksize != 0"));
|
|
|
|
|
|
|
|
/* ensure that sizeof(daddr_t) <= blocksize (for encblkno IVing) */
|
|
|
|
DIAGCONDPANIC(sizeof(daddr_t) > blocksize,
|
|
|
|
("cgd_cipher: sizeof(daddr_t) > blocksize"));
|
|
|
|
|
2009-11-10 23:39:36 +03:00
|
|
|
memset(zero_iv, 0x0, blocksize);
|
2002-10-04 22:22:35 +04:00
|
|
|
|
|
|
|
dstuio.uio_iov = dstiov;
|
|
|
|
dstuio.uio_iovcnt = 2;
|
|
|
|
|
|
|
|
srcuio.uio_iov = srciov;
|
|
|
|
srcuio.uio_iovcnt = 2;
|
|
|
|
|
|
|
|
dstiov[0].iov_base = sink;
|
|
|
|
dstiov[0].iov_len = blocksize;
|
|
|
|
srciov[0].iov_base = blkno_buf;
|
|
|
|
srciov[0].iov_len = blocksize;
|
|
|
|
dstiov[1].iov_len = secsize;
|
|
|
|
srciov[1].iov_len = secsize;
|
|
|
|
|
|
|
|
for (; len > 0; len -= secsize) {
|
|
|
|
dstiov[1].iov_base = dst;
|
|
|
|
srciov[1].iov_base = src;
|
|
|
|
|
2009-11-10 23:39:36 +03:00
|
|
|
memset(blkno_buf, 0x0, blocksize);
|
2002-10-04 22:22:35 +04:00
|
|
|
blkno2blkno_buf(blkno_buf, blkno);
|
|
|
|
if (dir == CGD_CIPHER_DECRYPT) {
|
|
|
|
dstuio.uio_iovcnt = 1;
|
|
|
|
srcuio.uio_iovcnt = 1;
|
|
|
|
IFDEBUG(CGDB_CRYPTO, hexprint("step 0: blkno_buf",
|
2009-11-10 23:39:36 +03:00
|
|
|
blkno_buf, blocksize));
|
2002-10-04 22:22:35 +04:00
|
|
|
cipher(cs->sc_cdata.cf_priv, &dstuio, &srcuio,
|
|
|
|
zero_iv, CGD_CIPHER_ENCRYPT);
|
|
|
|
memcpy(blkno_buf, sink, blocksize);
|
|
|
|
dstuio.uio_iovcnt = 2;
|
|
|
|
srcuio.uio_iovcnt = 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
IFDEBUG(CGDB_CRYPTO, hexprint("step 1: blkno_buf",
|
2009-11-10 23:39:36 +03:00
|
|
|
blkno_buf, blocksize));
|
2002-10-04 22:22:35 +04:00
|
|
|
cipher(cs->sc_cdata.cf_priv, &dstuio, &srcuio, zero_iv, dir);
|
|
|
|
IFDEBUG(CGDB_CRYPTO, hexprint("step 2: sink",
|
2009-11-10 23:39:36 +03:00
|
|
|
sink, blocksize));
|
2002-10-04 22:22:35 +04:00
|
|
|
|
|
|
|
dst += secsize;
|
|
|
|
src += secsize;
|
|
|
|
blkno++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
static void
|
2005-05-31 23:20:37 +04:00
|
|
|
hexprint(const char *start, void *buf, int len)
|
2002-10-04 22:22:35 +04:00
|
|
|
{
|
|
|
|
char *c = buf;
|
|
|
|
|
|
|
|
DIAGCONDPANIC(len < 0, ("hexprint: called with len < 0"));
|
|
|
|
printf("%s: len=%06d 0x", start, len);
|
|
|
|
while (len--)
|
2007-01-19 21:59:59 +03:00
|
|
|
printf("%02x", (unsigned char) *c++);
|
2002-10-04 22:22:35 +04:00
|
|
|
}
|
|
|
|
#endif
|
2009-06-05 23:21:02 +04:00
|
|
|
|
2013-12-28 23:25:07 +04:00
|
|
|
MODULE(MODULE_CLASS_DRIVER, cgd, "dk_subr");
|
2011-06-21 10:23:38 +04:00
|
|
|
|
|
|
|
#ifdef _MODULE
|
2010-01-13 02:49:34 +03:00
|
|
|
CFDRIVER_DECL(cgd, DV_DISK, NULL);
|
2011-06-21 10:23:38 +04:00
|
|
|
#endif
|
2009-06-05 23:21:02 +04:00
|
|
|
|
|
|
|
static int
|
|
|
|
cgd_modcmd(modcmd_t cmd, void *arg)
|
|
|
|
{
|
2013-09-12 16:28:49 +04:00
|
|
|
int error = 0;
|
2011-06-21 10:23:38 +04:00
|
|
|
|
2013-09-12 16:28:49 +04:00
|
|
|
#ifdef _MODULE
|
2014-10-03 01:01:38 +04:00
|
|
|
devmajor_t bmajor = -1, cmajor = -1;
|
2013-09-12 16:28:49 +04:00
|
|
|
#endif
|
2011-06-21 10:23:38 +04:00
|
|
|
|
2009-06-05 23:21:02 +04:00
|
|
|
switch (cmd) {
|
|
|
|
case MODULE_CMD_INIT:
|
2011-06-21 10:23:38 +04:00
|
|
|
#ifdef _MODULE
|
2010-01-13 02:49:34 +03:00
|
|
|
error = config_cfdriver_attach(&cgd_cd);
|
|
|
|
if (error)
|
|
|
|
break;
|
|
|
|
|
|
|
|
error = config_cfattach_attach(cgd_cd.cd_name, &cgd_ca);
|
|
|
|
if (error) {
|
|
|
|
config_cfdriver_detach(&cgd_cd);
|
|
|
|
aprint_error("%s: unable to register cfattach\n",
|
|
|
|
cgd_cd.cd_name);
|
|
|
|
break;
|
|
|
|
}
|
2011-06-21 10:23:38 +04:00
|
|
|
|
2010-01-13 02:49:34 +03:00
|
|
|
error = devsw_attach("cgd", &cgd_bdevsw, &bmajor,
|
2009-06-05 23:21:02 +04:00
|
|
|
&cgd_cdevsw, &cmajor);
|
2010-01-13 02:49:34 +03:00
|
|
|
if (error) {
|
|
|
|
config_cfattach_detach(cgd_cd.cd_name, &cgd_ca);
|
|
|
|
config_cfdriver_detach(&cgd_cd);
|
|
|
|
break;
|
|
|
|
}
|
2011-06-21 10:23:38 +04:00
|
|
|
#endif
|
2009-06-05 23:21:02 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case MODULE_CMD_FINI:
|
2011-06-21 10:23:38 +04:00
|
|
|
#ifdef _MODULE
|
2010-01-13 02:49:34 +03:00
|
|
|
error = config_cfattach_detach(cgd_cd.cd_name, &cgd_ca);
|
|
|
|
if (error)
|
|
|
|
break;
|
|
|
|
config_cfdriver_detach(&cgd_cd);
|
|
|
|
devsw_detach(&cgd_bdevsw, &cgd_cdevsw);
|
2011-06-21 10:23:38 +04:00
|
|
|
#endif
|
2009-06-05 23:21:02 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case MODULE_CMD_STAT:
|
|
|
|
return ENOTTY;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return ENOTTY;
|
|
|
|
}
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|