Commit | Line | Data |
---|---|---|
60f56dfc KM |
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 | * | |
0e1872ad | 12 | * from: Utah $Hdr: fd.c 1.1 90/07/09$ |
60f56dfc | 13 | * |
b28b3a13 | 14 | * @(#)vn.c 7.3 (Berkeley) %G% |
60f56dfc KM |
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 | ||
b28b3a13 KB |
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" | |
60f56dfc KM |
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 |