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: fd.c 1.3 89/12/03$ 13 * 14 * @(#)vn.c 7.11 (Berkeley) 06/05/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 void 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 void 215 vniodone(bp) 216 register struct buf *bp; 217 { 218 register struct buf *pbp = (struct buf *)bp->b_pfcent; /* XXX */ 219 register int unit = vnunit(pbp->b_dev); 220 int s; 221 222 s = splbio(); 223 #ifdef DEBUG 224 if (vndebug & VDB_IO) 225 printf("vniodone(%d): bp %x vp %x blkno %x addr %x cnt %x\n", 226 unit, bp, bp->b_vp, bp->b_blkno, bp->b_un.b_addr, 227 bp->b_bcount); 228 #endif 229 if (bp->b_error) { 230 #ifdef DEBUG 231 if (vndebug & VDB_IO) 232 printf("vniodone: bp %x error %d\n", bp, bp->b_error); 233 #endif 234 pbp->b_flags |= B_ERROR; 235 pbp->b_error = biowait(bp); 236 } 237 pbp->b_resid -= bp->b_bcount; 238 putvnbuf(bp); 239 if (pbp->b_resid == 0) { 240 #ifdef DEBUG 241 if (vndebug & VDB_IO) 242 printf("vniodone: pbp %x iodone\n", pbp); 243 #endif 244 biodone(pbp); 245 } 246 if (vntab[unit].b_actf) 247 vnstart(unit); 248 else 249 vntab[unit].b_active--; 250 splx(s); 251 } 252 253 vnread(dev, uio, flags, p) 254 dev_t dev; 255 struct uio *uio; 256 int flags; 257 struct proc *p; 258 { 259 register int unit = vnunit(dev); 260 261 #ifdef DEBUG 262 if (vndebug & VDB_FOLLOW) 263 printf("vnread(%x, %x, %x, %x)\n", dev, uio, flags, p); 264 #endif 265 return(physio(vnstrategy, &vnbuf[unit], dev, B_READ, minphys, uio)); 266 } 267 268 vnwrite(dev, uio, flags, p) 269 dev_t dev; 270 struct uio *uio; 271 int flags; 272 struct proc *p; 273 { 274 register int unit = vnunit(dev); 275 276 #ifdef DEBUG 277 if (vndebug & VDB_FOLLOW) 278 printf("vnwrite(%x, %x, %x, %x)\n", dev, uio, flags, p); 279 #endif 280 return(physio(vnstrategy, &vnbuf[unit], dev, B_WRITE, minphys, uio)); 281 } 282 283 /* ARGSUSED */ 284 vnioctl(dev, cmd, data, flag, p) 285 dev_t dev; 286 u_long cmd; 287 caddr_t data; 288 int flag; 289 struct proc *p; 290 { 291 USES_VOP_GETATTR; 292 USES_VOP_UNLOCK; 293 int unit = vnunit(dev); 294 register struct vn_softc *vn; 295 struct vn_ioctl *vio; 296 struct vattr vattr; 297 struct nameidata nd; 298 int error; 299 300 #ifdef DEBUG 301 if (vndebug & VDB_FOLLOW) 302 printf("vnioctl(%x, %x, %x, %x, %x): unit %d\n", 303 dev, cmd, data, flag, p, unit); 304 #endif 305 error = suser(p->p_ucred, &p->p_acflag); 306 if (error) 307 return (error); 308 if (unit >= NVN) 309 return (ENXIO); 310 311 vn = &vn_softc[unit]; 312 vio = (struct vn_ioctl *)data; 313 switch (cmd) { 314 315 case VNIOCSET: 316 if (vn->sc_flags & VNF_INITED) 317 return(EBUSY); 318 /* 319 * Always open for read and write. 320 * This is probably bogus, but it lets vn_open() 321 * weed out directories, sockets, etc. so we don't 322 * have to worry about them. 323 */ 324 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, vio->vn_file, p); 325 if (error = vn_open(&nd, FREAD|FWRITE, 0)) 326 return(error); 327 if (error = VOP_GETATTR(nd.ni_vp, &vattr, p->p_ucred, p)) { 328 VOP_UNLOCK(nd.ni_vp); 329 (void) vn_close(nd.ni_vp, FREAD|FWRITE, p->p_ucred, p); 330 return(error); 331 } 332 VOP_UNLOCK(nd.ni_vp); 333 vn->sc_vp = nd.ni_vp; 334 vn->sc_size = btodb(vattr.va_size); /* note truncation */ 335 if (error = vnsetcred(vn, p->p_ucred)) { 336 (void) vn_close(vn->sc_vp, FREAD|FWRITE, p->p_ucred, p); 337 return(error); 338 } 339 vnthrottle(vn, vn->sc_vp); 340 vio->vn_size = dbtob(vn->sc_size); 341 vn->sc_flags |= VNF_INITED; 342 #ifdef DEBUG 343 if (vndebug & VDB_INIT) 344 printf("vnioctl: SET vp %x size %x\n", 345 vn->sc_vp, vn->sc_size); 346 #endif 347 break; 348 349 case VNIOCCLR: 350 if ((vn->sc_flags & VNF_INITED) == 0) 351 return(ENXIO); 352 vnclear(vn); 353 #ifdef DEBUG 354 if (vndebug & VDB_INIT) 355 printf("vnioctl: CLRed\n"); 356 #endif 357 break; 358 359 default: 360 return(ENXIO); 361 } 362 return(0); 363 } 364 365 /* 366 * Duplicate the current processes' credentials. Since we are called only 367 * as the result of a SET ioctl and only root can do that, any future access 368 * to this "disk" is essentially as root. Note that credentials may change 369 * if some other uid can write directly to the mapped file (NFS). 370 */ 371 vnsetcred(vn, cred) 372 register struct vn_softc *vn; 373 struct ucred cred; 374 { 375 USES_VOP_READ; 376 struct uio auio; 377 struct iovec aiov; 378 char tmpbuf[DEV_BSIZE]; 379 380 vn->sc_cred = crdup(cred); 381 /* XXX: Horrible kludge to establish credentials for NFS */ 382 aiov.iov_base = tmpbuf; 383 aiov.iov_len = MIN(DEV_BSIZE, dbtob(vn->sc_size)); 384 auio.uio_iov = &aiov; 385 auio.uio_iovcnt = 1; 386 auio.uio_offset = 0; 387 auio.uio_rw = UIO_READ; 388 auio.uio_segflg = UIO_SYSSPACE; 389 auio.uio_resid = aiov.iov_len; 390 return(VOP_READ(vn->sc_vp, &auio, 0, vn->sc_cred)); 391 } 392 393 /* 394 * Set maxactive based on FS type 395 */ 396 vnthrottle(vn, vp) 397 register struct vn_softc *vn; 398 struct vnode *vp; 399 { 400 extern int (**ufs_vnodeop_p)(); 401 extern int (**nfsv2_vnodeop_p)(); 402 403 if (vp->v_op == nfsv2_vnodeop_p) 404 vn->sc_maxactive = 2; 405 else 406 vn->sc_maxactive = 8; 407 408 if (vn->sc_maxactive < 1) 409 vn->sc_maxactive = 1; 410 } 411 412 vnshutdown() 413 { 414 register struct vn_softc *vn; 415 416 for (vn = &vn_softc[0]; vn < &vn_softc[NVN]; vn++) 417 if (vn->sc_flags & VNF_INITED) 418 vnclear(vn); 419 } 420 421 vnclear(vn) 422 register struct vn_softc *vn; 423 { 424 USES_VOP_FSYNC; 425 register struct vnode *vp = vn->sc_vp; 426 struct proc *p = curproc; /* XXX */ 427 428 #ifdef DEBUG 429 if (vndebug & VDB_FOLLOW) 430 printf("vnclear(%x): vp %x\n", vp); 431 #endif 432 vn->sc_flags &= ~VNF_INITED; 433 if (vp == (struct vnode *)0) 434 panic("vnioctl: null vp"); 435 #if 0 436 /* XXX - this doesn't work right now */ 437 (void) VOP_FSYNC(vp, 0, vn->sc_cred, MNT_WAIT, p); 438 #endif 439 (void) vn_close(vp, FREAD|FWRITE, vn->sc_cred, p); 440 crfree(vn->sc_cred); 441 vn->sc_vp = (struct vnode *)0; 442 vn->sc_cred = (struct ucred *)0; 443 vn->sc_size = 0; 444 } 445 446 vnsize(dev) 447 dev_t dev; 448 { 449 int unit = vnunit(dev); 450 register struct vn_softc *vn = &vn_softc[unit]; 451 452 if (unit >= NVN || (vn->sc_flags & VNF_INITED) == 0) 453 return(-1); 454 return(vn->sc_size); 455 } 456 457 vndump(dev) 458 { 459 return(ENXIO); 460 } 461 #endif 462