xref: /original-bsd/sys/dev/vn.c (revision cfa2a17a)
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.5 (Berkeley) 05/07/91
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 	int unit = vnunit(bp->b_dev);
110 	register struct vn_softc *vn = &vn_softc[unit];
111 	register struct buf *nbp;
112 	register int bn, bsize, resid;
113 	register caddr_t addr;
114 	int sz, flags;
115 	extern int vniodone();
116 
117 #ifdef DEBUG
118 	if (vndebug & VDB_FOLLOW)
119 		printf("vnstrategy(%x): unit %d\n", bp, unit);
120 #endif
121 	if ((vn->sc_flags & VNF_INITED) == 0) {
122 		bp->b_error = ENXIO;
123 		bp->b_flags |= B_ERROR;
124 		biodone(bp);
125 		return;
126 	}
127 	bn = bp->b_blkno;
128 	sz = howmany(bp->b_bcount, DEV_BSIZE);
129 	bp->b_resid = bp->b_bcount;
130 	if (bn < 0 || bn + sz > vn->sc_size) {
131 		if (bn != vn->sc_size) {
132 			bp->b_error = EINVAL;
133 			bp->b_flags |= B_ERROR;
134 		}
135 		biodone(bp);
136 		return;
137 	}
138 	bn = dbtob(bn);
139 	bsize = vn->sc_vp->v_mount->mnt_stat.f_bsize;
140 	addr = bp->b_un.b_addr;
141 	flags = bp->b_flags | B_CALL;
142 	for (resid = bp->b_resid; resid; resid -= sz) {
143 		struct vnode *vp;
144 		daddr_t nbn;
145 		int off, s;
146 
147 		nbp = getvnbuf();
148 		off = bn % bsize;
149 		sz = MIN(bsize - off, resid);
150 		(void) VOP_BMAP(vn->sc_vp, bn / bsize, &vp, &nbn);
151 #ifdef DEBUG
152 		if (vndebug & VDB_IO)
153 			printf("vnstrategy: vp %x/%x bn %x/%x\n",
154 			       vn->sc_vp, vp, bn, nbn);
155 #endif
156 		nbp->b_flags = flags;
157 		nbp->b_bcount = sz;
158 		nbp->b_bufsize = bp->b_bufsize;
159 		nbp->b_error = 0;
160 		if (vp->v_type == VBLK || vp->v_type == VCHR)
161 			nbp->b_dev = vp->v_rdev;
162 		else
163 			nbp->b_dev = NODEV;
164 		nbp->b_un.b_addr = addr;
165 		nbp->b_blkno = nbn + btodb(off);
166 		nbp->b_proc = bp->b_proc;
167 		nbp->b_iodone = vniodone;
168 		nbp->b_vp = vp;
169 		nbp->b_pfcent = (int) bp;	/* XXX */
170 		/*
171 		 * Just sort by block number
172 		 */
173 		nbp->b_cylin = nbp->b_blkno;
174 		s = splbio();
175 		disksort(&vntab[unit], nbp);
176 		if (vntab[unit].b_active < vn->sc_maxactive) {
177 			vntab[unit].b_active++;
178 			vnstart(unit);
179 		}
180 		splx(s);
181 		bn += sz;
182 		addr += sz;
183 	}
184 }
185 
186 /*
187  * Feed requests sequentially.
188  * We do it this way to keep from flooding NFS servers if we are connected
189  * to an NFS file.  This places the burden on the client rather than the
190  * server.
191  */
192 vnstart(unit)
193 {
194 	register struct vn_softc *vn = &vn_softc[unit];
195 	register struct buf *bp;
196 
197 	/*
198 	 * Dequeue now since lower level strategy routine might
199 	 * queue using same links
200 	 */
201 	bp = vntab[unit].b_actf;
202 	vntab[unit].b_actf = bp->b_actf;
203 #ifdef DEBUG
204 	if (vndebug & VDB_IO)
205 		printf("vnstart(%d): bp %x vp %x blkno %x addr %x cnt %x\n",
206 		       unit, bp, bp->b_vp, bp->b_blkno, bp->b_un.b_addr,
207 		       bp->b_bcount);
208 #endif
209 	VOP_STRATEGY(bp);
210 }
211 
212 vniodone(bp)
213 	register struct buf *bp;
214 {
215 	register struct buf *pbp = (struct buf *)bp->b_pfcent;	/* XXX */
216 	register int unit = vnunit(pbp->b_dev);
217 	int s;
218 
219 	s = splbio();
220 #ifdef DEBUG
221 	if (vndebug & VDB_IO)
222 		printf("vniodone(%d): bp %x vp %x blkno %x addr %x cnt %x\n",
223 		       unit, bp, bp->b_vp, bp->b_blkno, bp->b_un.b_addr,
224 		       bp->b_bcount);
225 #endif
226 	if (bp->b_error) {
227 #ifdef DEBUG
228 		if (vndebug & VDB_IO)
229 			printf("vniodone: bp %x error %d\n", bp, bp->b_error);
230 #endif
231 		pbp->b_flags |= B_ERROR;
232 		pbp->b_error = biowait(bp);
233 	}
234 	pbp->b_resid -= bp->b_bcount;
235 	putvnbuf(bp);
236 	if (pbp->b_resid == 0) {
237 #ifdef DEBUG
238 		if (vndebug & VDB_IO)
239 			printf("vniodone: pbp %x iodone\n", pbp);
240 #endif
241 		biodone(pbp);
242 	}
243 	if (vntab[unit].b_actf)
244 		vnstart(unit);
245 	else
246 		vntab[unit].b_active--;
247 	splx(s);
248 }
249 
250 vnread(dev, uio, flags, p)
251 	dev_t dev;
252 	struct uio *uio;
253 	int flags;
254 	struct proc *p;
255 {
256 	register int unit = vnunit(dev);
257 
258 #ifdef DEBUG
259 	if (vndebug & VDB_FOLLOW)
260 		printf("vnread(%x, %x, %x, %x)\n", dev, uio, flags, p);
261 #endif
262 	return(physio(vnstrategy, &vnbuf[unit], dev, B_READ, minphys, uio));
263 }
264 
265 vnwrite(dev, uio, flags, p)
266 	dev_t dev;
267 	struct uio *uio;
268 	int flags;
269 	struct proc *p;
270 {
271 	register int unit = vnunit(dev);
272 
273 #ifdef DEBUG
274 	if (vndebug & VDB_FOLLOW)
275 		printf("vnwrite(%x, %x, %x, %x)\n", dev, uio, flags, p);
276 #endif
277 	return(physio(vnstrategy, &vnbuf[unit], dev, B_WRITE, minphys, uio));
278 }
279 
280 /* ARGSUSED */
281 vnioctl(dev, cmd, data, flag, p)
282 	dev_t dev;
283 	u_long cmd;
284 	caddr_t data;
285 	int flag;
286 	struct proc *p;
287 {
288 	int unit = vnunit(dev);
289 	register struct vn_softc *vn;
290 	struct vn_ioctl *vio;
291 	struct vattr vattr;
292 	struct nameidata nd;
293 	int error;
294 
295 #ifdef DEBUG
296 	if (vndebug & VDB_FOLLOW)
297 		printf("vnioctl(%x, %x, %x, %x, %x): unit %d\n",
298 		       dev, cmd, data, flag, p, unit);
299 #endif
300 	error = suser(p->p_ucred, &p->p_acflag);
301 	if (error)
302 		return (error);
303 	if (unit >= NVN)
304 		return (ENXIO);
305 
306 	vn = &vn_softc[unit];
307 	vio = (struct vn_ioctl *)data;
308 	switch (cmd) {
309 
310 	case VNIOCSET:
311 		if (vn->sc_flags & VNF_INITED)
312 			return(EBUSY);
313 		/*
314 		 * Always open for read and write.
315 		 * This is probably bogus, but it lets vn_open()
316 		 * weed out directories, sockets, etc. so we don't
317 		 * have to worry about them.
318 		 */
319 		nd.ni_segflg = UIO_USERSPACE;
320 		nd.ni_dirp = vio->vn_file;
321 		error = vn_open(&nd, p, FREAD|FWRITE, 0);
322 		if (error)
323 			return(error);
324 		error = VOP_GETATTR(nd.ni_vp, &vattr, p->p_ucred, p);
325 		if (error) {
326 			vrele(nd.ni_vp);
327 			return(error);
328 		}
329 		vn->sc_vp = nd.ni_vp;
330 		vn->sc_size = btodb(vattr.va_size);	/* note truncation */
331 		error = vnsetcred(vn, p->p_ucred);
332 		if (error) {
333 			vrele(vn->sc_vp);
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 struct vnodeops ufs_vnodeops, nfsv2_vnodeops;
397 
398 	if (vp->v_op == &nfsv2_vnodeops)
399 		vn->sc_maxactive = 2;
400 	else
401 		vn->sc_maxactive = 8;
402 
403 	if (vn->sc_maxactive < 1)
404 		vn->sc_maxactive = 1;
405 }
406 
407 vnshutdown()
408 {
409 	register struct vn_softc *vn;
410 
411 	for (vn = &vn_softc[0]; vn < &vn_softc[NVN]; vn++)
412 		if (vn->sc_flags & VNF_INITED)
413 			vnclear(vn);
414 }
415 
416 vnclear(vn)
417 	register struct vn_softc *vn;
418 {
419 	register struct vnode *vp = vn->sc_vp;
420 
421 #ifdef DEBUG
422 	if (vndebug & VDB_FOLLOW)
423 		printf("vnclear(%x): vp %x\n", vp);
424 #endif
425 	vn->sc_flags &= ~VNF_INITED;
426 	if (vp == (struct vnode *)0)
427 		panic("vnioctl: null vp");
428 #if 0
429 	/* XXX - this doesn't work right now */
430 	(void) VOP_FSYNC(vp, 0, vn->sc_cred, MNT_WAIT, p);
431 #endif
432 	vrele(vp);
433 	crfree(vn->sc_cred);
434 	vn->sc_vp = (struct vnode *)0;
435 	vn->sc_cred = (struct ucred *)0;
436 	vn->sc_size = 0;
437 }
438 
439 vnsize(dev)
440 	dev_t dev;
441 {
442 	int unit = vnunit(dev);
443 	register struct vn_softc *vn = &vn_softc[unit];
444 
445 	if (unit >= NVN || (vn->sc_flags & VNF_INITED) == 0)
446 		return(-1);
447 	return(vn->sc_size);
448 }
449 
450 vndump(dev)
451 {
452 	return(ENXIO);
453 }
454 #endif
455