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.1 90/07/09$ 13 * 14 * @(#)vn.c 7.4 (Berkeley) 05/04/91 15 */ 16 17 /* 18 * CURRENTLY BROKEN; the name "fd" collides with /dev/fd/xxx. 19 * This would need to be converted to the new proc/user layout as well. 20 */ 21 /* 22 * File (vnode) disk driver. 23 * 24 * Block/character interface to a vnode. Note that this uses the 25 * VOP_BMAP/VOP_STRATEGY interface to the vnode instead of a simple 26 * VOP_RDWR. We do this to avoid distorting the local buffer cache. 27 * 28 * NOTE: There is a security issue involved with this driver. 29 * Once mounted all access to the contents of the "mapped" file via 30 * the special file is controlled by the permissions on the special 31 * file, the protection of the mapped file is ignored (effectively, 32 * by using root credentials in all transactions). 33 */ 34 #include "fd.h" 35 #if NFD > 0 36 37 #include "sys/param.h" 38 #include "sys/systm.h" 39 #include "sys/buf.h" 40 #include "sys/errno.h" 41 #include "sys/dkstat.h" 42 #include "sys/ioctl.h" 43 #include "sys/user.h" 44 #include "sys/vfs.h" 45 #include "sys/vnode.h" 46 #include "sys/file.h" 47 #include "sys/uio.h" 48 #include "sys/malloc.h" 49 50 #include "fdioctl.h" 51 52 #ifdef DEBUG 53 int fddebug = 0x00; 54 #define FDB_FOLLOW 0x01 55 #define FDB_INIT 0x02 56 #define FDB_IO 0x04 57 #endif 58 59 struct buf fdbuf[NFD]; 60 struct buf fdtab[NFD]; 61 62 #define b_cylin b_resid 63 64 #define fdunit(x) ((minor(x) >> 3) & 0x7) /* for consistency */ 65 66 #define getfdbuf() \ 67 ((struct buf *)malloc(sizeof(struct buf), M_DEVBUF, M_WAITOK)) 68 #define putfdbuf(bp) \ 69 free((caddr_t)(bp), M_DEVBUF) 70 71 struct fd_softc { 72 int sc_flags; /* flags */ 73 size_t sc_size; /* size of fd */ 74 struct vnode *sc_vp; /* vnode */ 75 struct ucred *sc_cred; /* credentials */ 76 int sc_maxactive; /* max # of active requests */ 77 } fd_softc[NFD]; 78 79 /* sc_flags */ 80 #define FDF_ALIVE 0x01 81 #define FDF_INITED 0x02 82 83 fdopen(dev, flags) 84 dev_t dev; 85 { 86 int unit = fdunit(dev); 87 88 #ifdef DEBUG 89 if (fddebug & FDB_FOLLOW) 90 printf("fdopen(%x, %x)\n", dev, flags); 91 #endif 92 if (unit >= NFD) 93 return(ENXIO); 94 return(0); 95 } 96 97 /* 98 * Break the request into bsize pieces and submit using VOP_BMAP/VOP_STRATEGY. 99 * Note that this driver can only be used for swapping over NFS on the hp 100 * since nfs_strategy on the vax cannot handle u-areas and page tables. 101 */ 102 fdstrategy(bp) 103 register struct buf *bp; 104 { 105 int unit = fdunit(bp->b_dev); 106 register struct fd_softc *fs = &fd_softc[unit]; 107 register struct buf *nbp; 108 register int bn, bsize, resid; 109 register caddr_t addr; 110 int sz, flags; 111 extern int fdiodone(); 112 113 #ifdef DEBUG 114 if (fddebug & FDB_FOLLOW) 115 printf("fdstrategy(%x): unit %d\n", bp, unit); 116 #endif 117 if ((fs->sc_flags & FDF_INITED) == 0) { 118 bp->b_error = ENXIO; 119 bp->b_flags |= B_ERROR; 120 iodone(bp); 121 return; 122 } 123 bn = bp->b_blkno; 124 sz = howmany(bp->b_bcount, DEV_BSIZE); 125 bp->b_resid = bp->b_bcount; 126 if (bn < 0 || bn + sz > fs->sc_size) { 127 if (bn != fs->sc_size) { 128 bp->b_error = EINVAL; 129 bp->b_flags |= B_ERROR; 130 } 131 iodone(bp); 132 return; 133 } 134 bn = dbtob(bn); 135 bsize = fs->sc_vp->v_vfsp->vfs_bsize; 136 addr = bp->b_un.b_addr; 137 flags = bp->b_flags | B_CALL; 138 for (resid = bp->b_resid; resid; resid -= sz) { 139 struct vnode *vp; 140 daddr_t nbn; 141 int off, s; 142 143 nbp = getfdbuf(); 144 off = bn % bsize; 145 sz = MIN(bsize - off, resid); 146 (void) VOP_BMAP(fs->sc_vp, bn / bsize, &vp, &nbn); 147 #ifdef DEBUG 148 if (fddebug & FDB_IO) 149 printf("fdstrategy: vp %x/%x bn %x/%x dev %x\n", 150 fs->sc_vp, vp, bn, nbn, vp->v_rdev); 151 #endif 152 nbp->b_flags = flags; 153 nbp->b_bcount = sz; 154 nbp->b_bufsize = bp->b_bufsize; 155 nbp->b_error = 0; 156 nbp->b_dev = vp->v_rdev; 157 nbp->b_un.b_addr = addr; 158 nbp->b_blkno = nbn + btodb(off); 159 nbp->b_proc = bp->b_proc; 160 nbp->b_iodone = fdiodone; 161 nbp->b_vp = vp; 162 nbp->b_pfcent = (int) bp; /* XXX */ 163 /* 164 * Just sort by block number 165 */ 166 nbp->b_cylin = nbp->b_blkno; 167 s = splbio(); 168 disksort(&fdtab[unit], nbp); 169 if (fdtab[unit].b_active < fs->sc_maxactive) { 170 fdtab[unit].b_active++; 171 fdstart(unit); 172 } 173 splx(s); 174 bn += sz; 175 addr += sz; 176 } 177 } 178 179 /* 180 * Feed requests sequentially. 181 * We do it this way to keep from flooding NFS servers if we are connected 182 * to an NFS file. This places the burden on the client rather than the 183 * server. 184 */ 185 fdstart(unit) 186 { 187 register struct fd_softc *fs = &fd_softc[unit]; 188 register struct buf *bp; 189 190 /* 191 * Dequeue now since lower level strategy routine might 192 * queue using same links 193 */ 194 bp = fdtab[unit].b_actf; 195 fdtab[unit].b_actf = bp->b_actf; 196 #ifdef DEBUG 197 if (fddebug & FDB_IO) 198 printf("fdstart(%d): bp %x vp %x blkno %x addr %x cnt %x\n", 199 unit, bp, bp->b_vp, bp->b_blkno, bp->b_un.b_addr, 200 bp->b_bcount); 201 #endif 202 VOP_STRATEGY(bp); 203 } 204 205 fdiodone(bp) 206 register struct buf *bp; 207 { 208 register struct buf *pbp = (struct buf *)bp->b_pfcent; /* XXX */ 209 register int unit = fdunit(pbp->b_dev); 210 int s; 211 212 s = splbio(); 213 #ifdef DEBUG 214 if (fddebug & FDB_IO) 215 printf("fdiodone(%d): bp %x vp %x blkno %x addr %x cnt %x\n", 216 unit, bp, bp->b_vp, bp->b_blkno, bp->b_un.b_addr, 217 bp->b_bcount); 218 #endif 219 if (bp->b_error) { 220 #ifdef DEBUG 221 if (fddebug & FDB_IO) 222 printf("fdiodone: bp %x error %d\n", bp, bp->b_error); 223 #endif 224 pbp->b_flags |= B_ERROR; 225 pbp->b_error = geterror(bp); 226 } 227 pbp->b_resid -= bp->b_bcount; 228 putfdbuf(bp); 229 if (pbp->b_resid == 0) { 230 #ifdef DEBUG 231 if (fddebug & FDB_IO) 232 printf("fdiodone: pbp %x iodone\n", pbp); 233 #endif 234 iodone(pbp); 235 } 236 if (fdtab[unit].b_actf) 237 fdstart(unit); 238 else 239 fdtab[unit].b_active--; 240 splx(s); 241 } 242 243 fdread(dev, uio) 244 dev_t dev; 245 struct uio *uio; 246 { 247 register int unit = fdunit(dev); 248 249 #ifdef DEBUG 250 if (fddebug & FDB_FOLLOW) 251 printf("fdread(%x, %x)\n", dev, uio); 252 #endif 253 return(physio(fdstrategy, &fdbuf[unit], dev, B_READ, minphys, uio)); 254 } 255 256 fdwrite(dev, uio) 257 dev_t dev; 258 struct uio *uio; 259 { 260 register int unit = fdunit(dev); 261 262 #ifdef DEBUG 263 if (fddebug & FDB_FOLLOW) 264 printf("fdwrite(%x, %x)\n", dev, uio); 265 #endif 266 return(physio(fdstrategy, &fdbuf[unit], dev, B_WRITE, minphys, uio)); 267 } 268 269 /* ARGSUSED */ 270 fdioctl(dev, cmd, data, flag) 271 dev_t dev; 272 u_long cmd; 273 caddr_t data; 274 int flag; 275 { 276 int unit = fdunit(dev); 277 register struct fd_softc *fs; 278 struct fd_ioctl *fio; 279 struct vattr vattr; 280 struct vnode *vp; 281 int error; 282 283 #ifdef DEBUG 284 if (fddebug & FDB_FOLLOW) 285 printf("fdioctl(%x, %x, %x, %x): unit %d\n", 286 dev, cmd, data, flag, unit); 287 #endif 288 error = suser(u.u_cred, &u.u_acflag); 289 if (error) 290 return (error); 291 if (unit >= NFD) 292 return (ENXIO); 293 294 fs = &fd_softc[unit]; 295 fio = (struct fd_ioctl *)data; 296 switch (cmd) { 297 298 case FDIOCSET: 299 if (fs->sc_flags & FDF_INITED) 300 return(EBUSY); 301 /* 302 * Always open for read and write. 303 * This is probably bogus, but it lets vn_open() 304 * weed out directories, sockets, etc. so we don't 305 * have to worry about them. 306 */ 307 error = vn_open(fio->fd_file, UIO_USERSPACE, 308 FREAD|FWRITE, 0, &vp); 309 if (error) 310 return(error); 311 error = VOP_GETATTR(vp, &vattr, u.u_cred); 312 if (error) { 313 vn_close(vp, FREAD|FWRITE); 314 VN_RELE(vp); 315 return(error); 316 } 317 fs->sc_vp = vp; 318 fs->sc_size = btodb(vattr.va_size); /* note truncation */ 319 error = fdsetcred(fs); 320 if (error) { 321 vn_close(vp, FREAD|FWRITE); 322 VN_RELE(vp); 323 return(error); 324 } 325 fdthrottle(fs, vp); 326 fio->fd_size = dbtob(fs->sc_size); 327 fs->sc_flags |= FDF_INITED; 328 #ifdef DEBUG 329 if (fddebug & FDB_INIT) 330 printf("fdioctl: SET vp %x size %x\n", 331 fs->sc_vp, fs->sc_size); 332 #endif 333 break; 334 335 case FDIOCCLR: 336 if ((fs->sc_flags & FDF_INITED) == 0) 337 return(ENXIO); 338 fdclear(fs); 339 #ifdef DEBUG 340 if (fddebug & FDB_INIT) 341 printf("fdioctl: CLRed\n"); 342 #endif 343 break; 344 345 default: 346 return(ENXIO); 347 } 348 return(0); 349 } 350 351 /* 352 * Duplicate the current processes' credentials. Since we are called only 353 * as the result of a SET ioctl and only root can do that, any future access 354 * to this "disk" is essentially as root. Note that credentials may change 355 * if some other uid can write directly to the mapped file (NFS). 356 */ 357 fdsetcred(fs) 358 register struct fd_softc *fs; 359 { 360 struct uio auio; 361 struct iovec aiov; 362 char tmpbuf[DEV_BSIZE]; 363 364 fs->sc_cred = crdup(u.u_cred); 365 /* XXX: Horrible kludge to establish credentials for NFS */ 366 aiov.iov_base = tmpbuf; 367 aiov.iov_len = MIN(DEV_BSIZE, dbtob(fs->sc_size)); 368 auio.uio_iov = &aiov; 369 auio.uio_iovcnt = 1; 370 auio.uio_offset = 0; 371 auio.uio_rw = UIO_READ; 372 auio.uio_segflg = UIO_SYSSPACE; 373 auio.uio_resid = aiov.iov_len; 374 return(VOP_READ(fs->sc_vp, &auio, 0, fs->sc_cred)); 375 } 376 377 /* 378 * Set maxactive based on FS type 379 */ 380 fdthrottle(fs, vp) 381 register struct fd_softc *fs; 382 struct vnode *vp; 383 { 384 extern struct vnodeops ufs_vnodeops, nfs_vnodeops; 385 386 if (vp->v_op == &nfs_vnodeops) 387 fs->sc_maxactive = 2; 388 else 389 fs->sc_maxactive = 8; 390 391 if (fs->sc_maxactive < 1) 392 fs->sc_maxactive = 1; 393 } 394 395 fdshutdown() 396 { 397 register struct fd_softc *fs; 398 399 for (fs = &fd_softc[0]; fs < &fd_softc[NFD]; fs++) 400 if (fs->sc_flags & FDF_INITED) 401 fdclear(fs); 402 } 403 404 fdclear(fs) 405 register struct fd_softc *fs; 406 { 407 register struct vnode *vp = fs->sc_vp; 408 409 #ifdef DEBUG 410 if (fddebug & FDB_FOLLOW) 411 printf("fdclear(%x): vp %x\n", vp); 412 #endif 413 fs->sc_flags &= ~FDF_INITED; 414 if (vp == (struct vnode *)0) 415 panic("fdioctl: null vp"); 416 #if 0 417 /* XXX - this doesn't work right now */ 418 (void) VOP_FSYNC(vp, fs->sc_cred); 419 #endif 420 vn_close(vp, FREAD|FWRITE); 421 VN_RELE(vp); 422 crfree(fs->sc_cred); 423 fs->sc_vp = (struct vnode *)0; 424 fs->sc_cred = (struct ucred *)0; 425 fs->sc_size = 0; 426 } 427 428 fdsize(dev) 429 dev_t dev; 430 { 431 int unit = fdunit(dev); 432 register struct fd_softc *fs = &fd_softc[unit]; 433 434 if (unit >= NFD || (fs->sc_flags & FDF_INITED) == 0) 435 return(-1); 436 return(fs->sc_size); 437 } 438 439 fddump(dev) 440 { 441 return(ENXIO); 442 } 443 #endif 444