xref: /original-bsd/sys/dev/vn.c (revision 68d9582f)
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.11 (Berkeley) 06/05/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 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);
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 void
215 vniodone(bp)
216 	register struct buf *bp;
217 {
218 	register struct buf *pbp = (struct buf *)bp->b_pfcent;	/* XXX */
219 	register int unit = vnunit(pbp->b_dev);
220 	int s;
221 
222 	s = splbio();
223 #ifdef DEBUG
224 	if (vndebug & VDB_IO)
225 		printf("vniodone(%d): bp %x vp %x blkno %x addr %x cnt %x\n",
226 		       unit, bp, bp->b_vp, bp->b_blkno, bp->b_un.b_addr,
227 		       bp->b_bcount);
228 #endif
229 	if (bp->b_error) {
230 #ifdef DEBUG
231 		if (vndebug & VDB_IO)
232 			printf("vniodone: bp %x error %d\n", bp, bp->b_error);
233 #endif
234 		pbp->b_flags |= B_ERROR;
235 		pbp->b_error = biowait(bp);
236 	}
237 	pbp->b_resid -= bp->b_bcount;
238 	putvnbuf(bp);
239 	if (pbp->b_resid == 0) {
240 #ifdef DEBUG
241 		if (vndebug & VDB_IO)
242 			printf("vniodone: pbp %x iodone\n", pbp);
243 #endif
244 		biodone(pbp);
245 	}
246 	if (vntab[unit].b_actf)
247 		vnstart(unit);
248 	else
249 		vntab[unit].b_active--;
250 	splx(s);
251 }
252 
253 vnread(dev, uio, flags, p)
254 	dev_t dev;
255 	struct uio *uio;
256 	int flags;
257 	struct proc *p;
258 {
259 	register int unit = vnunit(dev);
260 
261 #ifdef DEBUG
262 	if (vndebug & VDB_FOLLOW)
263 		printf("vnread(%x, %x, %x, %x)\n", dev, uio, flags, p);
264 #endif
265 	return(physio(vnstrategy, &vnbuf[unit], dev, B_READ, minphys, uio));
266 }
267 
268 vnwrite(dev, uio, flags, p)
269 	dev_t dev;
270 	struct uio *uio;
271 	int flags;
272 	struct proc *p;
273 {
274 	register int unit = vnunit(dev);
275 
276 #ifdef DEBUG
277 	if (vndebug & VDB_FOLLOW)
278 		printf("vnwrite(%x, %x, %x, %x)\n", dev, uio, flags, p);
279 #endif
280 	return(physio(vnstrategy, &vnbuf[unit], dev, B_WRITE, minphys, uio));
281 }
282 
283 /* ARGSUSED */
284 vnioctl(dev, cmd, data, flag, p)
285 	dev_t dev;
286 	u_long cmd;
287 	caddr_t data;
288 	int flag;
289 	struct proc *p;
290 {
291 	USES_VOP_GETATTR;
292 	USES_VOP_UNLOCK;
293 	int unit = vnunit(dev);
294 	register struct vn_softc *vn;
295 	struct vn_ioctl *vio;
296 	struct vattr vattr;
297 	struct nameidata nd;
298 	int error;
299 
300 #ifdef DEBUG
301 	if (vndebug & VDB_FOLLOW)
302 		printf("vnioctl(%x, %x, %x, %x, %x): unit %d\n",
303 		       dev, cmd, data, flag, p, unit);
304 #endif
305 	error = suser(p->p_ucred, &p->p_acflag);
306 	if (error)
307 		return (error);
308 	if (unit >= NVN)
309 		return (ENXIO);
310 
311 	vn = &vn_softc[unit];
312 	vio = (struct vn_ioctl *)data;
313 	switch (cmd) {
314 
315 	case VNIOCSET:
316 		if (vn->sc_flags & VNF_INITED)
317 			return(EBUSY);
318 		/*
319 		 * Always open for read and write.
320 		 * This is probably bogus, but it lets vn_open()
321 		 * weed out directories, sockets, etc. so we don't
322 		 * have to worry about them.
323 		 */
324 		NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, vio->vn_file, p);
325 		if (error = vn_open(&nd, FREAD|FWRITE, 0))
326 			return(error);
327 		if (error = VOP_GETATTR(nd.ni_vp, &vattr, p->p_ucred, p)) {
328 			VOP_UNLOCK(nd.ni_vp);
329 			(void) vn_close(nd.ni_vp, FREAD|FWRITE, p->p_ucred, p);
330 			return(error);
331 		}
332 		VOP_UNLOCK(nd.ni_vp);
333 		vn->sc_vp = nd.ni_vp;
334 		vn->sc_size = btodb(vattr.va_size);	/* note truncation */
335 		if (error = vnsetcred(vn, p->p_ucred)) {
336 			(void) vn_close(vn->sc_vp, FREAD|FWRITE, p->p_ucred, p);
337 			return(error);
338 		}
339 		vnthrottle(vn, vn->sc_vp);
340 		vio->vn_size = dbtob(vn->sc_size);
341 		vn->sc_flags |= VNF_INITED;
342 #ifdef DEBUG
343 		if (vndebug & VDB_INIT)
344 			printf("vnioctl: SET vp %x size %x\n",
345 			       vn->sc_vp, vn->sc_size);
346 #endif
347 		break;
348 
349 	case VNIOCCLR:
350 		if ((vn->sc_flags & VNF_INITED) == 0)
351 			return(ENXIO);
352 		vnclear(vn);
353 #ifdef DEBUG
354 		if (vndebug & VDB_INIT)
355 			printf("vnioctl: CLRed\n");
356 #endif
357 		break;
358 
359 	default:
360 		return(ENXIO);
361 	}
362 	return(0);
363 }
364 
365 /*
366  * Duplicate the current processes' credentials.  Since we are called only
367  * as the result of a SET ioctl and only root can do that, any future access
368  * to this "disk" is essentially as root.  Note that credentials may change
369  * if some other uid can write directly to the mapped file (NFS).
370  */
371 vnsetcred(vn, cred)
372 	register struct vn_softc *vn;
373 	struct ucred cred;
374 {
375 	USES_VOP_READ;
376 	struct uio auio;
377 	struct iovec aiov;
378 	char tmpbuf[DEV_BSIZE];
379 
380 	vn->sc_cred = crdup(cred);
381 	/* XXX: Horrible kludge to establish credentials for NFS */
382 	aiov.iov_base = tmpbuf;
383 	aiov.iov_len = MIN(DEV_BSIZE, dbtob(vn->sc_size));
384 	auio.uio_iov = &aiov;
385 	auio.uio_iovcnt = 1;
386 	auio.uio_offset = 0;
387 	auio.uio_rw = UIO_READ;
388 	auio.uio_segflg = UIO_SYSSPACE;
389 	auio.uio_resid = aiov.iov_len;
390 	return(VOP_READ(vn->sc_vp, &auio, 0, vn->sc_cred));
391 }
392 
393 /*
394  * Set maxactive based on FS type
395  */
396 vnthrottle(vn, vp)
397 	register struct vn_softc *vn;
398 	struct vnode *vp;
399 {
400 	extern int (**ufs_vnodeop_p)();
401 	extern int (**nfsv2_vnodeop_p)();
402 
403 	if (vp->v_op == nfsv2_vnodeop_p)
404 		vn->sc_maxactive = 2;
405 	else
406 		vn->sc_maxactive = 8;
407 
408 	if (vn->sc_maxactive < 1)
409 		vn->sc_maxactive = 1;
410 }
411 
412 vnshutdown()
413 {
414 	register struct vn_softc *vn;
415 
416 	for (vn = &vn_softc[0]; vn < &vn_softc[NVN]; vn++)
417 		if (vn->sc_flags & VNF_INITED)
418 			vnclear(vn);
419 }
420 
421 vnclear(vn)
422 	register struct vn_softc *vn;
423 {
424 	USES_VOP_FSYNC;
425 	register struct vnode *vp = vn->sc_vp;
426 	struct proc *p = curproc;		/* XXX */
427 
428 #ifdef DEBUG
429 	if (vndebug & VDB_FOLLOW)
430 		printf("vnclear(%x): vp %x\n", vp);
431 #endif
432 	vn->sc_flags &= ~VNF_INITED;
433 	if (vp == (struct vnode *)0)
434 		panic("vnioctl: null vp");
435 #if 0
436 	/* XXX - this doesn't work right now */
437 	(void) VOP_FSYNC(vp, 0, vn->sc_cred, MNT_WAIT, p);
438 #endif
439 	(void) vn_close(vp, FREAD|FWRITE, vn->sc_cred, p);
440 	crfree(vn->sc_cred);
441 	vn->sc_vp = (struct vnode *)0;
442 	vn->sc_cred = (struct ucred *)0;
443 	vn->sc_size = 0;
444 }
445 
446 vnsize(dev)
447 	dev_t dev;
448 {
449 	int unit = vnunit(dev);
450 	register struct vn_softc *vn = &vn_softc[unit];
451 
452 	if (unit >= NVN || (vn->sc_flags & VNF_INITED) == 0)
453 		return(-1);
454 	return(vn->sc_size);
455 }
456 
457 vndump(dev)
458 {
459 	return(ENXIO);
460 }
461 #endif
462