xref: /csrg-svn/sys/dev/vn.c (revision 53517)
141480Smckusick /*
241480Smckusick  * Copyright (c) 1988 University of Utah.
341480Smckusick  * Copyright (c) 1990 The Regents of the University of California.
441480Smckusick  * All rights reserved.
541480Smckusick  *
641480Smckusick  * This code is derived from software contributed to Berkeley by
741480Smckusick  * the Systems Programming Group of the University of Utah Computer
841480Smckusick  * Science Department.
941480Smckusick  *
1041480Smckusick  * %sccs.include.redist.c%
1141480Smckusick  *
1249299Shibler  * from: Utah $Hdr: vn.c 1.1 91/04/30$
1341480Smckusick  *
14*53517Sheideman  *	@(#)vn.c	7.10 (Berkeley) 05/14/92
1541480Smckusick  */
1641480Smckusick 
1741480Smckusick /*
1849299Shibler  * Vnode disk driver.
1941480Smckusick  *
2049299Shibler  * Block/character interface to a vnode.  Allows one to treat a file
2149299Shibler  * as a disk (e.g. build a filesystem in it, mount it, etc.).
2241480Smckusick  *
2349299Shibler  * NOTE 1: This uses the VOP_BMAP/VOP_STRATEGY interface to the vnode
2449299Shibler  * instead of a simple VOP_RDWR.  We do this to avoid distorting the
2549299Shibler  * local buffer cache.
2649299Shibler  *
2749299Shibler  * NOTE 2: There is a security issue involved with this driver.
2841480Smckusick  * Once mounted all access to the contents of the "mapped" file via
2941480Smckusick  * the special file is controlled by the permissions on the special
3041480Smckusick  * file, the protection of the mapped file is ignored (effectively,
3141480Smckusick  * by using root credentials in all transactions).
3241480Smckusick  */
3349299Shibler #include "vn.h"
3449299Shibler #if NVN > 0
3541480Smckusick 
3645788Sbostic #include "sys/param.h"
3745788Sbostic #include "sys/systm.h"
3849299Shibler #include "sys/namei.h"
3949299Shibler #include "sys/proc.h"
4045788Sbostic #include "sys/errno.h"
4145788Sbostic #include "sys/dkstat.h"
4249299Shibler #include "sys/buf.h"
4349299Shibler #include "sys/malloc.h"
4445788Sbostic #include "sys/ioctl.h"
4549299Shibler #include "sys/mount.h"
4645788Sbostic #include "sys/vnode.h"
4749299Shibler #include "sys/specdev.h"
4845788Sbostic #include "sys/file.h"
4945788Sbostic #include "sys/uio.h"
5041480Smckusick 
5149299Shibler #include "vnioctl.h"
5241480Smckusick 
5341480Smckusick #ifdef DEBUG
5449299Shibler int vndebug = 0x00;
5549299Shibler #define VDB_FOLLOW	0x01
5649299Shibler #define VDB_INIT	0x02
5749299Shibler #define VDB_IO		0x04
5841480Smckusick #endif
5941480Smckusick 
6049299Shibler struct	buf vnbuf[NVN];
6149299Shibler struct	buf vntab[NVN];
6241480Smckusick 
6341480Smckusick #define b_cylin	b_resid
6441480Smckusick 
6549299Shibler #define	vnunit(x)	((minor(x) >> 3) & 0x7)	/* for consistency */
6641480Smckusick 
6749299Shibler #define	getvnbuf()	\
6841480Smckusick 	((struct buf *)malloc(sizeof(struct buf), M_DEVBUF, M_WAITOK))
6949299Shibler #define putvnbuf(bp)	\
7041480Smckusick 	free((caddr_t)(bp), M_DEVBUF)
7141480Smckusick 
7249299Shibler struct vn_softc {
7341480Smckusick 	int		 sc_flags;	/* flags */
7449299Shibler 	size_t		 sc_size;	/* size of vn */
7541480Smckusick 	struct vnode	*sc_vp;		/* vnode */
7641480Smckusick 	struct ucred	*sc_cred;	/* credentials */
7741480Smckusick 	int		 sc_maxactive;	/* max # of active requests */
7849299Shibler } vn_softc[NVN];
7941480Smckusick 
8041480Smckusick /* sc_flags */
8149299Shibler #define	VNF_ALIVE	0x01
8249299Shibler #define VNF_INITED	0x02
8341480Smckusick 
8449299Shibler int
8549299Shibler vnopen(dev, flags, mode, p)
8641480Smckusick 	dev_t dev;
8749299Shibler 	int flags, mode;
8849299Shibler 	struct proc *p;
8941480Smckusick {
9049299Shibler 	int unit = vnunit(dev);
9141480Smckusick 
9241480Smckusick #ifdef DEBUG
9349299Shibler 	if (vndebug & VDB_FOLLOW)
9449299Shibler 		printf("vnopen(%x, %x, %x, %x)\n", dev, flags, mode, p);
9541480Smckusick #endif
9649299Shibler 	if (unit >= NVN)
9741480Smckusick 		return(ENXIO);
9841480Smckusick 	return(0);
9941480Smckusick }
10041480Smckusick 
10141480Smckusick /*
10241480Smckusick  * Break the request into bsize pieces and submit using VOP_BMAP/VOP_STRATEGY.
10341480Smckusick  * Note that this driver can only be used for swapping over NFS on the hp
10441480Smckusick  * since nfs_strategy on the vax cannot handle u-areas and page tables.
10541480Smckusick  */
10649299Shibler vnstrategy(bp)
10741480Smckusick 	register struct buf *bp;
10841480Smckusick {
109*53517Sheideman 	USES_VOP_BMAP;
11049299Shibler 	int unit = vnunit(bp->b_dev);
11149299Shibler 	register struct vn_softc *vn = &vn_softc[unit];
11241480Smckusick 	register struct buf *nbp;
11341480Smckusick 	register int bn, bsize, resid;
11441480Smckusick 	register caddr_t addr;
11541480Smckusick 	int sz, flags;
11649299Shibler 	extern int vniodone();
11741480Smckusick 
11841480Smckusick #ifdef DEBUG
11949299Shibler 	if (vndebug & VDB_FOLLOW)
12049299Shibler 		printf("vnstrategy(%x): unit %d\n", bp, unit);
12141480Smckusick #endif
12249299Shibler 	if ((vn->sc_flags & VNF_INITED) == 0) {
12341480Smckusick 		bp->b_error = ENXIO;
12441480Smckusick 		bp->b_flags |= B_ERROR;
12549299Shibler 		biodone(bp);
12641480Smckusick 		return;
12741480Smckusick 	}
12841480Smckusick 	bn = bp->b_blkno;
12941480Smckusick 	sz = howmany(bp->b_bcount, DEV_BSIZE);
13041480Smckusick 	bp->b_resid = bp->b_bcount;
13149299Shibler 	if (bn < 0 || bn + sz > vn->sc_size) {
13249299Shibler 		if (bn != vn->sc_size) {
13341480Smckusick 			bp->b_error = EINVAL;
13441480Smckusick 			bp->b_flags |= B_ERROR;
13541480Smckusick 		}
13649299Shibler 		biodone(bp);
13741480Smckusick 		return;
13841480Smckusick 	}
13941480Smckusick 	bn = dbtob(bn);
14051945Smckusick 	bsize = vn->sc_vp->v_mount->mnt_stat.f_iosize;
14141480Smckusick 	addr = bp->b_un.b_addr;
14241480Smckusick 	flags = bp->b_flags | B_CALL;
14341480Smckusick 	for (resid = bp->b_resid; resid; resid -= sz) {
14441480Smckusick 		struct vnode *vp;
14541480Smckusick 		daddr_t nbn;
14641480Smckusick 		int off, s;
14741480Smckusick 
14849299Shibler 		nbp = getvnbuf();
14941480Smckusick 		off = bn % bsize;
15041480Smckusick 		sz = MIN(bsize - off, resid);
15149299Shibler 		(void) VOP_BMAP(vn->sc_vp, bn / bsize, &vp, &nbn);
15241480Smckusick #ifdef DEBUG
15349299Shibler 		if (vndebug & VDB_IO)
15449299Shibler 			printf("vnstrategy: vp %x/%x bn %x/%x\n",
15549299Shibler 			       vn->sc_vp, vp, bn, nbn);
15641480Smckusick #endif
15741480Smckusick 		nbp->b_flags = flags;
15841480Smckusick 		nbp->b_bcount = sz;
15941480Smckusick 		nbp->b_bufsize = bp->b_bufsize;
16041480Smckusick 		nbp->b_error = 0;
16149299Shibler 		if (vp->v_type == VBLK || vp->v_type == VCHR)
16249299Shibler 			nbp->b_dev = vp->v_rdev;
16349299Shibler 		else
16449299Shibler 			nbp->b_dev = NODEV;
16541480Smckusick 		nbp->b_un.b_addr = addr;
16641480Smckusick 		nbp->b_blkno = nbn + btodb(off);
16741480Smckusick 		nbp->b_proc = bp->b_proc;
16849299Shibler 		nbp->b_iodone = vniodone;
16941480Smckusick 		nbp->b_vp = vp;
17041480Smckusick 		nbp->b_pfcent = (int) bp;	/* XXX */
17141480Smckusick 		/*
17241480Smckusick 		 * Just sort by block number
17341480Smckusick 		 */
17441480Smckusick 		nbp->b_cylin = nbp->b_blkno;
17541480Smckusick 		s = splbio();
17649299Shibler 		disksort(&vntab[unit], nbp);
17749299Shibler 		if (vntab[unit].b_active < vn->sc_maxactive) {
17849299Shibler 			vntab[unit].b_active++;
17949299Shibler 			vnstart(unit);
18041480Smckusick 		}
18141480Smckusick 		splx(s);
18241480Smckusick 		bn += sz;
18341480Smckusick 		addr += sz;
18441480Smckusick 	}
18541480Smckusick }
18641480Smckusick 
18741480Smckusick /*
18841480Smckusick  * Feed requests sequentially.
18941480Smckusick  * We do it this way to keep from flooding NFS servers if we are connected
19041480Smckusick  * to an NFS file.  This places the burden on the client rather than the
19141480Smckusick  * server.
19241480Smckusick  */
19349299Shibler vnstart(unit)
19441480Smckusick {
195*53517Sheideman 	USES_VOP_STRATEGY;
19649299Shibler 	register struct vn_softc *vn = &vn_softc[unit];
19741480Smckusick 	register struct buf *bp;
19841480Smckusick 
19941480Smckusick 	/*
20041480Smckusick 	 * Dequeue now since lower level strategy routine might
20141480Smckusick 	 * queue using same links
20241480Smckusick 	 */
20349299Shibler 	bp = vntab[unit].b_actf;
20449299Shibler 	vntab[unit].b_actf = bp->b_actf;
20541480Smckusick #ifdef DEBUG
20649299Shibler 	if (vndebug & VDB_IO)
20749299Shibler 		printf("vnstart(%d): bp %x vp %x blkno %x addr %x cnt %x\n",
20841480Smckusick 		       unit, bp, bp->b_vp, bp->b_blkno, bp->b_un.b_addr,
20941480Smckusick 		       bp->b_bcount);
21041480Smckusick #endif
21141480Smckusick 	VOP_STRATEGY(bp);
21241480Smckusick }
21341480Smckusick 
21449299Shibler vniodone(bp)
21541480Smckusick 	register struct buf *bp;
21641480Smckusick {
21741480Smckusick 	register struct buf *pbp = (struct buf *)bp->b_pfcent;	/* XXX */
21849299Shibler 	register int unit = vnunit(pbp->b_dev);
21941480Smckusick 	int s;
22041480Smckusick 
22141480Smckusick 	s = splbio();
22241480Smckusick #ifdef DEBUG
22349299Shibler 	if (vndebug & VDB_IO)
22449299Shibler 		printf("vniodone(%d): bp %x vp %x blkno %x addr %x cnt %x\n",
22541480Smckusick 		       unit, bp, bp->b_vp, bp->b_blkno, bp->b_un.b_addr,
22641480Smckusick 		       bp->b_bcount);
22741480Smckusick #endif
22841480Smckusick 	if (bp->b_error) {
22941480Smckusick #ifdef DEBUG
23049299Shibler 		if (vndebug & VDB_IO)
23149299Shibler 			printf("vniodone: bp %x error %d\n", bp, bp->b_error);
23241480Smckusick #endif
23341480Smckusick 		pbp->b_flags |= B_ERROR;
23449299Shibler 		pbp->b_error = biowait(bp);
23541480Smckusick 	}
23641480Smckusick 	pbp->b_resid -= bp->b_bcount;
23749299Shibler 	putvnbuf(bp);
23841480Smckusick 	if (pbp->b_resid == 0) {
23941480Smckusick #ifdef DEBUG
24049299Shibler 		if (vndebug & VDB_IO)
24149299Shibler 			printf("vniodone: pbp %x iodone\n", pbp);
24241480Smckusick #endif
24349299Shibler 		biodone(pbp);
24441480Smckusick 	}
24549299Shibler 	if (vntab[unit].b_actf)
24649299Shibler 		vnstart(unit);
24741480Smckusick 	else
24849299Shibler 		vntab[unit].b_active--;
24941480Smckusick 	splx(s);
25041480Smckusick }
25141480Smckusick 
25249299Shibler vnread(dev, uio, flags, p)
25341480Smckusick 	dev_t dev;
25441480Smckusick 	struct uio *uio;
25549299Shibler 	int flags;
25649299Shibler 	struct proc *p;
25741480Smckusick {
25849299Shibler 	register int unit = vnunit(dev);
25941480Smckusick 
26041480Smckusick #ifdef DEBUG
26149299Shibler 	if (vndebug & VDB_FOLLOW)
26249299Shibler 		printf("vnread(%x, %x, %x, %x)\n", dev, uio, flags, p);
26341480Smckusick #endif
26449299Shibler 	return(physio(vnstrategy, &vnbuf[unit], dev, B_READ, minphys, uio));
26541480Smckusick }
26641480Smckusick 
26749299Shibler vnwrite(dev, uio, flags, p)
26841480Smckusick 	dev_t dev;
26941480Smckusick 	struct uio *uio;
27049299Shibler 	int flags;
27149299Shibler 	struct proc *p;
27241480Smckusick {
27349299Shibler 	register int unit = vnunit(dev);
27441480Smckusick 
27541480Smckusick #ifdef DEBUG
27649299Shibler 	if (vndebug & VDB_FOLLOW)
27749299Shibler 		printf("vnwrite(%x, %x, %x, %x)\n", dev, uio, flags, p);
27841480Smckusick #endif
27949299Shibler 	return(physio(vnstrategy, &vnbuf[unit], dev, B_WRITE, minphys, uio));
28041480Smckusick }
28141480Smckusick 
28241480Smckusick /* ARGSUSED */
28349299Shibler vnioctl(dev, cmd, data, flag, p)
28441480Smckusick 	dev_t dev;
28541480Smckusick 	u_long cmd;
28641480Smckusick 	caddr_t data;
28741480Smckusick 	int flag;
28849299Shibler 	struct proc *p;
28941480Smckusick {
290*53517Sheideman 	USES_VOP_GETATTR;
291*53517Sheideman 	USES_VOP_UNLOCK;
29249299Shibler 	int unit = vnunit(dev);
29349299Shibler 	register struct vn_softc *vn;
29449299Shibler 	struct vn_ioctl *vio;
29541480Smckusick 	struct vattr vattr;
29649299Shibler 	struct nameidata nd;
29741480Smckusick 	int error;
29841480Smckusick 
29941480Smckusick #ifdef DEBUG
30049299Shibler 	if (vndebug & VDB_FOLLOW)
30149299Shibler 		printf("vnioctl(%x, %x, %x, %x, %x): unit %d\n",
30249299Shibler 		       dev, cmd, data, flag, p, unit);
30341480Smckusick #endif
30449299Shibler 	error = suser(p->p_ucred, &p->p_acflag);
30541480Smckusick 	if (error)
30641480Smckusick 		return (error);
30749299Shibler 	if (unit >= NVN)
30841480Smckusick 		return (ENXIO);
30941480Smckusick 
31049299Shibler 	vn = &vn_softc[unit];
31149299Shibler 	vio = (struct vn_ioctl *)data;
31241480Smckusick 	switch (cmd) {
31341480Smckusick 
31449299Shibler 	case VNIOCSET:
31549299Shibler 		if (vn->sc_flags & VNF_INITED)
31641480Smckusick 			return(EBUSY);
31741480Smckusick 		/*
31841480Smckusick 		 * Always open for read and write.
31941480Smckusick 		 * This is probably bogus, but it lets vn_open()
32041480Smckusick 		 * weed out directories, sockets, etc. so we don't
32141480Smckusick 		 * have to worry about them.
32241480Smckusick 		 */
32352761Shibler 		NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, vio->vn_file, p);
32452761Shibler 		if (error = vn_open(&nd, FREAD|FWRITE, 0))
32541480Smckusick 			return(error);
32650115Smckusick 		if (error = VOP_GETATTR(nd.ni_vp, &vattr, p->p_ucred, p)) {
32750115Smckusick 			VOP_UNLOCK(nd.ni_vp);
32850115Smckusick 			(void) vn_close(nd.ni_vp, FREAD|FWRITE, p->p_ucred, p);
32941480Smckusick 			return(error);
33041480Smckusick 		}
33150115Smckusick 		VOP_UNLOCK(nd.ni_vp);
33249299Shibler 		vn->sc_vp = nd.ni_vp;
33349299Shibler 		vn->sc_size = btodb(vattr.va_size);	/* note truncation */
33450115Smckusick 		if (error = vnsetcred(vn, p->p_ucred)) {
33550115Smckusick 			(void) vn_close(vn->sc_vp, FREAD|FWRITE, p->p_ucred, p);
33641480Smckusick 			return(error);
33741480Smckusick 		}
33849299Shibler 		vnthrottle(vn, vn->sc_vp);
33949299Shibler 		vio->vn_size = dbtob(vn->sc_size);
34049299Shibler 		vn->sc_flags |= VNF_INITED;
34141480Smckusick #ifdef DEBUG
34249299Shibler 		if (vndebug & VDB_INIT)
34349299Shibler 			printf("vnioctl: SET vp %x size %x\n",
34449299Shibler 			       vn->sc_vp, vn->sc_size);
34541480Smckusick #endif
34641480Smckusick 		break;
34741480Smckusick 
34849299Shibler 	case VNIOCCLR:
34949299Shibler 		if ((vn->sc_flags & VNF_INITED) == 0)
35041480Smckusick 			return(ENXIO);
35149299Shibler 		vnclear(vn);
35241480Smckusick #ifdef DEBUG
35349299Shibler 		if (vndebug & VDB_INIT)
35449299Shibler 			printf("vnioctl: CLRed\n");
35541480Smckusick #endif
35641480Smckusick 		break;
35741480Smckusick 
35841480Smckusick 	default:
35941480Smckusick 		return(ENXIO);
36041480Smckusick 	}
36141480Smckusick 	return(0);
36241480Smckusick }
36341480Smckusick 
36441480Smckusick /*
36541480Smckusick  * Duplicate the current processes' credentials.  Since we are called only
36641480Smckusick  * as the result of a SET ioctl and only root can do that, any future access
36741480Smckusick  * to this "disk" is essentially as root.  Note that credentials may change
36841480Smckusick  * if some other uid can write directly to the mapped file (NFS).
36941480Smckusick  */
37049299Shibler vnsetcred(vn, cred)
37149299Shibler 	register struct vn_softc *vn;
37249299Shibler 	struct ucred cred;
37341480Smckusick {
374*53517Sheideman 	USES_VOP_READ;
37541480Smckusick 	struct uio auio;
37641480Smckusick 	struct iovec aiov;
37741480Smckusick 	char tmpbuf[DEV_BSIZE];
37841480Smckusick 
37949299Shibler 	vn->sc_cred = crdup(cred);
38041480Smckusick 	/* XXX: Horrible kludge to establish credentials for NFS */
38141480Smckusick 	aiov.iov_base = tmpbuf;
38249299Shibler 	aiov.iov_len = MIN(DEV_BSIZE, dbtob(vn->sc_size));
38341480Smckusick 	auio.uio_iov = &aiov;
38441480Smckusick 	auio.uio_iovcnt = 1;
38541480Smckusick 	auio.uio_offset = 0;
38641480Smckusick 	auio.uio_rw = UIO_READ;
38741480Smckusick 	auio.uio_segflg = UIO_SYSSPACE;
38841480Smckusick 	auio.uio_resid = aiov.iov_len;
38949299Shibler 	return(VOP_READ(vn->sc_vp, &auio, 0, vn->sc_cred));
39041480Smckusick }
39141480Smckusick 
39241480Smckusick /*
39341480Smckusick  * Set maxactive based on FS type
39441480Smckusick  */
39549299Shibler vnthrottle(vn, vp)
39649299Shibler 	register struct vn_softc *vn;
39741480Smckusick 	struct vnode *vp;
39841480Smckusick {
399*53517Sheideman 	extern int (**ufs_vnodeop_p)();
400*53517Sheideman 	extern int (**nfsv2_vnodeop_p)();
40141480Smckusick 
402*53517Sheideman 	if (vp->v_op == nfsv2_vnodeop_p)
40349299Shibler 		vn->sc_maxactive = 2;
40441480Smckusick 	else
40549299Shibler 		vn->sc_maxactive = 8;
40641480Smckusick 
40749299Shibler 	if (vn->sc_maxactive < 1)
40849299Shibler 		vn->sc_maxactive = 1;
40941480Smckusick }
41041480Smckusick 
41149299Shibler vnshutdown()
41241480Smckusick {
41349299Shibler 	register struct vn_softc *vn;
41441480Smckusick 
41549299Shibler 	for (vn = &vn_softc[0]; vn < &vn_softc[NVN]; vn++)
41649299Shibler 		if (vn->sc_flags & VNF_INITED)
41749299Shibler 			vnclear(vn);
41841480Smckusick }
41941480Smckusick 
42049299Shibler vnclear(vn)
42149299Shibler 	register struct vn_softc *vn;
42241480Smckusick {
423*53517Sheideman 	USES_VOP_FSYNC;
42449299Shibler 	register struct vnode *vp = vn->sc_vp;
42550115Smckusick 	struct proc *p = curproc;		/* XXX */
42641480Smckusick 
42741480Smckusick #ifdef DEBUG
42849299Shibler 	if (vndebug & VDB_FOLLOW)
42949299Shibler 		printf("vnclear(%x): vp %x\n", vp);
43041480Smckusick #endif
43149299Shibler 	vn->sc_flags &= ~VNF_INITED;
43241480Smckusick 	if (vp == (struct vnode *)0)
43349299Shibler 		panic("vnioctl: null vp");
43441480Smckusick #if 0
43541480Smckusick 	/* XXX - this doesn't work right now */
43649299Shibler 	(void) VOP_FSYNC(vp, 0, vn->sc_cred, MNT_WAIT, p);
43741480Smckusick #endif
43850115Smckusick 	(void) vn_close(vp, FREAD|FWRITE, vn->sc_cred, p);
43949299Shibler 	crfree(vn->sc_cred);
44049299Shibler 	vn->sc_vp = (struct vnode *)0;
44149299Shibler 	vn->sc_cred = (struct ucred *)0;
44249299Shibler 	vn->sc_size = 0;
44341480Smckusick }
44441480Smckusick 
44549299Shibler vnsize(dev)
44641480Smckusick 	dev_t dev;
44741480Smckusick {
44849299Shibler 	int unit = vnunit(dev);
44949299Shibler 	register struct vn_softc *vn = &vn_softc[unit];
45041480Smckusick 
45149299Shibler 	if (unit >= NVN || (vn->sc_flags & VNF_INITED) == 0)
45241480Smckusick 		return(-1);
45349299Shibler 	return(vn->sc_size);
45441480Smckusick }
45541480Smckusick 
45649299Shibler vndump(dev)
45741480Smckusick {
45841480Smckusick 	return(ENXIO);
45941480Smckusick }
46041480Smckusick #endif
461