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 * 12*53921Shibler * from: Utah $Hdr: fd.c 1.3 89/12/03$ 1341480Smckusick * 14*53921Shibler * @(#)vn.c 7.11 (Berkeley) 06/05/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 { 10953517Sheideman 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; 116*53921Shibler extern void 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 { 19553517Sheideman 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 214*53921Shibler void 21549299Shibler vniodone(bp) 21641480Smckusick register struct buf *bp; 21741480Smckusick { 21841480Smckusick register struct buf *pbp = (struct buf *)bp->b_pfcent; /* XXX */ 21949299Shibler register int unit = vnunit(pbp->b_dev); 22041480Smckusick int s; 22141480Smckusick 22241480Smckusick s = splbio(); 22341480Smckusick #ifdef DEBUG 22449299Shibler if (vndebug & VDB_IO) 22549299Shibler printf("vniodone(%d): bp %x vp %x blkno %x addr %x cnt %x\n", 22641480Smckusick unit, bp, bp->b_vp, bp->b_blkno, bp->b_un.b_addr, 22741480Smckusick bp->b_bcount); 22841480Smckusick #endif 22941480Smckusick if (bp->b_error) { 23041480Smckusick #ifdef DEBUG 23149299Shibler if (vndebug & VDB_IO) 23249299Shibler printf("vniodone: bp %x error %d\n", bp, bp->b_error); 23341480Smckusick #endif 23441480Smckusick pbp->b_flags |= B_ERROR; 23549299Shibler pbp->b_error = biowait(bp); 23641480Smckusick } 23741480Smckusick pbp->b_resid -= bp->b_bcount; 23849299Shibler putvnbuf(bp); 23941480Smckusick if (pbp->b_resid == 0) { 24041480Smckusick #ifdef DEBUG 24149299Shibler if (vndebug & VDB_IO) 24249299Shibler printf("vniodone: pbp %x iodone\n", pbp); 24341480Smckusick #endif 24449299Shibler biodone(pbp); 24541480Smckusick } 24649299Shibler if (vntab[unit].b_actf) 24749299Shibler vnstart(unit); 24841480Smckusick else 24949299Shibler vntab[unit].b_active--; 25041480Smckusick splx(s); 25141480Smckusick } 25241480Smckusick 25349299Shibler vnread(dev, uio, flags, p) 25441480Smckusick dev_t dev; 25541480Smckusick struct uio *uio; 25649299Shibler int flags; 25749299Shibler struct proc *p; 25841480Smckusick { 25949299Shibler register int unit = vnunit(dev); 26041480Smckusick 26141480Smckusick #ifdef DEBUG 26249299Shibler if (vndebug & VDB_FOLLOW) 26349299Shibler printf("vnread(%x, %x, %x, %x)\n", dev, uio, flags, p); 26441480Smckusick #endif 26549299Shibler return(physio(vnstrategy, &vnbuf[unit], dev, B_READ, minphys, uio)); 26641480Smckusick } 26741480Smckusick 26849299Shibler vnwrite(dev, uio, flags, p) 26941480Smckusick dev_t dev; 27041480Smckusick struct uio *uio; 27149299Shibler int flags; 27249299Shibler struct proc *p; 27341480Smckusick { 27449299Shibler register int unit = vnunit(dev); 27541480Smckusick 27641480Smckusick #ifdef DEBUG 27749299Shibler if (vndebug & VDB_FOLLOW) 27849299Shibler printf("vnwrite(%x, %x, %x, %x)\n", dev, uio, flags, p); 27941480Smckusick #endif 28049299Shibler return(physio(vnstrategy, &vnbuf[unit], dev, B_WRITE, minphys, uio)); 28141480Smckusick } 28241480Smckusick 28341480Smckusick /* ARGSUSED */ 28449299Shibler vnioctl(dev, cmd, data, flag, p) 28541480Smckusick dev_t dev; 28641480Smckusick u_long cmd; 28741480Smckusick caddr_t data; 28841480Smckusick int flag; 28949299Shibler struct proc *p; 29041480Smckusick { 29153517Sheideman USES_VOP_GETATTR; 29253517Sheideman USES_VOP_UNLOCK; 29349299Shibler int unit = vnunit(dev); 29449299Shibler register struct vn_softc *vn; 29549299Shibler struct vn_ioctl *vio; 29641480Smckusick struct vattr vattr; 29749299Shibler struct nameidata nd; 29841480Smckusick int error; 29941480Smckusick 30041480Smckusick #ifdef DEBUG 30149299Shibler if (vndebug & VDB_FOLLOW) 30249299Shibler printf("vnioctl(%x, %x, %x, %x, %x): unit %d\n", 30349299Shibler dev, cmd, data, flag, p, unit); 30441480Smckusick #endif 30549299Shibler error = suser(p->p_ucred, &p->p_acflag); 30641480Smckusick if (error) 30741480Smckusick return (error); 30849299Shibler if (unit >= NVN) 30941480Smckusick return (ENXIO); 31041480Smckusick 31149299Shibler vn = &vn_softc[unit]; 31249299Shibler vio = (struct vn_ioctl *)data; 31341480Smckusick switch (cmd) { 31441480Smckusick 31549299Shibler case VNIOCSET: 31649299Shibler if (vn->sc_flags & VNF_INITED) 31741480Smckusick return(EBUSY); 31841480Smckusick /* 31941480Smckusick * Always open for read and write. 32041480Smckusick * This is probably bogus, but it lets vn_open() 32141480Smckusick * weed out directories, sockets, etc. so we don't 32241480Smckusick * have to worry about them. 32341480Smckusick */ 32452761Shibler NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, vio->vn_file, p); 32552761Shibler if (error = vn_open(&nd, FREAD|FWRITE, 0)) 32641480Smckusick return(error); 32750115Smckusick if (error = VOP_GETATTR(nd.ni_vp, &vattr, p->p_ucred, p)) { 32850115Smckusick VOP_UNLOCK(nd.ni_vp); 32950115Smckusick (void) vn_close(nd.ni_vp, FREAD|FWRITE, p->p_ucred, p); 33041480Smckusick return(error); 33141480Smckusick } 33250115Smckusick VOP_UNLOCK(nd.ni_vp); 33349299Shibler vn->sc_vp = nd.ni_vp; 33449299Shibler vn->sc_size = btodb(vattr.va_size); /* note truncation */ 33550115Smckusick if (error = vnsetcred(vn, p->p_ucred)) { 33650115Smckusick (void) vn_close(vn->sc_vp, FREAD|FWRITE, p->p_ucred, p); 33741480Smckusick return(error); 33841480Smckusick } 33949299Shibler vnthrottle(vn, vn->sc_vp); 34049299Shibler vio->vn_size = dbtob(vn->sc_size); 34149299Shibler vn->sc_flags |= VNF_INITED; 34241480Smckusick #ifdef DEBUG 34349299Shibler if (vndebug & VDB_INIT) 34449299Shibler printf("vnioctl: SET vp %x size %x\n", 34549299Shibler vn->sc_vp, vn->sc_size); 34641480Smckusick #endif 34741480Smckusick break; 34841480Smckusick 34949299Shibler case VNIOCCLR: 35049299Shibler if ((vn->sc_flags & VNF_INITED) == 0) 35141480Smckusick return(ENXIO); 35249299Shibler vnclear(vn); 35341480Smckusick #ifdef DEBUG 35449299Shibler if (vndebug & VDB_INIT) 35549299Shibler printf("vnioctl: CLRed\n"); 35641480Smckusick #endif 35741480Smckusick break; 35841480Smckusick 35941480Smckusick default: 36041480Smckusick return(ENXIO); 36141480Smckusick } 36241480Smckusick return(0); 36341480Smckusick } 36441480Smckusick 36541480Smckusick /* 36641480Smckusick * Duplicate the current processes' credentials. Since we are called only 36741480Smckusick * as the result of a SET ioctl and only root can do that, any future access 36841480Smckusick * to this "disk" is essentially as root. Note that credentials may change 36941480Smckusick * if some other uid can write directly to the mapped file (NFS). 37041480Smckusick */ 37149299Shibler vnsetcred(vn, cred) 37249299Shibler register struct vn_softc *vn; 37349299Shibler struct ucred cred; 37441480Smckusick { 37553517Sheideman USES_VOP_READ; 37641480Smckusick struct uio auio; 37741480Smckusick struct iovec aiov; 37841480Smckusick char tmpbuf[DEV_BSIZE]; 37941480Smckusick 38049299Shibler vn->sc_cred = crdup(cred); 38141480Smckusick /* XXX: Horrible kludge to establish credentials for NFS */ 38241480Smckusick aiov.iov_base = tmpbuf; 38349299Shibler aiov.iov_len = MIN(DEV_BSIZE, dbtob(vn->sc_size)); 38441480Smckusick auio.uio_iov = &aiov; 38541480Smckusick auio.uio_iovcnt = 1; 38641480Smckusick auio.uio_offset = 0; 38741480Smckusick auio.uio_rw = UIO_READ; 38841480Smckusick auio.uio_segflg = UIO_SYSSPACE; 38941480Smckusick auio.uio_resid = aiov.iov_len; 39049299Shibler return(VOP_READ(vn->sc_vp, &auio, 0, vn->sc_cred)); 39141480Smckusick } 39241480Smckusick 39341480Smckusick /* 39441480Smckusick * Set maxactive based on FS type 39541480Smckusick */ 39649299Shibler vnthrottle(vn, vp) 39749299Shibler register struct vn_softc *vn; 39841480Smckusick struct vnode *vp; 39941480Smckusick { 40053517Sheideman extern int (**ufs_vnodeop_p)(); 40153517Sheideman extern int (**nfsv2_vnodeop_p)(); 40241480Smckusick 40353517Sheideman if (vp->v_op == nfsv2_vnodeop_p) 40449299Shibler vn->sc_maxactive = 2; 40541480Smckusick else 40649299Shibler vn->sc_maxactive = 8; 40741480Smckusick 40849299Shibler if (vn->sc_maxactive < 1) 40949299Shibler vn->sc_maxactive = 1; 41041480Smckusick } 41141480Smckusick 41249299Shibler vnshutdown() 41341480Smckusick { 41449299Shibler register struct vn_softc *vn; 41541480Smckusick 41649299Shibler for (vn = &vn_softc[0]; vn < &vn_softc[NVN]; vn++) 41749299Shibler if (vn->sc_flags & VNF_INITED) 41849299Shibler vnclear(vn); 41941480Smckusick } 42041480Smckusick 42149299Shibler vnclear(vn) 42249299Shibler register struct vn_softc *vn; 42341480Smckusick { 42453517Sheideman USES_VOP_FSYNC; 42549299Shibler register struct vnode *vp = vn->sc_vp; 42650115Smckusick struct proc *p = curproc; /* XXX */ 42741480Smckusick 42841480Smckusick #ifdef DEBUG 42949299Shibler if (vndebug & VDB_FOLLOW) 43049299Shibler printf("vnclear(%x): vp %x\n", vp); 43141480Smckusick #endif 43249299Shibler vn->sc_flags &= ~VNF_INITED; 43341480Smckusick if (vp == (struct vnode *)0) 43449299Shibler panic("vnioctl: null vp"); 43541480Smckusick #if 0 43641480Smckusick /* XXX - this doesn't work right now */ 43749299Shibler (void) VOP_FSYNC(vp, 0, vn->sc_cred, MNT_WAIT, p); 43841480Smckusick #endif 43950115Smckusick (void) vn_close(vp, FREAD|FWRITE, vn->sc_cred, p); 44049299Shibler crfree(vn->sc_cred); 44149299Shibler vn->sc_vp = (struct vnode *)0; 44249299Shibler vn->sc_cred = (struct ucred *)0; 44349299Shibler vn->sc_size = 0; 44441480Smckusick } 44541480Smckusick 44649299Shibler vnsize(dev) 44741480Smckusick dev_t dev; 44841480Smckusick { 44949299Shibler int unit = vnunit(dev); 45049299Shibler register struct vn_softc *vn = &vn_softc[unit]; 45141480Smckusick 45249299Shibler if (unit >= NVN || (vn->sc_flags & VNF_INITED) == 0) 45341480Smckusick return(-1); 45449299Shibler return(vn->sc_size); 45541480Smckusick } 45641480Smckusick 45749299Shibler vndump(dev) 45841480Smckusick { 45941480Smckusick return(ENXIO); 46041480Smckusick } 46141480Smckusick #endif 462