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