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