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