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