vnd.c revision 1.4 1 /*
2 * Copyright (c) 1988 University of Utah.
3 * Copyright (c) 1990, 1993
4 * The Regents of the University of California. 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 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 *
38 * from: Utah $Hdr: vn.c 1.8 92/12/20$
39 *
40 * @(#)vn.c 8.1 (Berkeley) 6/10/93
41 */
42
43 /*
44 * Vnode disk driver.
45 *
46 * Block/character interface to a vnode. Allows one to treat a file
47 * as a disk (e.g. build a filesystem in it, mount it, etc.).
48 *
49 * NOTE 1: This uses the VOP_BMAP/VOP_STRATEGY interface to the vnode
50 * instead of a simple VOP_RDWR. We do this to avoid distorting the
51 * local buffer cache.
52 *
53 * NOTE 2: There is a security issue involved with this driver.
54 * Once mounted all access to the contents of the "mapped" file via
55 * the special file is controlled by the permissions on the special
56 * file, the protection of the mapped file is ignored (effectively,
57 * by using root credentials in all transactions).
58 *
59 * NOTE 3: Doesn't interact with leases, should it?
60 */
61 #include "vn.h"
62 #if NVN > 0
63
64 #include <sys/param.h>
65 #include <sys/systm.h>
66 #include <sys/namei.h>
67 #include <sys/proc.h>
68 #include <sys/errno.h>
69 #include <sys/dkstat.h>
70 #include <sys/buf.h>
71 #include <sys/malloc.h>
72 #include <sys/ioctl.h>
73 #include <sys/mount.h>
74 #include <sys/vnode.h>
75 #include <sys/file.h>
76 #include <sys/uio.h>
77
78 #include <miscfs/specfs/specdev.h>
79
80 #include <dev/vnioctl.h>
81
82 #ifdef DEBUG
83 int vndebug = 0x00;
84 #define VDB_FOLLOW 0x01
85 #define VDB_INIT 0x02
86 #define VDB_IO 0x04
87 #endif
88
89 #define b_cylin b_resid
90
91 #define vnunit(x) ((minor(x) >> 3) & 0x7) /* for consistency */
92
93 #define getvnbuf() \
94 ((struct buf *)malloc(sizeof(struct buf), M_DEVBUF, M_WAITOK))
95 #define putvnbuf(bp) \
96 free((caddr_t)(bp), M_DEVBUF)
97
98 struct vn_softc {
99 int sc_flags; /* flags */
100 size_t sc_size; /* size of vn */
101 struct vnode *sc_vp; /* vnode */
102 struct ucred *sc_cred; /* credentials */
103 int sc_maxactive; /* max # of active requests */
104 struct buf sc_tab; /* transfer queue */
105 };
106
107 /* sc_flags */
108 #define VNF_ALIVE 0x01
109 #define VNF_INITED 0x02
110
111 #if 0 /* if you need static allocation */
112 struct vn_softc vn_softc[NVN];
113 int numvnd = NVN;
114 #else
115 struct vn_softc *vn_softc;
116 int numvnd;
117 #endif
118
119 void
120 vnattach(num)
121 int num;
122 {
123 char *mem;
124 register u_long size;
125
126 if (num <= 0)
127 return;
128 size = num * sizeof(struct vn_softc);
129 mem = malloc(size, M_DEVBUF, M_NOWAIT);
130 if (mem == NULL) {
131 printf("WARNING: no memory for vnode disks\n");
132 return;
133 }
134 bzero(mem, size);
135 vn_softc = (struct vn_softc *)mem;
136 numvnd = num;
137 }
138
139 int
140 vnopen(dev, flags, mode, p)
141 dev_t dev;
142 int flags, mode;
143 struct proc *p;
144 {
145 int unit = vnunit(dev);
146
147 #ifdef DEBUG
148 if (vndebug & VDB_FOLLOW)
149 printf("vnopen(%x, %x, %x, %x)\n", dev, flags, mode, p);
150 #endif
151 if (unit >= numvnd)
152 return(ENXIO);
153 return(0);
154 }
155
156 int
157 vnclose(dev, flags, mode, p)
158 dev_t dev;
159 int flags, mode;
160 struct proc *p;
161 {
162 #ifdef DEBUG
163 if (vndebug & VDB_FOLLOW)
164 printf("vnclose(%x, %x, %x, %x)\n", dev, flags, mode, p);
165 #endif
166 return 0;
167 }
168
169 /*
170 * Break the request into bsize pieces and submit using VOP_BMAP/VOP_STRATEGY.
171 * Note that this driver can only be used for swapping over NFS on the hp
172 * since nfs_strategy on the vax cannot handle u-areas and page tables.
173 */
174 vnstrategy(bp)
175 register struct buf *bp;
176 {
177 int unit = vnunit(bp->b_dev);
178 register struct vn_softc *vn = &vn_softc[unit];
179 register struct buf *nbp;
180 register int bn, bsize, resid;
181 register caddr_t addr;
182 int sz, flags;
183 extern void vniodone();
184
185 #ifdef DEBUG
186 if (vndebug & VDB_FOLLOW)
187 printf("vnstrategy(%x): unit %d\n", bp, unit);
188 #endif
189 if ((vn->sc_flags & VNF_INITED) == 0) {
190 bp->b_error = ENXIO;
191 bp->b_flags |= B_ERROR;
192 biodone(bp);
193 return;
194 }
195 bn = bp->b_blkno;
196 sz = howmany(bp->b_bcount, DEV_BSIZE);
197 bp->b_resid = bp->b_bcount;
198 if (bn < 0 || bn + sz > vn->sc_size) {
199 if (bn != vn->sc_size) {
200 bp->b_error = EINVAL;
201 bp->b_flags |= B_ERROR;
202 }
203 biodone(bp);
204 return;
205 }
206 bn = dbtob(bn);
207 #if (BSD > 199103)
208 bsize = vn->sc_vp->v_mount->mnt_stat.f_iosize;
209 #else
210 bsize = vn->sc_vp->v_mount->mnt_stat.f_bsize;
211 #endif
212 addr = bp->b_un.b_addr;
213 flags = bp->b_flags | B_CALL;
214 for (resid = bp->b_resid; resid; resid -= sz) {
215 struct vnode *vp;
216 daddr_t nbn;
217 int off, s;
218
219 nbp = getvnbuf();
220 off = bn % bsize;
221 sz = min(bsize - off, resid);
222 #if (BSD > 199103)
223 (void) VOP_BMAP(vn->sc_vp, bn / bsize, &vp, &nbn, NULL);
224 #else
225 (void) VOP_BMAP(vn->sc_vp, bn / bsize, &vp, &nbn);
226 #endif
227 #ifdef DEBUG
228 if (vndebug & VDB_IO)
229 printf("vnstrategy: vp %x/%x bn %x/%x\n",
230 vn->sc_vp, vp, bn, nbn);
231 #endif
232 nbp->b_flags = flags;
233 nbp->b_bcount = sz;
234 nbp->b_bufsize = bp->b_bufsize;
235 nbp->b_error = 0;
236 if (vp->v_type == VBLK || vp->v_type == VCHR)
237 nbp->b_dev = vp->v_rdev;
238 else
239 nbp->b_dev = NODEV;
240 nbp->b_un.b_addr = addr;
241 nbp->b_blkno = nbn + btodb(off);
242 nbp->b_proc = bp->b_proc;
243 nbp->b_iodone = vniodone;
244 nbp->b_vp = vp;
245 nbp->b_pfcent = (int) bp; /* XXX */
246 nbp->b_rcred = vn->sc_cred; /* XXX crdup? */
247 nbp->b_wcred = vn->sc_cred; /* XXX crdup? */
248 nbp->b_dirtyoff = bp->b_dirtyoff;
249 nbp->b_dirtyend = bp->b_dirtyend;
250 #if (BSD > 199103)
251 nbp->b_validoff = bp->b_validoff;
252 nbp->b_validend = bp->b_validend;
253 #endif
254 /*
255 * There is a hole in the file...punt.
256 * Note that we deal with this after the nbp allocation.
257 * This ensures that we properly clean up any operations
258 * that we have already fired off.
259 *
260 * XXX we could deal with this but it would be
261 * a hassle (in the write case).
262 */
263 if ((long)nbn == -1) {
264 nbp->b_error = EIO;
265 nbp->b_flags |= B_ERROR;
266 bp->b_resid -= (resid - sz);
267 biodone(nbp);
268 return;
269 }
270 /*
271 * Just sort by block number
272 */
273 nbp->b_cylin = nbp->b_blkno;
274 s = splbio();
275 disksort(&vn->sc_tab, nbp);
276 if (vn->sc_tab.b_active < vn->sc_maxactive) {
277 vn->sc_tab.b_active++;
278 vnstart(vn);
279 }
280 splx(s);
281 bn += sz;
282 addr += sz;
283 }
284 }
285
286 /*
287 * Feed requests sequentially.
288 * We do it this way to keep from flooding NFS servers if we are connected
289 * to an NFS file. This places the burden on the client rather than the
290 * server.
291 */
292 vnstart(vn)
293 register struct vn_softc *vn;
294 {
295 register struct buf *bp;
296
297 /*
298 * Dequeue now since lower level strategy routine might
299 * queue using same links
300 */
301 bp = vn->sc_tab.b_actf;
302 vn->sc_tab.b_actf = bp->b_actf;
303 #ifdef DEBUG
304 if (vndebug & VDB_IO)
305 printf("vnstart(%d): bp %x vp %x blkno %x addr %x cnt %x\n",
306 vn-vn_softc, bp, bp->b_vp, bp->b_blkno, bp->b_un.b_addr,
307 bp->b_bcount);
308 #endif
309 if ((bp->b_flags & B_READ) == 0)
310 bp->b_vp->v_numoutput++;
311 VOP_STRATEGY(bp);
312 }
313
314 void
315 vniodone(bp)
316 register struct buf *bp;
317 {
318 register struct buf *pbp = (struct buf *)bp->b_pfcent; /* XXX */
319 register struct vn_softc *vn = &vn_softc[vnunit(pbp->b_dev)];
320 int s;
321
322 s = splbio();
323 #ifdef DEBUG
324 if (vndebug & VDB_IO)
325 printf("vniodone(%d): bp %x vp %x blkno %x addr %x cnt %x\n",
326 vn-vn_softc, bp, bp->b_vp, bp->b_blkno, bp->b_un.b_addr,
327 bp->b_bcount);
328 #endif
329 if (bp->b_error) {
330 #ifdef DEBUG
331 if (vndebug & VDB_IO)
332 printf("vniodone: bp %x error %d\n", bp, bp->b_error);
333 #endif
334 pbp->b_flags |= B_ERROR;
335 pbp->b_error = biowait(bp);
336 }
337 pbp->b_resid -= bp->b_bcount;
338 putvnbuf(bp);
339 if (pbp->b_resid == 0) {
340 #ifdef DEBUG
341 if (vndebug & VDB_IO)
342 printf("vniodone: pbp %x iodone\n", pbp);
343 #endif
344 biodone(pbp);
345 }
346 if (vn->sc_tab.b_actf)
347 vnstart(vn);
348 else
349 vn->sc_tab.b_active--;
350 splx(s);
351 }
352
353 vnread(dev, uio, flags, p)
354 dev_t dev;
355 struct uio *uio;
356 int flags;
357 struct proc *p;
358 {
359
360 #ifdef DEBUG
361 if (vndebug & VDB_FOLLOW)
362 printf("vnread(%x, %x, %x, %x)\n", dev, uio, flags, p);
363 #endif
364 return(physio(vnstrategy, NULL, dev, B_READ, minphys, uio));
365 }
366
367 vnwrite(dev, uio, flags, p)
368 dev_t dev;
369 struct uio *uio;
370 int flags;
371 struct proc *p;
372 {
373
374 #ifdef DEBUG
375 if (vndebug & VDB_FOLLOW)
376 printf("vnwrite(%x, %x, %x, %x)\n", dev, uio, flags, p);
377 #endif
378 return(physio(vnstrategy, NULL, dev, B_WRITE, minphys, uio));
379 }
380
381 /* ARGSUSED */
382 vnioctl(dev, cmd, data, flag, p)
383 dev_t dev;
384 u_long cmd;
385 caddr_t data;
386 int flag;
387 struct proc *p;
388 {
389 int unit = vnunit(dev);
390 register struct vn_softc *vn;
391 struct vn_ioctl *vio;
392 struct vattr vattr;
393 struct nameidata nd;
394 int error;
395
396 #ifdef DEBUG
397 if (vndebug & VDB_FOLLOW)
398 printf("vnioctl(%x, %x, %x, %x, %x): unit %d\n",
399 dev, cmd, data, flag, p, unit);
400 #endif
401 error = suser(p->p_ucred, &p->p_acflag);
402 if (error)
403 return (error);
404 if (unit >= numvnd)
405 return (ENXIO);
406
407 vn = &vn_softc[unit];
408 vio = (struct vn_ioctl *)data;
409 switch (cmd) {
410
411 case VNIOCSET:
412 if (vn->sc_flags & VNF_INITED)
413 return(EBUSY);
414 /*
415 * Always open for read and write.
416 * This is probably bogus, but it lets vn_open()
417 * weed out directories, sockets, etc. so we don't
418 * have to worry about them.
419 */
420 #if (BSD > 199103)
421 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, vio->vn_file, p);
422 if (error = vn_open(&nd, FREAD|FWRITE, 0))
423 return(error);
424 #else
425 nd.ni_nameiop = LOOKUP | FOLLOW;
426 nd.ni_segflg = UIO_USERSPACE;
427 nd.ni_dirp = vio->vn_file;
428 if (error = vn_open(&nd, p, FREAD|FWRITE, 0))
429 return(error);
430 #endif
431 if (error = VOP_GETATTR(nd.ni_vp, &vattr, p->p_ucred, p)) {
432 VOP_UNLOCK(nd.ni_vp);
433 (void) vn_close(nd.ni_vp, FREAD|FWRITE, p->p_ucred, p);
434 return(error);
435 }
436 VOP_UNLOCK(nd.ni_vp);
437 vn->sc_vp = nd.ni_vp;
438 vn->sc_size = btodb(vattr.va_size); /* note truncation */
439 if (error = vnsetcred(vn, p->p_ucred)) {
440 (void) vn_close(vn->sc_vp, FREAD|FWRITE, p->p_ucred, p);
441 return(error);
442 }
443 vnthrottle(vn, vn->sc_vp);
444 vio->vn_size = dbtob(vn->sc_size);
445 vn->sc_flags |= VNF_INITED;
446 #ifdef DEBUG
447 if (vndebug & VDB_INIT)
448 printf("vnioctl: SET vp %x size %x\n",
449 vn->sc_vp, vn->sc_size);
450 #endif
451 break;
452
453 case VNIOCCLR:
454 if ((vn->sc_flags & VNF_INITED) == 0)
455 return(ENXIO);
456 vnclear(vn);
457 #ifdef DEBUG
458 if (vndebug & VDB_INIT)
459 printf("vnioctl: CLRed\n");
460 #endif
461 break;
462
463 default:
464 return(ENXIO);
465 }
466 return(0);
467 }
468
469 /*
470 * Duplicate the current processes' credentials. Since we are called only
471 * as the result of a SET ioctl and only root can do that, any future access
472 * to this "disk" is essentially as root. Note that credentials may change
473 * if some other uid can write directly to the mapped file (NFS).
474 */
475 vnsetcred(vn, cred)
476 register struct vn_softc *vn;
477 struct ucred *cred;
478 {
479 struct uio auio;
480 struct iovec aiov;
481 char tmpbuf[DEV_BSIZE];
482
483 vn->sc_cred = crdup(cred);
484 /* XXX: Horrible kludge to establish credentials for NFS */
485 aiov.iov_base = tmpbuf;
486 aiov.iov_len = min(DEV_BSIZE, dbtob(vn->sc_size));
487 auio.uio_iov = &aiov;
488 auio.uio_iovcnt = 1;
489 auio.uio_offset = 0;
490 auio.uio_rw = UIO_READ;
491 auio.uio_segflg = UIO_SYSSPACE;
492 auio.uio_resid = aiov.iov_len;
493 return(VOP_READ(vn->sc_vp, &auio, 0, vn->sc_cred));
494 }
495
496 /*
497 * Set maxactive based on FS type
498 */
499 vnthrottle(vn, vp)
500 register struct vn_softc *vn;
501 struct vnode *vp;
502 {
503 #ifdef NFSCLIENT
504 #if (BSD > 199103)
505 extern int (**nfsv2_vnodeop_p)();
506 if (vp->v_op == nfsv2_vnodeop_p)
507 #else
508 extern struct vnodeops nfsv2_vnodeops;
509 if (vp->v_op == &nfsv2_vnodeops)
510 #endif
511 vn->sc_maxactive = 2;
512 else
513 #endif
514 vn->sc_maxactive = 8;
515
516 if (vn->sc_maxactive < 1)
517 vn->sc_maxactive = 1;
518 }
519
520 vnshutdown()
521 {
522 register struct vn_softc *vn;
523
524 for (vn = &vn_softc[0]; vn < &vn_softc[numvnd]; vn++)
525 if (vn->sc_flags & VNF_INITED)
526 vnclear(vn);
527 }
528
529 vnclear(vn)
530 register struct vn_softc *vn;
531 {
532 register struct vnode *vp = vn->sc_vp;
533 struct proc *p = curproc; /* XXX */
534
535 #ifdef DEBUG
536 if (vndebug & VDB_FOLLOW)
537 printf("vnclear(%x): vp %x\n", vp);
538 #endif
539 vn->sc_flags &= ~VNF_INITED;
540 if (vp == (struct vnode *)0)
541 panic("vnioctl: null vp");
542 #if 0
543 /* XXX - this doesn't work right now */
544 (void) VOP_FSYNC(vp, 0, vn->sc_cred, MNT_WAIT, p);
545 #endif
546 (void) vn_close(vp, FREAD|FWRITE, vn->sc_cred, p);
547 crfree(vn->sc_cred);
548 vn->sc_vp = (struct vnode *)0;
549 vn->sc_cred = (struct ucred *)0;
550 vn->sc_size = 0;
551 }
552
553 vnsize(dev)
554 dev_t dev;
555 {
556 int unit = vnunit(dev);
557 register struct vn_softc *vn = &vn_softc[unit];
558
559 if (unit >= numvnd || (vn->sc_flags & VNF_INITED) == 0)
560 return(-1);
561 return(vn->sc_size);
562 }
563
564 vndump(dev)
565 {
566 return(ENXIO);
567 }
568 #endif
569