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.14 (Berkeley) 10/11/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/file.h> 48 #include <sys/uio.h> 49 50 #include <miscfs/specfs/specdev.h> 51 52 #include <dev/vnioctl.h> 53 54 #ifdef DEBUG 55 int vndebug = 0x00; 56 #define VDB_FOLLOW 0x01 57 #define VDB_INIT 0x02 58 #define VDB_IO 0x04 59 #endif 60 61 struct buf vnbuf[NVN]; 62 struct buf vntab[NVN]; 63 64 #define b_cylin b_resid 65 66 #define vnunit(x) ((minor(x) >> 3) & 0x7) /* for consistency */ 67 68 #define getvnbuf() \ 69 ((struct buf *)malloc(sizeof(struct buf), M_DEVBUF, M_WAITOK)) 70 #define putvnbuf(bp) \ 71 free((caddr_t)(bp), M_DEVBUF) 72 73 struct vn_softc { 74 int sc_flags; /* flags */ 75 size_t sc_size; /* size of vn */ 76 struct vnode *sc_vp; /* vnode */ 77 struct ucred *sc_cred; /* credentials */ 78 int sc_maxactive; /* max # of active requests */ 79 } vn_softc[NVN]; 80 81 /* sc_flags */ 82 #define VNF_ALIVE 0x01 83 #define VNF_INITED 0x02 84 85 int 86 vnopen(dev, flags, mode, p) 87 dev_t dev; 88 int flags, mode; 89 struct proc *p; 90 { 91 int unit = vnunit(dev); 92 93 #ifdef DEBUG 94 if (vndebug & VDB_FOLLOW) 95 printf("vnopen(%x, %x, %x, %x)\n", dev, flags, mode, p); 96 #endif 97 if (unit >= NVN) 98 return(ENXIO); 99 return(0); 100 } 101 102 /* 103 * Break the request into bsize pieces and submit using VOP_BMAP/VOP_STRATEGY. 104 * Note that this driver can only be used for swapping over NFS on the hp 105 * since nfs_strategy on the vax cannot handle u-areas and page tables. 106 */ 107 vnstrategy(bp) 108 register struct buf *bp; 109 { 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, NULL); 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 register struct vn_softc *vn = &vn_softc[unit]; 196 register struct buf *bp; 197 198 /* 199 * Dequeue now since lower level strategy routine might 200 * queue using same links 201 */ 202 bp = vntab[unit].b_actf; 203 vntab[unit].b_actf = bp->b_actf; 204 #ifdef DEBUG 205 if (vndebug & VDB_IO) 206 printf("vnstart(%d): bp %x vp %x blkno %x addr %x cnt %x\n", 207 unit, bp, bp->b_vp, bp->b_blkno, bp->b_un.b_addr, 208 bp->b_bcount); 209 #endif 210 VOP_STRATEGY(bp); 211 } 212 213 void 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 int unit = vnunit(dev); 291 register struct vn_softc *vn; 292 struct vn_ioctl *vio; 293 struct vattr vattr; 294 struct nameidata nd; 295 int error; 296 297 #ifdef DEBUG 298 if (vndebug & VDB_FOLLOW) 299 printf("vnioctl(%x, %x, %x, %x, %x): unit %d\n", 300 dev, cmd, data, flag, p, unit); 301 #endif 302 error = suser(p->p_ucred, &p->p_acflag); 303 if (error) 304 return (error); 305 if (unit >= NVN) 306 return (ENXIO); 307 308 vn = &vn_softc[unit]; 309 vio = (struct vn_ioctl *)data; 310 switch (cmd) { 311 312 case VNIOCSET: 313 if (vn->sc_flags & VNF_INITED) 314 return(EBUSY); 315 /* 316 * Always open for read and write. 317 * This is probably bogus, but it lets vn_open() 318 * weed out directories, sockets, etc. so we don't 319 * have to worry about them. 320 */ 321 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, vio->vn_file, p); 322 if (error = vn_open(&nd, FREAD|FWRITE, 0)) 323 return(error); 324 if (error = VOP_GETATTR(nd.ni_vp, &vattr, p->p_ucred, p)) { 325 VOP_UNLOCK(nd.ni_vp); 326 (void) vn_close(nd.ni_vp, FREAD|FWRITE, p->p_ucred, p); 327 return(error); 328 } 329 VOP_UNLOCK(nd.ni_vp); 330 vn->sc_vp = nd.ni_vp; 331 vn->sc_size = btodb(vattr.va_size); /* note truncation */ 332 if (error = vnsetcred(vn, p->p_ucred)) { 333 (void) vn_close(vn->sc_vp, FREAD|FWRITE, p->p_ucred, p); 334 return(error); 335 } 336 vnthrottle(vn, vn->sc_vp); 337 vio->vn_size = dbtob(vn->sc_size); 338 vn->sc_flags |= VNF_INITED; 339 #ifdef DEBUG 340 if (vndebug & VDB_INIT) 341 printf("vnioctl: SET vp %x size %x\n", 342 vn->sc_vp, vn->sc_size); 343 #endif 344 break; 345 346 case VNIOCCLR: 347 if ((vn->sc_flags & VNF_INITED) == 0) 348 return(ENXIO); 349 vnclear(vn); 350 #ifdef DEBUG 351 if (vndebug & VDB_INIT) 352 printf("vnioctl: CLRed\n"); 353 #endif 354 break; 355 356 default: 357 return(ENXIO); 358 } 359 return(0); 360 } 361 362 /* 363 * Duplicate the current processes' credentials. Since we are called only 364 * as the result of a SET ioctl and only root can do that, any future access 365 * to this "disk" is essentially as root. Note that credentials may change 366 * if some other uid can write directly to the mapped file (NFS). 367 */ 368 vnsetcred(vn, cred) 369 register struct vn_softc *vn; 370 struct ucred cred; 371 { 372 struct uio auio; 373 struct iovec aiov; 374 char tmpbuf[DEV_BSIZE]; 375 376 vn->sc_cred = crdup(cred); 377 /* XXX: Horrible kludge to establish credentials for NFS */ 378 aiov.iov_base = tmpbuf; 379 aiov.iov_len = min(DEV_BSIZE, dbtob(vn->sc_size)); 380 auio.uio_iov = &aiov; 381 auio.uio_iovcnt = 1; 382 auio.uio_offset = 0; 383 auio.uio_rw = UIO_READ; 384 auio.uio_segflg = UIO_SYSSPACE; 385 auio.uio_resid = aiov.iov_len; 386 return(VOP_READ(vn->sc_vp, &auio, 0, vn->sc_cred)); 387 } 388 389 /* 390 * Set maxactive based on FS type 391 */ 392 vnthrottle(vn, vp) 393 register struct vn_softc *vn; 394 struct vnode *vp; 395 { 396 extern int (**ufs_vnodeop_p)(); 397 extern int (**nfsv2_vnodeop_p)(); 398 399 if (vp->v_op == nfsv2_vnodeop_p) 400 vn->sc_maxactive = 2; 401 else 402 vn->sc_maxactive = 8; 403 404 if (vn->sc_maxactive < 1) 405 vn->sc_maxactive = 1; 406 } 407 408 vnshutdown() 409 { 410 register struct vn_softc *vn; 411 412 for (vn = &vn_softc[0]; vn < &vn_softc[NVN]; vn++) 413 if (vn->sc_flags & VNF_INITED) 414 vnclear(vn); 415 } 416 417 vnclear(vn) 418 register struct vn_softc *vn; 419 { 420 register struct vnode *vp = vn->sc_vp; 421 struct proc *p = curproc; /* XXX */ 422 423 #ifdef DEBUG 424 if (vndebug & VDB_FOLLOW) 425 printf("vnclear(%x): vp %x\n", vp); 426 #endif 427 vn->sc_flags &= ~VNF_INITED; 428 if (vp == (struct vnode *)0) 429 panic("vnioctl: null vp"); 430 #if 0 431 /* XXX - this doesn't work right now */ 432 (void) VOP_FSYNC(vp, 0, vn->sc_cred, MNT_WAIT, p); 433 #endif 434 (void) vn_close(vp, FREAD|FWRITE, vn->sc_cred, p); 435 crfree(vn->sc_cred); 436 vn->sc_vp = (struct vnode *)0; 437 vn->sc_cred = (struct ucred *)0; 438 vn->sc_size = 0; 439 } 440 441 vnsize(dev) 442 dev_t dev; 443 { 444 int unit = vnunit(dev); 445 register struct vn_softc *vn = &vn_softc[unit]; 446 447 if (unit >= NVN || (vn->sc_flags & VNF_INITED) == 0) 448 return(-1); 449 return(vn->sc_size); 450 } 451 452 vndump(dev) 453 { 454 return(ENXIO); 455 } 456 #endif 457