summaryrefslogtreecommitdiff
path: root/sys/dev/vnd.c
diff options
context:
space:
mode:
authorFelix Kronlage <fkr@cvs.openbsd.org>2007-09-29 07:08:03 +0000
committerFelix Kronlage <fkr@cvs.openbsd.org>2007-09-29 07:08:03 +0000
commitdabaa12e47d07a394e2007159360348b324b957b (patch)
tree12f589a446bd58ff10c3f0e23260a836be58d95b /sys/dev/vnd.c
parent4cd98ce9820d1d28700d87970c57f22e9c8b6009 (diff)
make safe vnds work on block devices.
from Piotr Durlej, tested by henning@, krw@, thib@ and myself ok thib@, grunk@, krw@
Diffstat (limited to 'sys/dev/vnd.c')
-rw-r--r--sys/dev/vnd.c59
1 files changed, 53 insertions, 6 deletions
diff --git a/sys/dev/vnd.c b/sys/dev/vnd.c
index 7e7549df0b0..f79f85b719b 100644
--- a/sys/dev/vnd.c
+++ b/sys/dev/vnd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: vnd.c,v 1.82 2007/09/12 18:45:14 mk Exp $ */
+/* $OpenBSD: vnd.c,v 1.83 2007/09/29 07:08:02 fkr Exp $ */
/* $NetBSD: vnd.c,v 1.26 1996/03/30 23:06:11 christos Exp $ */
/*
@@ -162,6 +162,7 @@ void vndiodone(struct buf *);
void vndshutdown(void);
void vndgetdisklabel(dev_t, struct vnd_softc *);
void vndencrypt(struct vnd_softc *, caddr_t, size_t, daddr64_t, int);
+size_t vndbdevsize(struct vnode *, struct proc *);
#define vndlock(sc) rw_enter(&sc->sc_rwlock, RW_WRITE|RW_INTR)
#define vndunlock(sc) rw_exit_write(&sc->sc_rwlock)
@@ -230,6 +231,12 @@ vndopen(dev_t dev, int flags, int mode, struct proc *p)
if ((error = vndlock(sc)) != 0)
return (error);
+ if (!vndsimple(dev) && sc->sc_vp != NULL &&
+ (sc->sc_vp->v_type != VREG || sc->sc_keyctx != NULL)) {
+ error = EINVAL;
+ goto bad;
+ }
+
if ((flags & FWRITE) && (sc->sc_flags & VNF_READONLY)) {
error = EROFS;
goto bad;
@@ -492,6 +499,15 @@ vndstrategy(struct buf *bp)
}
}
+ if (vnd->sc_vp->v_type != VREG || vnd->sc_keyctx != NULL) {
+ bp->b_error = EINVAL;
+ bp->b_flags |= B_ERROR;
+ s = splbio();
+ biodone(bp);
+ splx(s);
+ return;
+ }
+
/* The old-style buffercache bypassing method. */
bn += DL_GETPOFFSET(&vnd->sc_dk.dk_label->d_partitions[DISKPART(bp->b_dev)]);
bn = dbtob(bn);
@@ -691,6 +707,26 @@ vndwrite(dev_t dev, struct uio *uio, int flags)
return (physio(vndstrategy, NULL, dev, B_WRITE, minphys, uio));
}
+size_t
+vndbdevsize(struct vnode *vp, struct proc *p)
+{
+ struct partinfo pi;
+ struct bdevsw *bsw;
+ long sscale;
+ dev_t dev;
+
+ dev = vp->v_rdev;
+ bsw = bdevsw_lookup(dev);
+ if (bsw->d_ioctl == NULL)
+ return (0);
+ if (bsw->d_ioctl(dev, DIOCGPART, (caddr_t)&pi, FREAD, p))
+ return (0);
+ sscale = pi.disklab->d_secsize / DEV_BSIZE;
+ DNPRINTF(VDB_INIT, "vndbdevsize: size %li secsize %li sscale %li\n",
+ (long)pi.part->p_size,(long)pi.disklab->d_secsize,sscale);
+ return (pi.part->p_size * sscale);
+}
+
/* ARGSUSED */
int
vndioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
@@ -756,16 +792,27 @@ vndioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
return (error);
}
- error = VOP_GETATTR(nd.ni_vp, &vattr, p->p_ucred, p);
- if (error) {
+ if (nd.ni_vp->v_type != VREG && !vndsimple(dev)) {
VOP_UNLOCK(nd.ni_vp, 0, p);
- (void) vn_close(nd.ni_vp, VNDRW(vnd), p->p_ucred, p);
+ vn_close(nd.ni_vp, VNDRW(vnd), p->p_ucred, p);
vndunlock(vnd);
- return (error);
+ return (EINVAL);
+ }
+
+ if (nd.ni_vp->v_type == VBLK)
+ vnd->sc_size = vndbdevsize(nd.ni_vp, p);
+ else {
+ error = VOP_GETATTR(nd.ni_vp, &vattr, p->p_ucred, p);
+ if (error) {
+ VOP_UNLOCK(nd.ni_vp, 0, p);
+ vn_close(nd.ni_vp, VNDRW(vnd), p->p_ucred, p);
+ vndunlock(vnd);
+ return (error);
+ }
+ vnd->sc_size = btodb(vattr.va_size); /* note truncation */
}
VOP_UNLOCK(nd.ni_vp, 0, p);
vnd->sc_vp = nd.ni_vp;
- vnd->sc_size = btodb(vattr.va_size); /* note truncation */
if ((error = vndsetcred(vnd, p->p_ucred)) != 0) {
(void) vn_close(nd.ni_vp, VNDRW(vnd), p->p_ucred, p);
vndunlock(vnd);