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