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 * 12 * from: Utah $Hdr: vn.c 1.8 92/12/20$ 13 * 14 * @(#)vn.c 7.15 (Berkeley) 12/27/92 15 */ 16 17 /* 18 * Vnode disk driver. 19 * 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.). 22 * 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. 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 * 33 * NOTE 3: Doesn't interact with leases, should it? 34 */ 35 #include "vn.h" 36 #if NVN > 0 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/namei.h> 41 #include <sys/proc.h> 42 #include <sys/errno.h> 43 #include <sys/dkstat.h> 44 #include <sys/buf.h> 45 #include <sys/malloc.h> 46 #include <sys/ioctl.h> 47 #include <sys/mount.h> 48 #include <sys/vnode.h> 49 #include <sys/file.h> 50 #include <sys/uio.h> 51 52 #include <miscfs/specfs/specdev.h> 53 54 #include <dev/vnioctl.h> 55 56 #ifdef DEBUG 57 int vndebug = 0x00; 58 #define VDB_FOLLOW 0x01 59 #define VDB_INIT 0x02 60 #define VDB_IO 0x04 61 #endif 62 63 struct buf vnbuf[NVN]; 64 struct buf vntab[NVN]; 65 66 #define b_cylin b_resid 67 68 #define vnunit(x) ((minor(x) >> 3) & 0x7) /* for consistency */ 69 70 #define getvnbuf() \ 71 ((struct buf *)malloc(sizeof(struct buf), M_DEVBUF, M_WAITOK)) 72 #define putvnbuf(bp) \ 73 free((caddr_t)(bp), M_DEVBUF) 74 75 struct vn_softc { 76 int sc_flags; /* flags */ 77 size_t sc_size; /* size of vn */ 78 struct vnode *sc_vp; /* vnode */ 79 struct ucred *sc_cred; /* credentials */ 80 int sc_maxactive; /* max # of active requests */ 81 } vn_softc[NVN]; 82 83 /* sc_flags */ 84 #define VNF_ALIVE 0x01 85 #define VNF_INITED 0x02 86 87 int 88 vnopen(dev, flags, mode, p) 89 dev_t dev; 90 int flags, mode; 91 struct proc *p; 92 { 93 int unit = vnunit(dev); 94 95 #ifdef DEBUG 96 if (vndebug & VDB_FOLLOW) 97 printf("vnopen(%x, %x, %x, %x)\n", dev, flags, mode, p); 98 #endif 99 if (unit >= NVN) 100 return(ENXIO); 101 return(0); 102 } 103 104 /* 105 * Break the request into bsize pieces and submit using VOP_BMAP/VOP_STRATEGY. 106 * Note that this driver can only be used for swapping over NFS on the hp 107 * since nfs_strategy on the vax cannot handle u-areas and page tables. 108 */ 109 vnstrategy(bp) 110 register struct buf *bp; 111 { 112 int unit = vnunit(bp->b_dev); 113 register struct vn_softc *vn = &vn_softc[unit]; 114 register struct buf *nbp; 115 register int bn, bsize, resid; 116 register caddr_t addr; 117 int sz, flags; 118 extern void vniodone(); 119 120 #ifdef DEBUG 121 if (vndebug & VDB_FOLLOW) 122 printf("vnstrategy(%x): unit %d\n", bp, unit); 123 #endif 124 if ((vn->sc_flags & VNF_INITED) == 0) { 125 bp->b_error = ENXIO; 126 bp->b_flags |= B_ERROR; 127 biodone(bp); 128 return; 129 } 130 bn = bp->b_blkno; 131 sz = howmany(bp->b_bcount, DEV_BSIZE); 132 bp->b_resid = bp->b_bcount; 133 if (bn < 0 || bn + sz > vn->sc_size) { 134 if (bn != vn->sc_size) { 135 bp->b_error = EINVAL; 136 bp->b_flags |= B_ERROR; 137 } 138 biodone(bp); 139 return; 140 } 141 bn = dbtob(bn); 142 bsize = vn->sc_vp->v_mount->mnt_stat.f_iosize; 143 addr = bp->b_un.b_addr; 144 flags = bp->b_flags | B_CALL; 145 for (resid = bp->b_resid; resid; resid -= sz) { 146 struct vnode *vp; 147 daddr_t nbn; 148 int off, s; 149 150 nbp = getvnbuf(); 151 off = bn % bsize; 152 sz = min(bsize - off, resid); 153 (void) VOP_BMAP(vn->sc_vp, bn / bsize, &vp, &nbn, NULL); 154 #ifdef DEBUG 155 if (vndebug & VDB_IO) 156 printf("vnstrategy: vp %x/%x bn %x/%x\n", 157 vn->sc_vp, vp, bn, nbn); 158 #endif 159 nbp->b_flags = flags; 160 nbp->b_bcount = sz; 161 nbp->b_bufsize = bp->b_bufsize; 162 nbp->b_error = 0; 163 if (vp->v_type == VBLK || vp->v_type == VCHR) 164 nbp->b_dev = vp->v_rdev; 165 else 166 nbp->b_dev = NODEV; 167 nbp->b_un.b_addr = addr; 168 nbp->b_blkno = nbn + btodb(off); 169 nbp->b_proc = bp->b_proc; 170 nbp->b_iodone = vniodone; 171 nbp->b_vp = vp; 172 nbp->b_pfcent = (int) bp; /* XXX */ 173 nbp->b_rcred = vn->sc_cred; /* XXX crdup? */ 174 nbp->b_wcred = vn->sc_cred; /* XXX crdup? */ 175 nbp->b_dirtyoff = bp->b_dirtyoff; 176 nbp->b_dirtyend = bp->b_dirtyend; 177 nbp->b_validoff = bp->b_validoff; 178 nbp->b_validend = bp->b_validend; 179 /* 180 * Just sort by block number 181 */ 182 nbp->b_cylin = nbp->b_blkno; 183 s = splbio(); 184 disksort(&vntab[unit], nbp); 185 if (vntab[unit].b_active < vn->sc_maxactive) { 186 vntab[unit].b_active++; 187 vnstart(unit); 188 } 189 splx(s); 190 bn += sz; 191 addr += sz; 192 } 193 } 194 195 /* 196 * Feed requests sequentially. 197 * We do it this way to keep from flooding NFS servers if we are connected 198 * to an NFS file. This places the burden on the client rather than the 199 * server. 200 */ 201 vnstart(unit) 202 { 203 register struct vn_softc *vn = &vn_softc[unit]; 204 register struct buf *bp; 205 206 /* 207 * Dequeue now since lower level strategy routine might 208 * queue using same links 209 */ 210 bp = vntab[unit].b_actf; 211 vntab[unit].b_actf = bp->b_actf; 212 #ifdef DEBUG 213 if (vndebug & VDB_IO) 214 printf("vnstart(%d): bp %x vp %x blkno %x addr %x cnt %x\n", 215 unit, bp, bp->b_vp, bp->b_blkno, bp->b_un.b_addr, 216 bp->b_bcount); 217 #endif 218 if ((bp->b_flags & B_READ) == 0) 219 bp->b_vp->v_numoutput++; 220 VOP_STRATEGY(bp); 221 } 222 223 void 224 vniodone(bp) 225 register struct buf *bp; 226 { 227 register struct buf *pbp = (struct buf *)bp->b_pfcent; /* XXX */ 228 register int unit = vnunit(pbp->b_dev); 229 int s; 230 231 s = splbio(); 232 #ifdef DEBUG 233 if (vndebug & VDB_IO) 234 printf("vniodone(%d): bp %x vp %x blkno %x addr %x cnt %x\n", 235 unit, bp, bp->b_vp, bp->b_blkno, bp->b_un.b_addr, 236 bp->b_bcount); 237 #endif 238 if (bp->b_error) { 239 #ifdef DEBUG 240 if (vndebug & VDB_IO) 241 printf("vniodone: bp %x error %d\n", bp, bp->b_error); 242 #endif 243 pbp->b_flags |= B_ERROR; 244 pbp->b_error = biowait(bp); 245 } 246 pbp->b_resid -= bp->b_bcount; 247 putvnbuf(bp); 248 if (pbp->b_resid == 0) { 249 #ifdef DEBUG 250 if (vndebug & VDB_IO) 251 printf("vniodone: pbp %x iodone\n", pbp); 252 #endif 253 biodone(pbp); 254 } 255 if (vntab[unit].b_actf) 256 vnstart(unit); 257 else 258 vntab[unit].b_active--; 259 splx(s); 260 } 261 262 vnread(dev, uio, flags, p) 263 dev_t dev; 264 struct uio *uio; 265 int flags; 266 struct proc *p; 267 { 268 register int unit = vnunit(dev); 269 270 #ifdef DEBUG 271 if (vndebug & VDB_FOLLOW) 272 printf("vnread(%x, %x, %x, %x)\n", dev, uio, flags, p); 273 #endif 274 return(physio(vnstrategy, &vnbuf[unit], dev, B_READ, minphys, uio)); 275 } 276 277 vnwrite(dev, uio, flags, p) 278 dev_t dev; 279 struct uio *uio; 280 int flags; 281 struct proc *p; 282 { 283 register int unit = vnunit(dev); 284 285 #ifdef DEBUG 286 if (vndebug & VDB_FOLLOW) 287 printf("vnwrite(%x, %x, %x, %x)\n", dev, uio, flags, p); 288 #endif 289 return(physio(vnstrategy, &vnbuf[unit], dev, B_WRITE, minphys, uio)); 290 } 291 292 /* ARGSUSED */ 293 vnioctl(dev, cmd, data, flag, p) 294 dev_t dev; 295 u_long cmd; 296 caddr_t data; 297 int flag; 298 struct proc *p; 299 { 300 int unit = vnunit(dev); 301 register struct vn_softc *vn; 302 struct vn_ioctl *vio; 303 struct vattr vattr; 304 struct nameidata nd; 305 int error; 306 307 #ifdef DEBUG 308 if (vndebug & VDB_FOLLOW) 309 printf("vnioctl(%x, %x, %x, %x, %x): unit %d\n", 310 dev, cmd, data, flag, p, unit); 311 #endif 312 error = suser(p->p_ucred, &p->p_acflag); 313 if (error) 314 return (error); 315 if (unit >= NVN) 316 return (ENXIO); 317 318 vn = &vn_softc[unit]; 319 vio = (struct vn_ioctl *)data; 320 switch (cmd) { 321 322 case VNIOCSET: 323 if (vn->sc_flags & VNF_INITED) 324 return(EBUSY); 325 /* 326 * Always open for read and write. 327 * This is probably bogus, but it lets vn_open() 328 * weed out directories, sockets, etc. so we don't 329 * have to worry about them. 330 */ 331 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, vio->vn_file, p); 332 if (error = vn_open(&nd, FREAD|FWRITE, 0)) 333 return(error); 334 if (error = VOP_GETATTR(nd.ni_vp, &vattr, p->p_ucred, p)) { 335 VOP_UNLOCK(nd.ni_vp); 336 (void) vn_close(nd.ni_vp, FREAD|FWRITE, p->p_ucred, p); 337 return(error); 338 } 339 VOP_UNLOCK(nd.ni_vp); 340 vn->sc_vp = nd.ni_vp; 341 vn->sc_size = btodb(vattr.va_size); /* note truncation */ 342 if (error = vnsetcred(vn, p->p_ucred)) { 343 (void) vn_close(vn->sc_vp, FREAD|FWRITE, p->p_ucred, p); 344 return(error); 345 } 346 vnthrottle(vn, vn->sc_vp); 347 vio->vn_size = dbtob(vn->sc_size); 348 vn->sc_flags |= VNF_INITED; 349 #ifdef DEBUG 350 if (vndebug & VDB_INIT) 351 printf("vnioctl: SET vp %x size %x\n", 352 vn->sc_vp, vn->sc_size); 353 #endif 354 break; 355 356 case VNIOCCLR: 357 if ((vn->sc_flags & VNF_INITED) == 0) 358 return(ENXIO); 359 vnclear(vn); 360 #ifdef DEBUG 361 if (vndebug & VDB_INIT) 362 printf("vnioctl: CLRed\n"); 363 #endif 364 break; 365 366 default: 367 return(ENXIO); 368 } 369 return(0); 370 } 371 372 /* 373 * Duplicate the current processes' credentials. Since we are called only 374 * as the result of a SET ioctl and only root can do that, any future access 375 * to this "disk" is essentially as root. Note that credentials may change 376 * if some other uid can write directly to the mapped file (NFS). 377 */ 378 vnsetcred(vn, cred) 379 register struct vn_softc *vn; 380 struct ucred cred; 381 { 382 struct uio auio; 383 struct iovec aiov; 384 char tmpbuf[DEV_BSIZE]; 385 386 vn->sc_cred = crdup(cred); 387 /* XXX: Horrible kludge to establish credentials for NFS */ 388 aiov.iov_base = tmpbuf; 389 aiov.iov_len = min(DEV_BSIZE, dbtob(vn->sc_size)); 390 auio.uio_iov = &aiov; 391 auio.uio_iovcnt = 1; 392 auio.uio_offset = 0; 393 auio.uio_rw = UIO_READ; 394 auio.uio_segflg = UIO_SYSSPACE; 395 auio.uio_resid = aiov.iov_len; 396 return(VOP_READ(vn->sc_vp, &auio, 0, vn->sc_cred)); 397 } 398 399 /* 400 * Set maxactive based on FS type 401 */ 402 vnthrottle(vn, vp) 403 register struct vn_softc *vn; 404 struct vnode *vp; 405 { 406 extern int (**nfsv2_vnodeop_p)(); 407 408 if (vp->v_op == nfsv2_vnodeop_p) 409 vn->sc_maxactive = 2; 410 else 411 vn->sc_maxactive = 8; 412 413 if (vn->sc_maxactive < 1) 414 vn->sc_maxactive = 1; 415 } 416 417 vnshutdown() 418 { 419 register struct vn_softc *vn; 420 421 for (vn = &vn_softc[0]; vn < &vn_softc[NVN]; vn++) 422 if (vn->sc_flags & VNF_INITED) 423 vnclear(vn); 424 } 425 426 vnclear(vn) 427 register struct vn_softc *vn; 428 { 429 register struct vnode *vp = vn->sc_vp; 430 struct proc *p = curproc; /* XXX */ 431 432 #ifdef DEBUG 433 if (vndebug & VDB_FOLLOW) 434 printf("vnclear(%x): vp %x\n", vp); 435 #endif 436 vn->sc_flags &= ~VNF_INITED; 437 if (vp == (struct vnode *)0) 438 panic("vnioctl: null vp"); 439 #if 0 440 /* XXX - this doesn't work right now */ 441 (void) VOP_FSYNC(vp, 0, vn->sc_cred, MNT_WAIT, p); 442 #endif 443 (void) vn_close(vp, FREAD|FWRITE, vn->sc_cred, p); 444 crfree(vn->sc_cred); 445 vn->sc_vp = (struct vnode *)0; 446 vn->sc_cred = (struct ucred *)0; 447 vn->sc_size = 0; 448 } 449 450 vnsize(dev) 451 dev_t dev; 452 { 453 int unit = vnunit(dev); 454 register struct vn_softc *vn = &vn_softc[unit]; 455 456 if (unit >= NVN || (vn->sc_flags & VNF_INITED) == 0) 457 return(-1); 458 return(vn->sc_size); 459 } 460 461 vndump(dev) 462 { 463 return(ENXIO); 464 } 465 #endif 466