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.5 (Berkeley) 05/07/91 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 int unit = vnunit(bp->b_dev); 110 register struct vn_softc *vn = &vn_softc[unit]; 111 register struct buf *nbp; 112 register int bn, bsize, resid; 113 register caddr_t addr; 114 int sz, flags; 115 extern int vniodone(); 116 117 #ifdef DEBUG 118 if (vndebug & VDB_FOLLOW) 119 printf("vnstrategy(%x): unit %d\n", bp, unit); 120 #endif 121 if ((vn->sc_flags & VNF_INITED) == 0) { 122 bp->b_error = ENXIO; 123 bp->b_flags |= B_ERROR; 124 biodone(bp); 125 return; 126 } 127 bn = bp->b_blkno; 128 sz = howmany(bp->b_bcount, DEV_BSIZE); 129 bp->b_resid = bp->b_bcount; 130 if (bn < 0 || bn + sz > vn->sc_size) { 131 if (bn != vn->sc_size) { 132 bp->b_error = EINVAL; 133 bp->b_flags |= B_ERROR; 134 } 135 biodone(bp); 136 return; 137 } 138 bn = dbtob(bn); 139 bsize = vn->sc_vp->v_mount->mnt_stat.f_bsize; 140 addr = bp->b_un.b_addr; 141 flags = bp->b_flags | B_CALL; 142 for (resid = bp->b_resid; resid; resid -= sz) { 143 struct vnode *vp; 144 daddr_t nbn; 145 int off, s; 146 147 nbp = getvnbuf(); 148 off = bn % bsize; 149 sz = MIN(bsize - off, resid); 150 (void) VOP_BMAP(vn->sc_vp, bn / bsize, &vp, &nbn); 151 #ifdef DEBUG 152 if (vndebug & VDB_IO) 153 printf("vnstrategy: vp %x/%x bn %x/%x\n", 154 vn->sc_vp, vp, bn, nbn); 155 #endif 156 nbp->b_flags = flags; 157 nbp->b_bcount = sz; 158 nbp->b_bufsize = bp->b_bufsize; 159 nbp->b_error = 0; 160 if (vp->v_type == VBLK || vp->v_type == VCHR) 161 nbp->b_dev = vp->v_rdev; 162 else 163 nbp->b_dev = NODEV; 164 nbp->b_un.b_addr = addr; 165 nbp->b_blkno = nbn + btodb(off); 166 nbp->b_proc = bp->b_proc; 167 nbp->b_iodone = vniodone; 168 nbp->b_vp = vp; 169 nbp->b_pfcent = (int) bp; /* XXX */ 170 /* 171 * Just sort by block number 172 */ 173 nbp->b_cylin = nbp->b_blkno; 174 s = splbio(); 175 disksort(&vntab[unit], nbp); 176 if (vntab[unit].b_active < vn->sc_maxactive) { 177 vntab[unit].b_active++; 178 vnstart(unit); 179 } 180 splx(s); 181 bn += sz; 182 addr += sz; 183 } 184 } 185 186 /* 187 * Feed requests sequentially. 188 * We do it this way to keep from flooding NFS servers if we are connected 189 * to an NFS file. This places the burden on the client rather than the 190 * server. 191 */ 192 vnstart(unit) 193 { 194 register struct vn_softc *vn = &vn_softc[unit]; 195 register struct buf *bp; 196 197 /* 198 * Dequeue now since lower level strategy routine might 199 * queue using same links 200 */ 201 bp = vntab[unit].b_actf; 202 vntab[unit].b_actf = bp->b_actf; 203 #ifdef DEBUG 204 if (vndebug & VDB_IO) 205 printf("vnstart(%d): bp %x vp %x blkno %x addr %x cnt %x\n", 206 unit, bp, bp->b_vp, bp->b_blkno, bp->b_un.b_addr, 207 bp->b_bcount); 208 #endif 209 VOP_STRATEGY(bp); 210 } 211 212 vniodone(bp) 213 register struct buf *bp; 214 { 215 register struct buf *pbp = (struct buf *)bp->b_pfcent; /* XXX */ 216 register int unit = vnunit(pbp->b_dev); 217 int s; 218 219 s = splbio(); 220 #ifdef DEBUG 221 if (vndebug & VDB_IO) 222 printf("vniodone(%d): bp %x vp %x blkno %x addr %x cnt %x\n", 223 unit, bp, bp->b_vp, bp->b_blkno, bp->b_un.b_addr, 224 bp->b_bcount); 225 #endif 226 if (bp->b_error) { 227 #ifdef DEBUG 228 if (vndebug & VDB_IO) 229 printf("vniodone: bp %x error %d\n", bp, bp->b_error); 230 #endif 231 pbp->b_flags |= B_ERROR; 232 pbp->b_error = biowait(bp); 233 } 234 pbp->b_resid -= bp->b_bcount; 235 putvnbuf(bp); 236 if (pbp->b_resid == 0) { 237 #ifdef DEBUG 238 if (vndebug & VDB_IO) 239 printf("vniodone: pbp %x iodone\n", pbp); 240 #endif 241 biodone(pbp); 242 } 243 if (vntab[unit].b_actf) 244 vnstart(unit); 245 else 246 vntab[unit].b_active--; 247 splx(s); 248 } 249 250 vnread(dev, uio, flags, p) 251 dev_t dev; 252 struct uio *uio; 253 int flags; 254 struct proc *p; 255 { 256 register int unit = vnunit(dev); 257 258 #ifdef DEBUG 259 if (vndebug & VDB_FOLLOW) 260 printf("vnread(%x, %x, %x, %x)\n", dev, uio, flags, p); 261 #endif 262 return(physio(vnstrategy, &vnbuf[unit], dev, B_READ, minphys, uio)); 263 } 264 265 vnwrite(dev, uio, flags, p) 266 dev_t dev; 267 struct uio *uio; 268 int flags; 269 struct proc *p; 270 { 271 register int unit = vnunit(dev); 272 273 #ifdef DEBUG 274 if (vndebug & VDB_FOLLOW) 275 printf("vnwrite(%x, %x, %x, %x)\n", dev, uio, flags, p); 276 #endif 277 return(physio(vnstrategy, &vnbuf[unit], dev, B_WRITE, minphys, uio)); 278 } 279 280 /* ARGSUSED */ 281 vnioctl(dev, cmd, data, flag, p) 282 dev_t dev; 283 u_long cmd; 284 caddr_t data; 285 int flag; 286 struct proc *p; 287 { 288 int unit = vnunit(dev); 289 register struct vn_softc *vn; 290 struct vn_ioctl *vio; 291 struct vattr vattr; 292 struct nameidata nd; 293 int error; 294 295 #ifdef DEBUG 296 if (vndebug & VDB_FOLLOW) 297 printf("vnioctl(%x, %x, %x, %x, %x): unit %d\n", 298 dev, cmd, data, flag, p, unit); 299 #endif 300 error = suser(p->p_ucred, &p->p_acflag); 301 if (error) 302 return (error); 303 if (unit >= NVN) 304 return (ENXIO); 305 306 vn = &vn_softc[unit]; 307 vio = (struct vn_ioctl *)data; 308 switch (cmd) { 309 310 case VNIOCSET: 311 if (vn->sc_flags & VNF_INITED) 312 return(EBUSY); 313 /* 314 * Always open for read and write. 315 * This is probably bogus, but it lets vn_open() 316 * weed out directories, sockets, etc. so we don't 317 * have to worry about them. 318 */ 319 nd.ni_segflg = UIO_USERSPACE; 320 nd.ni_dirp = vio->vn_file; 321 error = vn_open(&nd, p, FREAD|FWRITE, 0); 322 if (error) 323 return(error); 324 error = VOP_GETATTR(nd.ni_vp, &vattr, p->p_ucred, p); 325 if (error) { 326 vrele(nd.ni_vp); 327 return(error); 328 } 329 vn->sc_vp = nd.ni_vp; 330 vn->sc_size = btodb(vattr.va_size); /* note truncation */ 331 error = vnsetcred(vn, p->p_ucred); 332 if (error) { 333 vrele(vn->sc_vp); 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 struct vnodeops ufs_vnodeops, nfsv2_vnodeops; 397 398 if (vp->v_op == &nfsv2_vnodeops) 399 vn->sc_maxactive = 2; 400 else 401 vn->sc_maxactive = 8; 402 403 if (vn->sc_maxactive < 1) 404 vn->sc_maxactive = 1; 405 } 406 407 vnshutdown() 408 { 409 register struct vn_softc *vn; 410 411 for (vn = &vn_softc[0]; vn < &vn_softc[NVN]; vn++) 412 if (vn->sc_flags & VNF_INITED) 413 vnclear(vn); 414 } 415 416 vnclear(vn) 417 register struct vn_softc *vn; 418 { 419 register struct vnode *vp = vn->sc_vp; 420 421 #ifdef DEBUG 422 if (vndebug & VDB_FOLLOW) 423 printf("vnclear(%x): vp %x\n", vp); 424 #endif 425 vn->sc_flags &= ~VNF_INITED; 426 if (vp == (struct vnode *)0) 427 panic("vnioctl: null vp"); 428 #if 0 429 /* XXX - this doesn't work right now */ 430 (void) VOP_FSYNC(vp, 0, vn->sc_cred, MNT_WAIT, p); 431 #endif 432 vrele(vp); 433 crfree(vn->sc_cred); 434 vn->sc_vp = (struct vnode *)0; 435 vn->sc_cred = (struct ucred *)0; 436 vn->sc_size = 0; 437 } 438 439 vnsize(dev) 440 dev_t dev; 441 { 442 int unit = vnunit(dev); 443 register struct vn_softc *vn = &vn_softc[unit]; 444 445 if (unit >= NVN || (vn->sc_flags & VNF_INITED) == 0) 446 return(-1); 447 return(vn->sc_size); 448 } 449 450 vndump(dev) 451 { 452 return(ENXIO); 453 } 454 #endif 455