xref: /csrg-svn/sys/dev/vn.c (revision 53517)
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: vn.c 1.1 91/04/30$
13  *
14  *	@(#)vn.c	7.10 (Berkeley) 05/14/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/specdev.h"
48 #include "sys/file.h"
49 #include "sys/uio.h"
50 
51 #include "vnioctl.h"
52 
53 #ifdef DEBUG
54 int vndebug = 0x00;
55 #define VDB_FOLLOW	0x01
56 #define VDB_INIT	0x02
57 #define VDB_IO		0x04
58 #endif
59 
60 struct	buf vnbuf[NVN];
61 struct	buf vntab[NVN];
62 
63 #define b_cylin	b_resid
64 
65 #define	vnunit(x)	((minor(x) >> 3) & 0x7)	/* for consistency */
66 
67 #define	getvnbuf()	\
68 	((struct buf *)malloc(sizeof(struct buf), M_DEVBUF, M_WAITOK))
69 #define putvnbuf(bp)	\
70 	free((caddr_t)(bp), M_DEVBUF)
71 
72 struct vn_softc {
73 	int		 sc_flags;	/* flags */
74 	size_t		 sc_size;	/* size of vn */
75 	struct vnode	*sc_vp;		/* vnode */
76 	struct ucred	*sc_cred;	/* credentials */
77 	int		 sc_maxactive;	/* max # of active requests */
78 } vn_softc[NVN];
79 
80 /* sc_flags */
81 #define	VNF_ALIVE	0x01
82 #define VNF_INITED	0x02
83 
84 int
85 vnopen(dev, flags, mode, p)
86 	dev_t dev;
87 	int flags, mode;
88 	struct proc *p;
89 {
90 	int unit = vnunit(dev);
91 
92 #ifdef DEBUG
93 	if (vndebug & VDB_FOLLOW)
94 		printf("vnopen(%x, %x, %x, %x)\n", dev, flags, mode, p);
95 #endif
96 	if (unit >= NVN)
97 		return(ENXIO);
98 	return(0);
99 }
100 
101 /*
102  * Break the request into bsize pieces and submit using VOP_BMAP/VOP_STRATEGY.
103  * Note that this driver can only be used for swapping over NFS on the hp
104  * since nfs_strategy on the vax cannot handle u-areas and page tables.
105  */
106 vnstrategy(bp)
107 	register struct buf *bp;
108 {
109 	USES_VOP_BMAP;
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 int 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);
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 	USES_VOP_STRATEGY;
196 	register struct vn_softc *vn = &vn_softc[unit];
197 	register struct buf *bp;
198 
199 	/*
200 	 * Dequeue now since lower level strategy routine might
201 	 * queue using same links
202 	 */
203 	bp = vntab[unit].b_actf;
204 	vntab[unit].b_actf = bp->b_actf;
205 #ifdef DEBUG
206 	if (vndebug & VDB_IO)
207 		printf("vnstart(%d): bp %x vp %x blkno %x addr %x cnt %x\n",
208 		       unit, bp, bp->b_vp, bp->b_blkno, bp->b_un.b_addr,
209 		       bp->b_bcount);
210 #endif
211 	VOP_STRATEGY(bp);
212 }
213 
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 	USES_VOP_GETATTR;
291 	USES_VOP_UNLOCK;
292 	int unit = vnunit(dev);
293 	register struct vn_softc *vn;
294 	struct vn_ioctl *vio;
295 	struct vattr vattr;
296 	struct nameidata nd;
297 	int error;
298 
299 #ifdef DEBUG
300 	if (vndebug & VDB_FOLLOW)
301 		printf("vnioctl(%x, %x, %x, %x, %x): unit %d\n",
302 		       dev, cmd, data, flag, p, unit);
303 #endif
304 	error = suser(p->p_ucred, &p->p_acflag);
305 	if (error)
306 		return (error);
307 	if (unit >= NVN)
308 		return (ENXIO);
309 
310 	vn = &vn_softc[unit];
311 	vio = (struct vn_ioctl *)data;
312 	switch (cmd) {
313 
314 	case VNIOCSET:
315 		if (vn->sc_flags & VNF_INITED)
316 			return(EBUSY);
317 		/*
318 		 * Always open for read and write.
319 		 * This is probably bogus, but it lets vn_open()
320 		 * weed out directories, sockets, etc. so we don't
321 		 * have to worry about them.
322 		 */
323 		NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, vio->vn_file, p);
324 		if (error = vn_open(&nd, FREAD|FWRITE, 0))
325 			return(error);
326 		if (error = VOP_GETATTR(nd.ni_vp, &vattr, p->p_ucred, p)) {
327 			VOP_UNLOCK(nd.ni_vp);
328 			(void) vn_close(nd.ni_vp, FREAD|FWRITE, p->p_ucred, p);
329 			return(error);
330 		}
331 		VOP_UNLOCK(nd.ni_vp);
332 		vn->sc_vp = nd.ni_vp;
333 		vn->sc_size = btodb(vattr.va_size);	/* note truncation */
334 		if (error = vnsetcred(vn, p->p_ucred)) {
335 			(void) vn_close(vn->sc_vp, FREAD|FWRITE, p->p_ucred, p);
336 			return(error);
337 		}
338 		vnthrottle(vn, vn->sc_vp);
339 		vio->vn_size = dbtob(vn->sc_size);
340 		vn->sc_flags |= VNF_INITED;
341 #ifdef DEBUG
342 		if (vndebug & VDB_INIT)
343 			printf("vnioctl: SET vp %x size %x\n",
344 			       vn->sc_vp, vn->sc_size);
345 #endif
346 		break;
347 
348 	case VNIOCCLR:
349 		if ((vn->sc_flags & VNF_INITED) == 0)
350 			return(ENXIO);
351 		vnclear(vn);
352 #ifdef DEBUG
353 		if (vndebug & VDB_INIT)
354 			printf("vnioctl: CLRed\n");
355 #endif
356 		break;
357 
358 	default:
359 		return(ENXIO);
360 	}
361 	return(0);
362 }
363 
364 /*
365  * Duplicate the current processes' credentials.  Since we are called only
366  * as the result of a SET ioctl and only root can do that, any future access
367  * to this "disk" is essentially as root.  Note that credentials may change
368  * if some other uid can write directly to the mapped file (NFS).
369  */
370 vnsetcred(vn, cred)
371 	register struct vn_softc *vn;
372 	struct ucred cred;
373 {
374 	USES_VOP_READ;
375 	struct uio auio;
376 	struct iovec aiov;
377 	char tmpbuf[DEV_BSIZE];
378 
379 	vn->sc_cred = crdup(cred);
380 	/* XXX: Horrible kludge to establish credentials for NFS */
381 	aiov.iov_base = tmpbuf;
382 	aiov.iov_len = MIN(DEV_BSIZE, dbtob(vn->sc_size));
383 	auio.uio_iov = &aiov;
384 	auio.uio_iovcnt = 1;
385 	auio.uio_offset = 0;
386 	auio.uio_rw = UIO_READ;
387 	auio.uio_segflg = UIO_SYSSPACE;
388 	auio.uio_resid = aiov.iov_len;
389 	return(VOP_READ(vn->sc_vp, &auio, 0, vn->sc_cred));
390 }
391 
392 /*
393  * Set maxactive based on FS type
394  */
395 vnthrottle(vn, vp)
396 	register struct vn_softc *vn;
397 	struct vnode *vp;
398 {
399 	extern int (**ufs_vnodeop_p)();
400 	extern int (**nfsv2_vnodeop_p)();
401 
402 	if (vp->v_op == nfsv2_vnodeop_p)
403 		vn->sc_maxactive = 2;
404 	else
405 		vn->sc_maxactive = 8;
406 
407 	if (vn->sc_maxactive < 1)
408 		vn->sc_maxactive = 1;
409 }
410 
411 vnshutdown()
412 {
413 	register struct vn_softc *vn;
414 
415 	for (vn = &vn_softc[0]; vn < &vn_softc[NVN]; vn++)
416 		if (vn->sc_flags & VNF_INITED)
417 			vnclear(vn);
418 }
419 
420 vnclear(vn)
421 	register struct vn_softc *vn;
422 {
423 	USES_VOP_FSYNC;
424 	register struct vnode *vp = vn->sc_vp;
425 	struct proc *p = curproc;		/* XXX */
426 
427 #ifdef DEBUG
428 	if (vndebug & VDB_FOLLOW)
429 		printf("vnclear(%x): vp %x\n", vp);
430 #endif
431 	vn->sc_flags &= ~VNF_INITED;
432 	if (vp == (struct vnode *)0)
433 		panic("vnioctl: null vp");
434 #if 0
435 	/* XXX - this doesn't work right now */
436 	(void) VOP_FSYNC(vp, 0, vn->sc_cred, MNT_WAIT, p);
437 #endif
438 	(void) vn_close(vp, FREAD|FWRITE, vn->sc_cred, p);
439 	crfree(vn->sc_cred);
440 	vn->sc_vp = (struct vnode *)0;
441 	vn->sc_cred = (struct ucred *)0;
442 	vn->sc_size = 0;
443 }
444 
445 vnsize(dev)
446 	dev_t dev;
447 {
448 	int unit = vnunit(dev);
449 	register struct vn_softc *vn = &vn_softc[unit];
450 
451 	if (unit >= NVN || (vn->sc_flags & VNF_INITED) == 0)
452 		return(-1);
453 	return(vn->sc_size);
454 }
455 
456 vndump(dev)
457 {
458 	return(ENXIO);
459 }
460 #endif
461