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