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