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.13 94/04/02$ 39 * 40 * @(#)vn.c 8.6 (Berkeley) 4/1/94 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 dovncluster = 1; 84 int vndebug = 0x00; 85 #define VDB_FOLLOW 0x01 86 #define VDB_INIT 0x02 87 #define VDB_IO 0x04 88 #endif 89 90 #define b_cylin b_resid 91 92 #define vnunit(x) ((minor(x) >> 3) & 0x7) /* for consistency */ 93 94 #define getvnbuf() \ 95 ((struct buf *)malloc(sizeof(struct buf), M_DEVBUF, M_WAITOK)) 96 #define putvnbuf(bp) \ 97 free((caddr_t)(bp), M_DEVBUF) 98 99 struct vn_softc { 100 int sc_flags; /* flags */ 101 size_t sc_size; /* size of vn */ 102 struct vnode *sc_vp; /* vnode */ 103 struct ucred *sc_cred; /* credentials */ 104 int sc_maxactive; /* max # of active requests */ 105 struct buf sc_tab; /* transfer queue */ 106 }; 107 108 /* sc_flags */ 109 #define VNF_ALIVE 0x01 110 #define VNF_INITED 0x02 111 112 #if 0 /* if you need static allocation */ 113 struct vn_softc vn_softc[NVN]; 114 int numvnd = NVN; 115 #else 116 struct vn_softc *vn_softc; 117 int numvnd; 118 #endif 119 120 void 121 vnattach(num) 122 int num; 123 { 124 char *mem; 125 register u_long size; 126 127 if (num <= 0) 128 return; 129 size = num * sizeof(struct vn_softc); 130 mem = malloc(size, M_DEVBUF, M_NOWAIT); 131 if (mem == NULL) { 132 printf("WARNING: no memory for vnode disks\n"); 133 return; 134 } 135 bzero(mem, size); 136 vn_softc = (struct vn_softc *)mem; 137 numvnd = num; 138 } 139 140 int 141 vnopen(dev, flags, mode, p) 142 dev_t dev; 143 int flags, mode; 144 struct proc *p; 145 { 146 int unit = vnunit(dev); 147 148 #ifdef DEBUG 149 if (vndebug & VDB_FOLLOW) 150 printf("vnopen(%x, %x, %x, %x)\n", dev, flags, mode, p); 151 #endif 152 if (unit >= numvnd) 153 return(ENXIO); 154 return(0); 155 } 156 157 int 158 vnclose(dev, flags, mode, p) 159 dev_t dev; 160 int flags, mode; 161 struct proc *p; 162 { 163 #ifdef DEBUG 164 if (vndebug & VDB_FOLLOW) 165 printf("vnclose(%x, %x, %x, %x)\n", dev, flags, mode, p); 166 #endif 167 return 0; 168 } 169 170 /* 171 * Break the request into bsize pieces and submit using VOP_BMAP/VOP_STRATEGY. 172 * Note that this driver can only be used for swapping over NFS on the hp 173 * since nfs_strategy on the vax cannot handle u-areas and page tables. 174 */ 175 void 176 vnstrategy(bp) 177 register struct buf *bp; 178 { 179 int unit = vnunit(bp->b_dev); 180 register struct vn_softc *vn = &vn_softc[unit]; 181 register struct buf *nbp; 182 register int bn, bsize, resid; 183 register caddr_t addr; 184 int sz, flags, error; 185 extern void vniodone(); 186 187 #ifdef DEBUG 188 if (vndebug & VDB_FOLLOW) 189 printf("vnstrategy(%x): unit %d\n", bp, unit); 190 #endif 191 if ((vn->sc_flags & VNF_INITED) == 0) { 192 bp->b_error = ENXIO; 193 bp->b_flags |= B_ERROR; 194 biodone(bp); 195 return; 196 } 197 bn = bp->b_blkno; 198 sz = howmany(bp->b_bcount, DEV_BSIZE); 199 bp->b_resid = bp->b_bcount; 200 if (bn < 0 || bn + sz > vn->sc_size) { 201 if (bn != vn->sc_size) { 202 bp->b_error = EINVAL; 203 bp->b_flags |= B_ERROR; 204 } 205 biodone(bp); 206 return; 207 } 208 bn = dbtob(bn); 209 bsize = vn->sc_vp->v_mount->mnt_stat.f_iosize; 210 addr = bp->b_data; 211 flags = bp->b_flags | B_CALL; 212 for (resid = bp->b_resid; resid; resid -= sz) { 213 struct vnode *vp; 214 daddr_t nbn; 215 int off, s, nra; 216 217 nra = 0; 218 error = VOP_BMAP(vn->sc_vp, bn / bsize, &vp, &nbn, &nra); 219 if (error == 0 && (long)nbn == -1) 220 error = EIO; 221 #ifdef DEBUG 222 if (!dovncluster) 223 nra = 0; 224 #endif 225 226 if (off = bn % bsize) 227 sz = bsize - off; 228 else 229 sz = (1 + nra) * bsize; 230 if (resid < sz) 231 sz = resid; 232 #ifdef DEBUG 233 if (vndebug & VDB_IO) 234 printf("vnstrategy: vp %x/%x bn %x/%x sz %x\n", 235 vn->sc_vp, vp, bn, nbn, sz); 236 #endif 237 238 nbp = getvnbuf(); 239 nbp->b_flags = flags; 240 nbp->b_bcount = sz; 241 nbp->b_bufsize = bp->b_bufsize; 242 nbp->b_error = 0; 243 if (vp->v_type == VBLK || vp->v_type == VCHR) 244 nbp->b_dev = vp->v_rdev; 245 else 246 nbp->b_dev = NODEV; 247 nbp->b_data = addr; 248 nbp->b_blkno = nbn + btodb(off); 249 nbp->b_proc = bp->b_proc; 250 nbp->b_iodone = vniodone; 251 nbp->b_vp = vp; 252 nbp->b_pfcent = (int) bp; /* XXX */ 253 nbp->b_rcred = vn->sc_cred; /* XXX crdup? */ 254 nbp->b_wcred = vn->sc_cred; /* XXX crdup? */ 255 nbp->b_dirtyoff = bp->b_dirtyoff; 256 nbp->b_dirtyend = bp->b_dirtyend; 257 nbp->b_validoff = bp->b_validoff; 258 nbp->b_validend = bp->b_validend; 259 /* 260 * If there was an error or a hole in the file...punt. 261 * Note that we deal with this after the nbp allocation. 262 * This ensures that we properly clean up any operations 263 * that we have already fired off. 264 * 265 * XXX we could deal with holes here but it would be 266 * a hassle (in the write case). 267 */ 268 if (error) { 269 nbp->b_error = error; 270 nbp->b_flags |= B_ERROR; 271 bp->b_resid -= (resid - sz); 272 biodone(nbp); 273 return; 274 } 275 /* 276 * Just sort by block number 277 */ 278 nbp->b_cylin = nbp->b_blkno; 279 s = splbio(); 280 disksort(&vn->sc_tab, nbp); 281 if (vn->sc_tab.b_active < vn->sc_maxactive) { 282 vn->sc_tab.b_active++; 283 vnstart(vn); 284 } 285 splx(s); 286 bn += sz; 287 addr += sz; 288 } 289 } 290 291 /* 292 * Feed requests sequentially. 293 * We do it this way to keep from flooding NFS servers if we are connected 294 * to an NFS file. This places the burden on the client rather than the 295 * server. 296 */ 297 vnstart(vn) 298 register struct vn_softc *vn; 299 { 300 register struct buf *bp; 301 302 /* 303 * Dequeue now since lower level strategy routine might 304 * queue using same links 305 */ 306 bp = vn->sc_tab.b_actf; 307 vn->sc_tab.b_actf = bp->b_actf; 308 #ifdef DEBUG 309 if (vndebug & VDB_IO) 310 printf("vnstart(%d): bp %x vp %x blkno %x addr %x cnt %x\n", 311 vn-vn_softc, bp, bp->b_vp, bp->b_blkno, bp->b_data, 312 bp->b_bcount); 313 #endif 314 if ((bp->b_flags & B_READ) == 0) 315 bp->b_vp->v_numoutput++; 316 VOP_STRATEGY(bp); 317 } 318 319 void 320 vniodone(bp) 321 register struct buf *bp; 322 { 323 register struct buf *pbp = (struct buf *)bp->b_pfcent; /* XXX */ 324 register struct vn_softc *vn = &vn_softc[vnunit(pbp->b_dev)]; 325 int s; 326 327 s = splbio(); 328 #ifdef DEBUG 329 if (vndebug & VDB_IO) 330 printf("vniodone(%d): bp %x vp %x blkno %x addr %x cnt %x\n", 331 vn-vn_softc, bp, bp->b_vp, bp->b_blkno, bp->b_data, 332 bp->b_bcount); 333 #endif 334 if (bp->b_error) { 335 #ifdef DEBUG 336 if (vndebug & VDB_IO) 337 printf("vniodone: bp %x error %d\n", bp, bp->b_error); 338 #endif 339 pbp->b_flags |= B_ERROR; 340 pbp->b_error = biowait(bp); 341 } 342 pbp->b_resid -= bp->b_bcount; 343 putvnbuf(bp); 344 if (pbp->b_resid == 0) { 345 #ifdef DEBUG 346 if (vndebug & VDB_IO) 347 printf("vniodone: pbp %x iodone\n", pbp); 348 #endif 349 biodone(pbp); 350 } 351 if (vn->sc_tab.b_actf) 352 vnstart(vn); 353 else 354 vn->sc_tab.b_active--; 355 splx(s); 356 } 357 358 /* ARGSUSED */ 359 vnioctl(dev, cmd, data, flag, p) 360 dev_t dev; 361 u_long cmd; 362 caddr_t data; 363 int flag; 364 struct proc *p; 365 { 366 int unit = vnunit(dev); 367 register struct vn_softc *vn; 368 struct vn_ioctl *vio; 369 struct vattr vattr; 370 struct nameidata nd; 371 int error; 372 373 #ifdef DEBUG 374 if (vndebug & VDB_FOLLOW) 375 printf("vnioctl(%x, %x, %x, %x, %x): unit %d\n", 376 dev, cmd, data, flag, p, unit); 377 #endif 378 error = suser(p->p_ucred, &p->p_acflag); 379 if (error) 380 return (error); 381 if (unit >= numvnd) 382 return (ENXIO); 383 384 vn = &vn_softc[unit]; 385 vio = (struct vn_ioctl *)data; 386 switch (cmd) { 387 388 case VNIOCSET: 389 if (vn->sc_flags & VNF_INITED) 390 return(EBUSY); 391 /* 392 * Always open for read and write. 393 * This is probably bogus, but it lets vn_open() 394 * weed out directories, sockets, etc. so we don't 395 * have to worry about them. 396 */ 397 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, vio->vn_file, p); 398 if (error = vn_open(&nd, FREAD|FWRITE, 0)) 399 return(error); 400 if (error = VOP_GETATTR(nd.ni_vp, &vattr, p->p_ucred, p)) { 401 VOP_UNLOCK(nd.ni_vp); 402 (void) vn_close(nd.ni_vp, FREAD|FWRITE, p->p_ucred, p); 403 return(error); 404 } 405 VOP_UNLOCK(nd.ni_vp); 406 vn->sc_vp = nd.ni_vp; 407 vn->sc_size = btodb(vattr.va_size); /* note truncation */ 408 if (error = vnsetcred(vn, p->p_ucred)) { 409 (void) vn_close(nd.ni_vp, FREAD|FWRITE, p->p_ucred, p); 410 return(error); 411 } 412 vnthrottle(vn, vn->sc_vp); 413 vio->vn_size = dbtob(vn->sc_size); 414 vn->sc_flags |= VNF_INITED; 415 #ifdef DEBUG 416 if (vndebug & VDB_INIT) 417 printf("vnioctl: SET vp %x size %x\n", 418 vn->sc_vp, vn->sc_size); 419 #endif 420 break; 421 422 case VNIOCCLR: 423 if ((vn->sc_flags & VNF_INITED) == 0) 424 return(ENXIO); 425 vnclear(vn); 426 #ifdef DEBUG 427 if (vndebug & VDB_INIT) 428 printf("vnioctl: CLRed\n"); 429 #endif 430 break; 431 432 default: 433 return(ENXIO); 434 } 435 return(0); 436 } 437 438 /* 439 * Duplicate the current processes' credentials. Since we are called only 440 * as the result of a SET ioctl and only root can do that, any future access 441 * to this "disk" is essentially as root. Note that credentials may change 442 * if some other uid can write directly to the mapped file (NFS). 443 */ 444 vnsetcred(vn, cred) 445 register struct vn_softc *vn; 446 struct ucred *cred; 447 { 448 struct uio auio; 449 struct iovec aiov; 450 char *tmpbuf; 451 int error; 452 453 vn->sc_cred = crdup(cred); 454 tmpbuf = malloc(DEV_BSIZE, M_TEMP, M_WAITOK); 455 456 /* XXX: Horrible kludge to establish credentials for NFS */ 457 aiov.iov_base = tmpbuf; 458 aiov.iov_len = min(DEV_BSIZE, dbtob(vn->sc_size)); 459 auio.uio_iov = &aiov; 460 auio.uio_iovcnt = 1; 461 auio.uio_offset = 0; 462 auio.uio_rw = UIO_READ; 463 auio.uio_segflg = UIO_SYSSPACE; 464 auio.uio_resid = aiov.iov_len; 465 error = VOP_READ(vn->sc_vp, &auio, 0, vn->sc_cred); 466 467 free(tmpbuf, M_TEMP); 468 return (error); 469 } 470 471 /* 472 * Set maxactive based on FS type 473 */ 474 vnthrottle(vn, vp) 475 register struct vn_softc *vn; 476 struct vnode *vp; 477 { 478 #ifdef NFSCLIENT 479 extern int (**nfsv2_vnodeop_p)(); 480 481 if (vp->v_op == nfsv2_vnodeop_p) 482 vn->sc_maxactive = 2; 483 else 484 #endif 485 vn->sc_maxactive = 8; 486 487 if (vn->sc_maxactive < 1) 488 vn->sc_maxactive = 1; 489 } 490 491 vnshutdown() 492 { 493 register struct vn_softc *vn; 494 495 for (vn = &vn_softc[0]; vn < &vn_softc[numvnd]; vn++) 496 if (vn->sc_flags & VNF_INITED) 497 vnclear(vn); 498 } 499 500 vnclear(vn) 501 register struct vn_softc *vn; 502 { 503 register struct vnode *vp = vn->sc_vp; 504 struct proc *p = curproc; /* XXX */ 505 506 #ifdef DEBUG 507 if (vndebug & VDB_FOLLOW) 508 printf("vnclear(%x): vp %x\n", vp); 509 #endif 510 vn->sc_flags &= ~VNF_INITED; 511 if (vp == (struct vnode *)0) 512 panic("vnioctl: null vp"); 513 (void) vn_close(vp, FREAD|FWRITE, vn->sc_cred, p); 514 crfree(vn->sc_cred); 515 vn->sc_vp = (struct vnode *)0; 516 vn->sc_cred = (struct ucred *)0; 517 vn->sc_size = 0; 518 } 519 520 vnsize(dev) 521 dev_t dev; 522 { 523 int unit = vnunit(dev); 524 register struct vn_softc *vn = &vn_softc[unit]; 525 526 if (unit >= numvnd || (vn->sc_flags & VNF_INITED) == 0) 527 return(-1); 528 return(vn->sc_size); 529 } 530 531 vndump(dev) 532 { 533 return(ENXIO); 534 } 535 #endif 536