xref: /original-bsd/sys/dev/vn.c (revision e0851aa6)
1 /*
2  * Copyright (c) 1988 University of Utah.
3  * Copyright (c) 1990 The Regents of the University of California.
4  * All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * the Systems Programming Group of the University of Utah Computer
8  * Science Department.
9  *
10  * %sccs.include.redist.c%
11  *
12  * from: Utah $Hdr: fd.c 1.3 89/12/03$
13  *
14  *	@(#)vn.c	7.14 (Berkeley) 10/11/92
15  */
16 
17 /*
18  * Vnode disk driver.
19  *
20  * Block/character interface to a vnode.  Allows one to treat a file
21  * as a disk (e.g. build a filesystem in it, mount it, etc.).
22  *
23  * NOTE 1: This uses the VOP_BMAP/VOP_STRATEGY interface to the vnode
24  * instead of a simple VOP_RDWR.  We do this to avoid distorting the
25  * local buffer cache.
26  *
27  * NOTE 2: There is a security issue involved with this driver.
28  * Once mounted all access to the contents of the "mapped" file via
29  * the special file is controlled by the permissions on the special
30  * file, the protection of the mapped file is ignored (effectively,
31  * by using root credentials in all transactions).
32  */
33 #include "vn.h"
34 #if NVN > 0
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/namei.h>
39 #include <sys/proc.h>
40 #include <sys/errno.h>
41 #include <sys/dkstat.h>
42 #include <sys/buf.h>
43 #include <sys/malloc.h>
44 #include <sys/ioctl.h>
45 #include <sys/mount.h>
46 #include <sys/vnode.h>
47 #include <sys/file.h>
48 #include <sys/uio.h>
49 
50 #include <miscfs/specfs/specdev.h>
51 
52 #include <dev/vnioctl.h>
53 
54 #ifdef DEBUG
55 int vndebug = 0x00;
56 #define VDB_FOLLOW	0x01
57 #define VDB_INIT	0x02
58 #define VDB_IO		0x04
59 #endif
60 
61 struct	buf vnbuf[NVN];
62 struct	buf vntab[NVN];
63 
64 #define b_cylin	b_resid
65 
66 #define	vnunit(x)	((minor(x) >> 3) & 0x7)	/* for consistency */
67 
68 #define	getvnbuf()	\
69 	((struct buf *)malloc(sizeof(struct buf), M_DEVBUF, M_WAITOK))
70 #define putvnbuf(bp)	\
71 	free((caddr_t)(bp), M_DEVBUF)
72 
73 struct vn_softc {
74 	int		 sc_flags;	/* flags */
75 	size_t		 sc_size;	/* size of vn */
76 	struct vnode	*sc_vp;		/* vnode */
77 	struct ucred	*sc_cred;	/* credentials */
78 	int		 sc_maxactive;	/* max # of active requests */
79 } vn_softc[NVN];
80 
81 /* sc_flags */
82 #define	VNF_ALIVE	0x01
83 #define VNF_INITED	0x02
84 
85 int
86 vnopen(dev, flags, mode, p)
87 	dev_t dev;
88 	int flags, mode;
89 	struct proc *p;
90 {
91 	int unit = vnunit(dev);
92 
93 #ifdef DEBUG
94 	if (vndebug & VDB_FOLLOW)
95 		printf("vnopen(%x, %x, %x, %x)\n", dev, flags, mode, p);
96 #endif
97 	if (unit >= NVN)
98 		return(ENXIO);
99 	return(0);
100 }
101 
102 /*
103  * Break the request into bsize pieces and submit using VOP_BMAP/VOP_STRATEGY.
104  * Note that this driver can only be used for swapping over NFS on the hp
105  * since nfs_strategy on the vax cannot handle u-areas and page tables.
106  */
107 vnstrategy(bp)
108 	register struct buf *bp;
109 {
110 	int unit = vnunit(bp->b_dev);
111 	register struct vn_softc *vn = &vn_softc[unit];
112 	register struct buf *nbp;
113 	register int bn, bsize, resid;
114 	register caddr_t addr;
115 	int sz, flags;
116 	extern void vniodone();
117 
118 #ifdef DEBUG
119 	if (vndebug & VDB_FOLLOW)
120 		printf("vnstrategy(%x): unit %d\n", bp, unit);
121 #endif
122 	if ((vn->sc_flags & VNF_INITED) == 0) {
123 		bp->b_error = ENXIO;
124 		bp->b_flags |= B_ERROR;
125 		biodone(bp);
126 		return;
127 	}
128 	bn = bp->b_blkno;
129 	sz = howmany(bp->b_bcount, DEV_BSIZE);
130 	bp->b_resid = bp->b_bcount;
131 	if (bn < 0 || bn + sz > vn->sc_size) {
132 		if (bn != vn->sc_size) {
133 			bp->b_error = EINVAL;
134 			bp->b_flags |= B_ERROR;
135 		}
136 		biodone(bp);
137 		return;
138 	}
139 	bn = dbtob(bn);
140 	bsize = vn->sc_vp->v_mount->mnt_stat.f_iosize;
141 	addr = bp->b_un.b_addr;
142 	flags = bp->b_flags | B_CALL;
143 	for (resid = bp->b_resid; resid; resid -= sz) {
144 		struct vnode *vp;
145 		daddr_t nbn;
146 		int off, s;
147 
148 		nbp = getvnbuf();
149 		off = bn % bsize;
150 		sz = min(bsize - off, resid);
151 		(void) VOP_BMAP(vn->sc_vp, bn / bsize, &vp, &nbn, NULL);
152 #ifdef DEBUG
153 		if (vndebug & VDB_IO)
154 			printf("vnstrategy: vp %x/%x bn %x/%x\n",
155 			       vn->sc_vp, vp, bn, nbn);
156 #endif
157 		nbp->b_flags = flags;
158 		nbp->b_bcount = sz;
159 		nbp->b_bufsize = bp->b_bufsize;
160 		nbp->b_error = 0;
161 		if (vp->v_type == VBLK || vp->v_type == VCHR)
162 			nbp->b_dev = vp->v_rdev;
163 		else
164 			nbp->b_dev = NODEV;
165 		nbp->b_un.b_addr = addr;
166 		nbp->b_blkno = nbn + btodb(off);
167 		nbp->b_proc = bp->b_proc;
168 		nbp->b_iodone = vniodone;
169 		nbp->b_vp = vp;
170 		nbp->b_pfcent = (int) bp;	/* XXX */
171 		/*
172 		 * Just sort by block number
173 		 */
174 		nbp->b_cylin = nbp->b_blkno;
175 		s = splbio();
176 		disksort(&vntab[unit], nbp);
177 		if (vntab[unit].b_active < vn->sc_maxactive) {
178 			vntab[unit].b_active++;
179 			vnstart(unit);
180 		}
181 		splx(s);
182 		bn += sz;
183 		addr += sz;
184 	}
185 }
186 
187 /*
188  * Feed requests sequentially.
189  * We do it this way to keep from flooding NFS servers if we are connected
190  * to an NFS file.  This places the burden on the client rather than the
191  * server.
192  */
193 vnstart(unit)
194 {
195 	register struct vn_softc *vn = &vn_softc[unit];
196 	register struct buf *bp;
197 
198 	/*
199 	 * Dequeue now since lower level strategy routine might
200 	 * queue using same links
201 	 */
202 	bp = vntab[unit].b_actf;
203 	vntab[unit].b_actf = bp->b_actf;
204 #ifdef DEBUG
205 	if (vndebug & VDB_IO)
206 		printf("vnstart(%d): bp %x vp %x blkno %x addr %x cnt %x\n",
207 		       unit, bp, bp->b_vp, bp->b_blkno, bp->b_un.b_addr,
208 		       bp->b_bcount);
209 #endif
210 	VOP_STRATEGY(bp);
211 }
212 
213 void
214 vniodone(bp)
215 	register struct buf *bp;
216 {
217 	register struct buf *pbp = (struct buf *)bp->b_pfcent;	/* XXX */
218 	register int unit = vnunit(pbp->b_dev);
219 	int s;
220 
221 	s = splbio();
222 #ifdef DEBUG
223 	if (vndebug & VDB_IO)
224 		printf("vniodone(%d): bp %x vp %x blkno %x addr %x cnt %x\n",
225 		       unit, bp, bp->b_vp, bp->b_blkno, bp->b_un.b_addr,
226 		       bp->b_bcount);
227 #endif
228 	if (bp->b_error) {
229 #ifdef DEBUG
230 		if (vndebug & VDB_IO)
231 			printf("vniodone: bp %x error %d\n", bp, bp->b_error);
232 #endif
233 		pbp->b_flags |= B_ERROR;
234 		pbp->b_error = biowait(bp);
235 	}
236 	pbp->b_resid -= bp->b_bcount;
237 	putvnbuf(bp);
238 	if (pbp->b_resid == 0) {
239 #ifdef DEBUG
240 		if (vndebug & VDB_IO)
241 			printf("vniodone: pbp %x iodone\n", pbp);
242 #endif
243 		biodone(pbp);
244 	}
245 	if (vntab[unit].b_actf)
246 		vnstart(unit);
247 	else
248 		vntab[unit].b_active--;
249 	splx(s);
250 }
251 
252 vnread(dev, uio, flags, p)
253 	dev_t dev;
254 	struct uio *uio;
255 	int flags;
256 	struct proc *p;
257 {
258 	register int unit = vnunit(dev);
259 
260 #ifdef DEBUG
261 	if (vndebug & VDB_FOLLOW)
262 		printf("vnread(%x, %x, %x, %x)\n", dev, uio, flags, p);
263 #endif
264 	return(physio(vnstrategy, &vnbuf[unit], dev, B_READ, minphys, uio));
265 }
266 
267 vnwrite(dev, uio, flags, p)
268 	dev_t dev;
269 	struct uio *uio;
270 	int flags;
271 	struct proc *p;
272 {
273 	register int unit = vnunit(dev);
274 
275 #ifdef DEBUG
276 	if (vndebug & VDB_FOLLOW)
277 		printf("vnwrite(%x, %x, %x, %x)\n", dev, uio, flags, p);
278 #endif
279 	return(physio(vnstrategy, &vnbuf[unit], dev, B_WRITE, minphys, uio));
280 }
281 
282 /* ARGSUSED */
283 vnioctl(dev, cmd, data, flag, p)
284 	dev_t dev;
285 	u_long cmd;
286 	caddr_t data;
287 	int flag;
288 	struct proc *p;
289 {
290 	int unit = vnunit(dev);
291 	register struct vn_softc *vn;
292 	struct vn_ioctl *vio;
293 	struct vattr vattr;
294 	struct nameidata nd;
295 	int error;
296 
297 #ifdef DEBUG
298 	if (vndebug & VDB_FOLLOW)
299 		printf("vnioctl(%x, %x, %x, %x, %x): unit %d\n",
300 		       dev, cmd, data, flag, p, unit);
301 #endif
302 	error = suser(p->p_ucred, &p->p_acflag);
303 	if (error)
304 		return (error);
305 	if (unit >= NVN)
306 		return (ENXIO);
307 
308 	vn = &vn_softc[unit];
309 	vio = (struct vn_ioctl *)data;
310 	switch (cmd) {
311 
312 	case VNIOCSET:
313 		if (vn->sc_flags & VNF_INITED)
314 			return(EBUSY);
315 		/*
316 		 * Always open for read and write.
317 		 * This is probably bogus, but it lets vn_open()
318 		 * weed out directories, sockets, etc. so we don't
319 		 * have to worry about them.
320 		 */
321 		NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, vio->vn_file, p);
322 		if (error = vn_open(&nd, FREAD|FWRITE, 0))
323 			return(error);
324 		if (error = VOP_GETATTR(nd.ni_vp, &vattr, p->p_ucred, p)) {
325 			VOP_UNLOCK(nd.ni_vp);
326 			(void) vn_close(nd.ni_vp, FREAD|FWRITE, p->p_ucred, p);
327 			return(error);
328 		}
329 		VOP_UNLOCK(nd.ni_vp);
330 		vn->sc_vp = nd.ni_vp;
331 		vn->sc_size = btodb(vattr.va_size);	/* note truncation */
332 		if (error = vnsetcred(vn, p->p_ucred)) {
333 			(void) vn_close(vn->sc_vp, FREAD|FWRITE, p->p_ucred, p);
334 			return(error);
335 		}
336 		vnthrottle(vn, vn->sc_vp);
337 		vio->vn_size = dbtob(vn->sc_size);
338 		vn->sc_flags |= VNF_INITED;
339 #ifdef DEBUG
340 		if (vndebug & VDB_INIT)
341 			printf("vnioctl: SET vp %x size %x\n",
342 			       vn->sc_vp, vn->sc_size);
343 #endif
344 		break;
345 
346 	case VNIOCCLR:
347 		if ((vn->sc_flags & VNF_INITED) == 0)
348 			return(ENXIO);
349 		vnclear(vn);
350 #ifdef DEBUG
351 		if (vndebug & VDB_INIT)
352 			printf("vnioctl: CLRed\n");
353 #endif
354 		break;
355 
356 	default:
357 		return(ENXIO);
358 	}
359 	return(0);
360 }
361 
362 /*
363  * Duplicate the current processes' credentials.  Since we are called only
364  * as the result of a SET ioctl and only root can do that, any future access
365  * to this "disk" is essentially as root.  Note that credentials may change
366  * if some other uid can write directly to the mapped file (NFS).
367  */
368 vnsetcred(vn, cred)
369 	register struct vn_softc *vn;
370 	struct ucred cred;
371 {
372 	struct uio auio;
373 	struct iovec aiov;
374 	char tmpbuf[DEV_BSIZE];
375 
376 	vn->sc_cred = crdup(cred);
377 	/* XXX: Horrible kludge to establish credentials for NFS */
378 	aiov.iov_base = tmpbuf;
379 	aiov.iov_len = min(DEV_BSIZE, dbtob(vn->sc_size));
380 	auio.uio_iov = &aiov;
381 	auio.uio_iovcnt = 1;
382 	auio.uio_offset = 0;
383 	auio.uio_rw = UIO_READ;
384 	auio.uio_segflg = UIO_SYSSPACE;
385 	auio.uio_resid = aiov.iov_len;
386 	return(VOP_READ(vn->sc_vp, &auio, 0, vn->sc_cred));
387 }
388 
389 /*
390  * Set maxactive based on FS type
391  */
392 vnthrottle(vn, vp)
393 	register struct vn_softc *vn;
394 	struct vnode *vp;
395 {
396 	extern int (**ufs_vnodeop_p)();
397 	extern int (**nfsv2_vnodeop_p)();
398 
399 	if (vp->v_op == nfsv2_vnodeop_p)
400 		vn->sc_maxactive = 2;
401 	else
402 		vn->sc_maxactive = 8;
403 
404 	if (vn->sc_maxactive < 1)
405 		vn->sc_maxactive = 1;
406 }
407 
408 vnshutdown()
409 {
410 	register struct vn_softc *vn;
411 
412 	for (vn = &vn_softc[0]; vn < &vn_softc[NVN]; vn++)
413 		if (vn->sc_flags & VNF_INITED)
414 			vnclear(vn);
415 }
416 
417 vnclear(vn)
418 	register struct vn_softc *vn;
419 {
420 	register struct vnode *vp = vn->sc_vp;
421 	struct proc *p = curproc;		/* XXX */
422 
423 #ifdef DEBUG
424 	if (vndebug & VDB_FOLLOW)
425 		printf("vnclear(%x): vp %x\n", vp);
426 #endif
427 	vn->sc_flags &= ~VNF_INITED;
428 	if (vp == (struct vnode *)0)
429 		panic("vnioctl: null vp");
430 #if 0
431 	/* XXX - this doesn't work right now */
432 	(void) VOP_FSYNC(vp, 0, vn->sc_cred, MNT_WAIT, p);
433 #endif
434 	(void) vn_close(vp, FREAD|FWRITE, vn->sc_cred, p);
435 	crfree(vn->sc_cred);
436 	vn->sc_vp = (struct vnode *)0;
437 	vn->sc_cred = (struct ucred *)0;
438 	vn->sc_size = 0;
439 }
440 
441 vnsize(dev)
442 	dev_t dev;
443 {
444 	int unit = vnunit(dev);
445 	register struct vn_softc *vn = &vn_softc[unit];
446 
447 	if (unit >= NVN || (vn->sc_flags & VNF_INITED) == 0)
448 		return(-1);
449 	return(vn->sc_size);
450 }
451 
452 vndump(dev)
453 {
454 	return(ENXIO);
455 }
456 #endif
457