Let's see if I can get this closer to right. The patch is against and tested on -current. Thank you, Pedro, for your help.
--- sys/dev/vnd.c.orig Sun Sep 10 19:18:28 2006 +++ sys/dev/vnd.c Mon Sep 11 15:54:30 2006 @@ -142,7 +142,10 @@ #define VNF_HAVELABEL 0x0400 #define VNF_BUSY 0x0800 #define VNF_SIMPLE 0x1000 +#define VNF_READONLY 0x2000 +#define FLG(vnd) (vnd->sc_flags & VNF_READONLY ? FREAD : FREAD|FWRITE) + struct vnd_softc *vnd_softc; int numvnd = 0; @@ -234,6 +237,11 @@ if ((error = vndlock(sc)) != 0) return (error); + if (flags & FWRITE && sc->sc_flags & VNF_READONLY) { + error = EROFS; + goto bad; + } + if ((sc->sc_flags & VNF_INITED) && (sc->sc_flags & VNF_HAVELABEL) == 0) { sc->sc_flags |= VNF_HAVELABEL; @@ -817,20 +825,25 @@ } /* - * Always open for read and write. - * This is probably bogus, but it lets vn_open() + * Open for read and write first. This lets vn_open() * weed out directories, sockets, etc. so we don't * have to worry about them. */ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, vio->vnd_file, p); - if ((error = vn_open(&nd, FREAD|FWRITE, 0)) != 0) { + vnd->sc_flags &= ~VNF_READONLY; + error = vn_open(&nd, FREAD|FWRITE, 0); + if (EROFS == error) { + vnd->sc_flags |= VNF_READONLY; + error = vn_open(&nd, FREAD, 0); + } + if (error) { vndunlock(vnd); return (error); } error = VOP_GETATTR(nd.ni_vp, &vattr, p->p_ucred, p); if (error) { VOP_UNLOCK(nd.ni_vp, 0, p); - (void) vn_close(nd.ni_vp, FREAD|FWRITE, p->p_ucred, p); + (void) vn_close(nd.ni_vp, FLG(vnd), p->p_ucred, p); vndunlock(vnd); return (error); } @@ -838,7 +851,7 @@ 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, FREAD|FWRITE, p->p_ucred, p); + (void) vn_close(nd.ni_vp, FLG(vnd), p->p_ucred, p); vndunlock(vnd); return (error); } @@ -851,7 +864,7 @@ if ((error = copyin(vio->vnd_key, key, vio->vnd_keylen)) != 0) { - (void) vn_close(nd.ni_vp, FREAD|FWRITE, + (void) vn_close(nd.ni_vp, FLG(vnd), p->p_ucred, p); vndunlock(vnd); return (error); @@ -1087,7 +1100,7 @@ vnd->sc_flags &= ~VNF_INITED; if (vp == (struct vnode *)0) panic("vndioctl: null vp"); - (void) vn_close(vp, FREAD|FWRITE, vnd->sc_cred, p); + (void) vn_close(vp, FLG(vnd), vnd->sc_cred, p); crfree(vnd->sc_cred); vnd->sc_vp = (struct vnode *)0; vnd->sc_cred = (struct ucred *)0; --- usr.sbin/vnconfig/vnconfig.c.orig Sun Sep 10 19:19:25 2006 +++ usr.sbin/vnconfig/vnconfig.c Mon Sep 11 15:28:27 2006 @@ -226,7 +226,7 @@ char *rdev; int rv; - if (opendev(dev, O_RDWR, OPENDEV_PART, &rdev) < 0) + if (opendev(dev, O_RDONLY, OPENDEV_PART, &rdev) < 0) err(4, "%s", rdev); f = fopen(rdev, "rw"); if (f == NULL) {