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 | * | |
0c17d899 | 12 | * from: Utah $Hdr: vn.c 1.1 91/04/30$ |
60f56dfc | 13 | * |
55ff681d | 14 | * @(#)vn.c 7.8 (Berkeley) %G% |
60f56dfc KM |
15 | */ |
16 | ||
0893c353 | 17 | /* |
0c17d899 | 18 | * Vnode disk driver. |
60f56dfc | 19 | * |
0c17d899 MH |
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.). | |
60f56dfc | 22 | * |
0c17d899 MH |
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. | |
60f56dfc KM |
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 | */ | |
0c17d899 MH |
33 | #include "vn.h" |
34 | #if NVN > 0 | |
60f56dfc | 35 | |
b28b3a13 KB |
36 | #include "sys/param.h" |
37 | #include "sys/systm.h" | |
0c17d899 MH |
38 | #include "sys/namei.h" |
39 | #include "sys/proc.h" | |
b28b3a13 KB |
40 | #include "sys/errno.h" |
41 | #include "sys/dkstat.h" | |
0c17d899 MH |
42 | #include "sys/buf.h" |
43 | #include "sys/malloc.h" | |
b28b3a13 | 44 | #include "sys/ioctl.h" |
0c17d899 | 45 | #include "sys/mount.h" |
b28b3a13 | 46 | #include "sys/vnode.h" |
0c17d899 | 47 | #include "sys/specdev.h" |
b28b3a13 KB |
48 | #include "sys/file.h" |
49 | #include "sys/uio.h" | |
60f56dfc | 50 | |
0c17d899 | 51 | #include "vnioctl.h" |
60f56dfc KM |
52 | |
53 | #ifdef DEBUG | |
0c17d899 MH |
54 | int vndebug = 0x00; |
55 | #define VDB_FOLLOW 0x01 | |
56 | #define VDB_INIT 0x02 | |
57 | #define VDB_IO 0x04 | |
60f56dfc KM |
58 | #endif |
59 | ||
0c17d899 MH |
60 | struct buf vnbuf[NVN]; |
61 | struct buf vntab[NVN]; | |
60f56dfc KM |
62 | |
63 | #define b_cylin b_resid | |
64 | ||
0c17d899 | 65 | #define vnunit(x) ((minor(x) >> 3) & 0x7) /* for consistency */ |
60f56dfc | 66 | |
0c17d899 | 67 | #define getvnbuf() \ |
60f56dfc | 68 | ((struct buf *)malloc(sizeof(struct buf), M_DEVBUF, M_WAITOK)) |
0c17d899 | 69 | #define putvnbuf(bp) \ |
60f56dfc KM |
70 | free((caddr_t)(bp), M_DEVBUF) |
71 | ||
0c17d899 | 72 | struct vn_softc { |
60f56dfc | 73 | int sc_flags; /* flags */ |
0c17d899 | 74 | size_t sc_size; /* size of vn */ |
60f56dfc KM |
75 | struct vnode *sc_vp; /* vnode */ |
76 | struct ucred *sc_cred; /* credentials */ | |
77 | int sc_maxactive; /* max # of active requests */ | |
0c17d899 | 78 | } vn_softc[NVN]; |
60f56dfc KM |
79 | |
80 | /* sc_flags */ | |
0c17d899 MH |
81 | #define VNF_ALIVE 0x01 |
82 | #define VNF_INITED 0x02 | |
60f56dfc | 83 | |
0c17d899 MH |
84 | int |
85 | vnopen(dev, flags, mode, p) | |
60f56dfc | 86 | dev_t dev; |
0c17d899 MH |
87 | int flags, mode; |
88 | struct proc *p; | |
60f56dfc | 89 | { |
0c17d899 | 90 | int unit = vnunit(dev); |
60f56dfc KM |
91 | |
92 | #ifdef DEBUG | |
0c17d899 MH |
93 | if (vndebug & VDB_FOLLOW) |
94 | printf("vnopen(%x, %x, %x, %x)\n", dev, flags, mode, p); | |
60f56dfc | 95 | #endif |
0c17d899 | 96 | if (unit >= NVN) |
60f56dfc KM |
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 | */ | |
0c17d899 | 106 | vnstrategy(bp) |
60f56dfc KM |
107 | register struct buf *bp; |
108 | { | |
0c17d899 MH |
109 | int unit = vnunit(bp->b_dev); |
110 | register struct vn_softc *vn = &vn_softc[unit]; | |
60f56dfc KM |
111 | register struct buf *nbp; |
112 | register int bn, bsize, resid; | |
113 | register caddr_t addr; | |
114 | int sz, flags; | |
0c17d899 | 115 | extern int vniodone(); |
60f56dfc KM |
116 | |
117 | #ifdef DEBUG | |
0c17d899 MH |
118 | if (vndebug & VDB_FOLLOW) |
119 | printf("vnstrategy(%x): unit %d\n", bp, unit); | |
60f56dfc | 120 | #endif |
0c17d899 | 121 | if ((vn->sc_flags & VNF_INITED) == 0) { |
60f56dfc KM |
122 | bp->b_error = ENXIO; |
123 | bp->b_flags |= B_ERROR; | |
0c17d899 | 124 | biodone(bp); |
60f56dfc KM |
125 | return; |
126 | } | |
127 | bn = bp->b_blkno; | |
128 | sz = howmany(bp->b_bcount, DEV_BSIZE); | |
129 | bp->b_resid = bp->b_bcount; | |
0c17d899 MH |
130 | if (bn < 0 || bn + sz > vn->sc_size) { |
131 | if (bn != vn->sc_size) { | |
60f56dfc KM |
132 | bp->b_error = EINVAL; |
133 | bp->b_flags |= B_ERROR; | |
134 | } | |
0c17d899 | 135 | biodone(bp); |
60f56dfc KM |
136 | return; |
137 | } | |
138 | bn = dbtob(bn); | |
cc173077 | 139 | bsize = vn->sc_vp->v_mount->mnt_stat.f_iosize; |
60f56dfc KM |
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 | ||
0c17d899 | 147 | nbp = getvnbuf(); |
60f56dfc KM |
148 | off = bn % bsize; |
149 | sz = MIN(bsize - off, resid); | |
0c17d899 | 150 | (void) VOP_BMAP(vn->sc_vp, bn / bsize, &vp, &nbn); |
60f56dfc | 151 | #ifdef DEBUG |
0c17d899 MH |
152 | if (vndebug & VDB_IO) |
153 | printf("vnstrategy: vp %x/%x bn %x/%x\n", | |
154 | vn->sc_vp, vp, bn, nbn); | |
60f56dfc KM |
155 | #endif |
156 | nbp->b_flags = flags; | |
157 | nbp->b_bcount = sz; | |
158 | nbp->b_bufsize = bp->b_bufsize; | |
159 | nbp->b_error = 0; | |
0c17d899 MH |
160 | if (vp->v_type == VBLK || vp->v_type == VCHR) |
161 | nbp->b_dev = vp->v_rdev; | |
162 | else | |
163 | nbp->b_dev = NODEV; | |
60f56dfc KM |
164 | nbp->b_un.b_addr = addr; |
165 | nbp->b_blkno = nbn + btodb(off); | |
166 | nbp->b_proc = bp->b_proc; | |
0c17d899 | 167 | nbp->b_iodone = vniodone; |
60f56dfc KM |
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(); | |
0c17d899 MH |
175 | disksort(&vntab[unit], nbp); |
176 | if (vntab[unit].b_active < vn->sc_maxactive) { | |
177 | vntab[unit].b_active++; | |
178 | vnstart(unit); | |
60f56dfc KM |
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 | */ | |
0c17d899 | 192 | vnstart(unit) |
60f56dfc | 193 | { |
0c17d899 | 194 | register struct vn_softc *vn = &vn_softc[unit]; |
60f56dfc KM |
195 | register struct buf *bp; |
196 | ||
197 | /* | |
198 | * Dequeue now since lower level strategy routine might | |
199 | * queue using same links | |
200 | */ | |
0c17d899 MH |
201 | bp = vntab[unit].b_actf; |
202 | vntab[unit].b_actf = bp->b_actf; | |
60f56dfc | 203 | #ifdef DEBUG |
0c17d899 MH |
204 | if (vndebug & VDB_IO) |
205 | printf("vnstart(%d): bp %x vp %x blkno %x addr %x cnt %x\n", | |
60f56dfc KM |
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 | ||
0c17d899 | 212 | vniodone(bp) |
60f56dfc KM |
213 | register struct buf *bp; |
214 | { | |
215 | register struct buf *pbp = (struct buf *)bp->b_pfcent; /* XXX */ | |
0c17d899 | 216 | register int unit = vnunit(pbp->b_dev); |
60f56dfc KM |
217 | int s; |
218 | ||
219 | s = splbio(); | |
220 | #ifdef DEBUG | |
0c17d899 MH |
221 | if (vndebug & VDB_IO) |
222 | printf("vniodone(%d): bp %x vp %x blkno %x addr %x cnt %x\n", | |
60f56dfc KM |
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 | |
0c17d899 MH |
228 | if (vndebug & VDB_IO) |
229 | printf("vniodone: bp %x error %d\n", bp, bp->b_error); | |
60f56dfc KM |
230 | #endif |
231 | pbp->b_flags |= B_ERROR; | |
0c17d899 | 232 | pbp->b_error = biowait(bp); |
60f56dfc KM |
233 | } |
234 | pbp->b_resid -= bp->b_bcount; | |
0c17d899 | 235 | putvnbuf(bp); |
60f56dfc KM |
236 | if (pbp->b_resid == 0) { |
237 | #ifdef DEBUG | |
0c17d899 MH |
238 | if (vndebug & VDB_IO) |
239 | printf("vniodone: pbp %x iodone\n", pbp); | |
60f56dfc | 240 | #endif |
0c17d899 | 241 | biodone(pbp); |
60f56dfc | 242 | } |
0c17d899 MH |
243 | if (vntab[unit].b_actf) |
244 | vnstart(unit); | |
60f56dfc | 245 | else |
0c17d899 | 246 | vntab[unit].b_active--; |
60f56dfc KM |
247 | splx(s); |
248 | } | |
249 | ||
0c17d899 | 250 | vnread(dev, uio, flags, p) |
60f56dfc KM |
251 | dev_t dev; |
252 | struct uio *uio; | |
0c17d899 MH |
253 | int flags; |
254 | struct proc *p; | |
60f56dfc | 255 | { |
0c17d899 | 256 | register int unit = vnunit(dev); |
60f56dfc KM |
257 | |
258 | #ifdef DEBUG | |
0c17d899 MH |
259 | if (vndebug & VDB_FOLLOW) |
260 | printf("vnread(%x, %x, %x, %x)\n", dev, uio, flags, p); | |
60f56dfc | 261 | #endif |
0c17d899 | 262 | return(physio(vnstrategy, &vnbuf[unit], dev, B_READ, minphys, uio)); |
60f56dfc KM |
263 | } |
264 | ||
0c17d899 | 265 | vnwrite(dev, uio, flags, p) |
60f56dfc KM |
266 | dev_t dev; |
267 | struct uio *uio; | |
0c17d899 MH |
268 | int flags; |
269 | struct proc *p; | |
60f56dfc | 270 | { |
0c17d899 | 271 | register int unit = vnunit(dev); |
60f56dfc KM |
272 | |
273 | #ifdef DEBUG | |
0c17d899 MH |
274 | if (vndebug & VDB_FOLLOW) |
275 | printf("vnwrite(%x, %x, %x, %x)\n", dev, uio, flags, p); | |
60f56dfc | 276 | #endif |
0c17d899 | 277 | return(physio(vnstrategy, &vnbuf[unit], dev, B_WRITE, minphys, uio)); |
60f56dfc KM |
278 | } |
279 | ||
280 | /* ARGSUSED */ | |
0c17d899 | 281 | vnioctl(dev, cmd, data, flag, p) |
60f56dfc KM |
282 | dev_t dev; |
283 | u_long cmd; | |
284 | caddr_t data; | |
285 | int flag; | |
0c17d899 | 286 | struct proc *p; |
60f56dfc | 287 | { |
0c17d899 MH |
288 | int unit = vnunit(dev); |
289 | register struct vn_softc *vn; | |
290 | struct vn_ioctl *vio; | |
60f56dfc | 291 | struct vattr vattr; |
0c17d899 | 292 | struct nameidata nd; |
60f56dfc KM |
293 | int error; |
294 | ||
295 | #ifdef DEBUG | |
0c17d899 MH |
296 | if (vndebug & VDB_FOLLOW) |
297 | printf("vnioctl(%x, %x, %x, %x, %x): unit %d\n", | |
298 | dev, cmd, data, flag, p, unit); | |
60f56dfc | 299 | #endif |
0c17d899 | 300 | error = suser(p->p_ucred, &p->p_acflag); |
60f56dfc KM |
301 | if (error) |
302 | return (error); | |
0c17d899 | 303 | if (unit >= NVN) |
60f56dfc KM |
304 | return (ENXIO); |
305 | ||
0c17d899 MH |
306 | vn = &vn_softc[unit]; |
307 | vio = (struct vn_ioctl *)data; | |
60f56dfc KM |
308 | switch (cmd) { |
309 | ||
0c17d899 MH |
310 | case VNIOCSET: |
311 | if (vn->sc_flags & VNF_INITED) | |
60f56dfc KM |
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 | */ | |
55ff681d MH |
319 | NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, vio->vn_file, p); |
320 | if (error = vn_open(&nd, FREAD|FWRITE, 0)) | |
60f56dfc | 321 | return(error); |
f2eb348c KM |
322 | if (error = VOP_GETATTR(nd.ni_vp, &vattr, p->p_ucred, p)) { |
323 | VOP_UNLOCK(nd.ni_vp); | |
324 | (void) vn_close(nd.ni_vp, FREAD|FWRITE, p->p_ucred, p); | |
60f56dfc KM |
325 | return(error); |
326 | } | |
f2eb348c | 327 | VOP_UNLOCK(nd.ni_vp); |
0c17d899 MH |
328 | vn->sc_vp = nd.ni_vp; |
329 | vn->sc_size = btodb(vattr.va_size); /* note truncation */ | |
f2eb348c KM |
330 | if (error = vnsetcred(vn, p->p_ucred)) { |
331 | (void) vn_close(vn->sc_vp, FREAD|FWRITE, p->p_ucred, p); | |
60f56dfc KM |
332 | return(error); |
333 | } | |
0c17d899 MH |
334 | vnthrottle(vn, vn->sc_vp); |
335 | vio->vn_size = dbtob(vn->sc_size); | |
336 | vn->sc_flags |= VNF_INITED; | |
60f56dfc | 337 | #ifdef DEBUG |
0c17d899 MH |
338 | if (vndebug & VDB_INIT) |
339 | printf("vnioctl: SET vp %x size %x\n", | |
340 | vn->sc_vp, vn->sc_size); | |
60f56dfc KM |
341 | #endif |
342 | break; | |
343 | ||
0c17d899 MH |
344 | case VNIOCCLR: |
345 | if ((vn->sc_flags & VNF_INITED) == 0) | |
60f56dfc | 346 | return(ENXIO); |
0c17d899 | 347 | vnclear(vn); |
60f56dfc | 348 | #ifdef DEBUG |
0c17d899 MH |
349 | if (vndebug & VDB_INIT) |
350 | printf("vnioctl: CLRed\n"); | |
60f56dfc KM |
351 | #endif |
352 | break; | |
353 | ||
354 | default: | |
355 | return(ENXIO); | |
356 | } | |
357 | return(0); | |
358 | } | |
359 | ||
360 | /* | |
361 | * Duplicate the current processes' credentials. Since we are called only | |
362 | * as the result of a SET ioctl and only root can do that, any future access | |
363 | * to this "disk" is essentially as root. Note that credentials may change | |
364 | * if some other uid can write directly to the mapped file (NFS). | |
365 | */ | |
0c17d899 MH |
366 | vnsetcred(vn, cred) |
367 | register struct vn_softc *vn; | |
368 | struct ucred cred; | |
60f56dfc KM |
369 | { |
370 | struct uio auio; | |
371 | struct iovec aiov; | |
372 | char tmpbuf[DEV_BSIZE]; | |
373 | ||
0c17d899 | 374 | vn->sc_cred = crdup(cred); |
60f56dfc KM |
375 | /* XXX: Horrible kludge to establish credentials for NFS */ |
376 | aiov.iov_base = tmpbuf; | |
0c17d899 | 377 | aiov.iov_len = MIN(DEV_BSIZE, dbtob(vn->sc_size)); |
60f56dfc KM |
378 | auio.uio_iov = &aiov; |
379 | auio.uio_iovcnt = 1; | |
380 | auio.uio_offset = 0; | |
381 | auio.uio_rw = UIO_READ; | |
382 | auio.uio_segflg = UIO_SYSSPACE; | |
383 | auio.uio_resid = aiov.iov_len; | |
0c17d899 | 384 | return(VOP_READ(vn->sc_vp, &auio, 0, vn->sc_cred)); |
60f56dfc KM |
385 | } |
386 | ||
387 | /* | |
388 | * Set maxactive based on FS type | |
389 | */ | |
0c17d899 MH |
390 | vnthrottle(vn, vp) |
391 | register struct vn_softc *vn; | |
60f56dfc KM |
392 | struct vnode *vp; |
393 | { | |
0c17d899 | 394 | extern struct vnodeops ufs_vnodeops, nfsv2_vnodeops; |
60f56dfc | 395 | |
0c17d899 MH |
396 | if (vp->v_op == &nfsv2_vnodeops) |
397 | vn->sc_maxactive = 2; | |
60f56dfc | 398 | else |
0c17d899 | 399 | vn->sc_maxactive = 8; |
60f56dfc | 400 | |
0c17d899 MH |
401 | if (vn->sc_maxactive < 1) |
402 | vn->sc_maxactive = 1; | |
60f56dfc KM |
403 | } |
404 | ||
0c17d899 | 405 | vnshutdown() |
60f56dfc | 406 | { |
0c17d899 | 407 | register struct vn_softc *vn; |
60f56dfc | 408 | |
0c17d899 MH |
409 | for (vn = &vn_softc[0]; vn < &vn_softc[NVN]; vn++) |
410 | if (vn->sc_flags & VNF_INITED) | |
411 | vnclear(vn); | |
60f56dfc KM |
412 | } |
413 | ||
0c17d899 MH |
414 | vnclear(vn) |
415 | register struct vn_softc *vn; | |
60f56dfc | 416 | { |
0c17d899 | 417 | register struct vnode *vp = vn->sc_vp; |
f2eb348c | 418 | struct proc *p = curproc; /* XXX */ |
60f56dfc KM |
419 | |
420 | #ifdef DEBUG | |
0c17d899 MH |
421 | if (vndebug & VDB_FOLLOW) |
422 | printf("vnclear(%x): vp %x\n", vp); | |
60f56dfc | 423 | #endif |
0c17d899 | 424 | vn->sc_flags &= ~VNF_INITED; |
60f56dfc | 425 | if (vp == (struct vnode *)0) |
0c17d899 | 426 | panic("vnioctl: null vp"); |
60f56dfc KM |
427 | #if 0 |
428 | /* XXX - this doesn't work right now */ | |
0c17d899 | 429 | (void) VOP_FSYNC(vp, 0, vn->sc_cred, MNT_WAIT, p); |
60f56dfc | 430 | #endif |
f2eb348c | 431 | (void) vn_close(vp, FREAD|FWRITE, vn->sc_cred, p); |
0c17d899 MH |
432 | crfree(vn->sc_cred); |
433 | vn->sc_vp = (struct vnode *)0; | |
434 | vn->sc_cred = (struct ucred *)0; | |
435 | vn->sc_size = 0; | |
60f56dfc KM |
436 | } |
437 | ||
0c17d899 | 438 | vnsize(dev) |
60f56dfc KM |
439 | dev_t dev; |
440 | { | |
0c17d899 MH |
441 | int unit = vnunit(dev); |
442 | register struct vn_softc *vn = &vn_softc[unit]; | |
60f56dfc | 443 | |
0c17d899 | 444 | if (unit >= NVN || (vn->sc_flags & VNF_INITED) == 0) |
60f56dfc | 445 | return(-1); |
0c17d899 | 446 | return(vn->sc_size); |
60f56dfc KM |
447 | } |
448 | ||
0c17d899 | 449 | vndump(dev) |
60f56dfc KM |
450 | { |
451 | return(ENXIO); | |
452 | } | |
453 | #endif |