1 /* $NetBSD: spec_vnops.c,v 1.20 1994/11/14 06:07:45 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1989, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * @(#)spec_vnops.c 8.6 (Berkeley) 4/9/94 36 */ 37 38 #include <sys/param.h> 39 #include <sys/proc.h> 40 #include <sys/systm.h> 41 #include <sys/kernel.h> 42 #include <sys/conf.h> 43 #include <sys/buf.h> 44 #include <sys/mount.h> 45 #include <sys/namei.h> 46 #include <sys/vnode.h> 47 #include <sys/stat.h> 48 #include <sys/errno.h> 49 #include <sys/ioctl.h> 50 #include <sys/file.h> 51 #include <sys/disklabel.h> 52 #include <miscfs/specfs/specdev.h> 53 54 /* symbolic sleep message strings for devices */ 55 char devopn[] = "devopn"; 56 char devio[] = "devio"; 57 char devwait[] = "devwait"; 58 char devin[] = "devin"; 59 char devout[] = "devout"; 60 char devioc[] = "devioc"; 61 char devcls[] = "devcls"; 62 63 int (**spec_vnodeop_p)(); 64 struct vnodeopv_entry_desc spec_vnodeop_entries[] = { 65 { &vop_default_desc, vn_default_error }, 66 { &vop_lookup_desc, spec_lookup }, /* lookup */ 67 { &vop_create_desc, spec_create }, /* create */ 68 { &vop_mknod_desc, spec_mknod }, /* mknod */ 69 { &vop_open_desc, spec_open }, /* open */ 70 { &vop_close_desc, spec_close }, /* close */ 71 { &vop_access_desc, spec_access }, /* access */ 72 { &vop_getattr_desc, spec_getattr }, /* getattr */ 73 { &vop_setattr_desc, spec_setattr }, /* setattr */ 74 { &vop_read_desc, spec_read }, /* read */ 75 { &vop_write_desc, spec_write }, /* write */ 76 { &vop_ioctl_desc, spec_ioctl }, /* ioctl */ 77 { &vop_select_desc, spec_select }, /* select */ 78 { &vop_mmap_desc, spec_mmap }, /* mmap */ 79 { &vop_fsync_desc, spec_fsync }, /* fsync */ 80 { &vop_seek_desc, spec_seek }, /* seek */ 81 { &vop_remove_desc, spec_remove }, /* remove */ 82 { &vop_link_desc, spec_link }, /* link */ 83 { &vop_rename_desc, spec_rename }, /* rename */ 84 { &vop_mkdir_desc, spec_mkdir }, /* mkdir */ 85 { &vop_rmdir_desc, spec_rmdir }, /* rmdir */ 86 { &vop_symlink_desc, spec_symlink }, /* symlink */ 87 { &vop_readdir_desc, spec_readdir }, /* readdir */ 88 { &vop_readlink_desc, spec_readlink }, /* readlink */ 89 { &vop_abortop_desc, spec_abortop }, /* abortop */ 90 { &vop_inactive_desc, spec_inactive }, /* inactive */ 91 { &vop_reclaim_desc, spec_reclaim }, /* reclaim */ 92 { &vop_lock_desc, spec_lock }, /* lock */ 93 { &vop_unlock_desc, spec_unlock }, /* unlock */ 94 { &vop_bmap_desc, spec_bmap }, /* bmap */ 95 { &vop_strategy_desc, spec_strategy }, /* strategy */ 96 { &vop_print_desc, spec_print }, /* print */ 97 { &vop_islocked_desc, spec_islocked }, /* islocked */ 98 { &vop_pathconf_desc, spec_pathconf }, /* pathconf */ 99 { &vop_advlock_desc, spec_advlock }, /* advlock */ 100 { &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */ 101 { &vop_valloc_desc, spec_valloc }, /* valloc */ 102 { &vop_vfree_desc, spec_vfree }, /* vfree */ 103 { &vop_truncate_desc, spec_truncate }, /* truncate */ 104 { &vop_update_desc, spec_update }, /* update */ 105 { &vop_bwrite_desc, spec_bwrite }, /* bwrite */ 106 { (struct vnodeop_desc*)NULL, (int(*)())NULL } 107 }; 108 struct vnodeopv_desc spec_vnodeop_opv_desc = 109 { &spec_vnodeop_p, spec_vnodeop_entries }; 110 111 /* 112 * Trivial lookup routine that always fails. 113 */ 114 int 115 spec_lookup(ap) 116 struct vop_lookup_args /* { 117 struct vnode *a_dvp; 118 struct vnode **a_vpp; 119 struct componentname *a_cnp; 120 } */ *ap; 121 { 122 123 *ap->a_vpp = NULL; 124 return (ENOTDIR); 125 } 126 127 /* 128 * Open a special file. 129 */ 130 /* ARGSUSED */ 131 spec_open(ap) 132 struct vop_open_args /* { 133 struct vnode *a_vp; 134 int a_mode; 135 struct ucred *a_cred; 136 struct proc *a_p; 137 struct file *a_fp; 138 } */ *ap; 139 { 140 struct vnode *bvp, *vp = ap->a_vp; 141 dev_t bdev, dev = (dev_t)vp->v_rdev; 142 register int maj = major(dev); 143 int error; 144 145 /* 146 * Don't allow open if fs is mounted -nodev. 147 */ 148 if (vp->v_mount && (vp->v_mount->mnt_flag & MNT_NODEV)) 149 return (ENXIO); 150 151 switch (vp->v_type) { 152 153 case VCHR: 154 if ((u_int)maj >= nchrdev) 155 return (ENXIO); 156 if (ap->a_cred != FSCRED && (ap->a_mode & FWRITE)) { 157 /* 158 * When running in very secure mode, do not allow 159 * opens for writing of any disk character devices. 160 */ 161 if (securelevel >= 2 && isdisk(dev, VCHR)) 162 return (EPERM); 163 /* 164 * When running in secure mode, do not allow opens 165 * for writing of /dev/mem, /dev/kmem, or character 166 * devices whose corresponding block devices are 167 * currently mounted. 168 */ 169 if (securelevel >= 1) { 170 if ((bdev = chrtoblk(dev)) != NODEV && 171 vfinddev(bdev, VBLK, &bvp) && 172 bvp->v_usecount > 0 && 173 (error = vfs_mountedon(bvp))) 174 return (error); 175 if (iskmemdev(dev)) 176 return (EPERM); 177 } 178 } 179 VOP_UNLOCK(vp); 180 error = (*cdevsw[maj].d_open)(dev, ap->a_mode, S_IFCHR, 181 ap->a_p, ap->a_fp); 182 VOP_LOCK(vp); 183 return (error); 184 185 case VBLK: 186 if ((u_int)maj >= nblkdev) 187 return (ENXIO); 188 /* 189 * When running in very secure mode, do not allow 190 * opens for writing of any disk block devices. 191 */ 192 if (securelevel >= 2 && ap->a_cred != FSCRED && 193 (ap->a_mode & FWRITE) && isdisk(dev, VBLK)) 194 return (EPERM); 195 /* 196 * Do not allow opens of block devices that are 197 * currently mounted. 198 */ 199 if (error = vfs_mountedon(vp)) 200 return (error); 201 return ((*bdevsw[maj].d_open)(dev, ap->a_mode, S_IFBLK, 202 ap->a_p, ap->a_fp)); 203 } 204 return (0); 205 } 206 207 /* 208 * Vnode op for read 209 */ 210 /* ARGSUSED */ 211 spec_read(ap) 212 struct vop_read_args /* { 213 struct vnode *a_vp; 214 struct uio *a_uio; 215 int a_ioflag; 216 struct ucred *a_cred; 217 } */ *ap; 218 { 219 register struct vnode *vp = ap->a_vp; 220 register struct uio *uio = ap->a_uio; 221 struct proc *p = uio->uio_procp; 222 struct buf *bp; 223 daddr_t bn, nextbn; 224 long bsize, bscale, ssize; 225 struct partinfo dpart; 226 int n, on, majordev, (*ioctl)(); 227 int error = 0; 228 dev_t dev; 229 230 #ifdef DIAGNOSTIC 231 if (uio->uio_rw != UIO_READ) 232 panic("spec_read mode"); 233 if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc) 234 panic("spec_read proc"); 235 #endif 236 if (uio->uio_resid == 0) 237 return (0); 238 239 switch (vp->v_type) { 240 241 case VCHR: 242 VOP_UNLOCK(vp); 243 error = (*cdevsw[major(vp->v_rdev)].d_read) 244 (vp->v_rdev, uio, ap->a_ioflag); 245 VOP_LOCK(vp); 246 return (error); 247 248 case VBLK: 249 if (uio->uio_offset < 0) 250 return (EINVAL); 251 bsize = BLKDEV_IOSIZE; 252 ssize = DEV_BSIZE; 253 dev = vp->v_rdev; 254 if ((majordev = major(dev)) < nblkdev && 255 (ioctl = bdevsw[majordev].d_ioctl) != NULL && 256 (*ioctl)(dev, DIOCGPART, (caddr_t)&dpart, FREAD, p) == 0) { 257 if (dpart.part->p_fstype == FS_BSDFFS && 258 dpart.part->p_frag != 0 && dpart.part->p_fsize != 0) 259 bsize = dpart.part->p_frag * 260 dpart.part->p_fsize; 261 if (dpart.disklab->d_secsize != 0) 262 ssize = dpart.disklab->d_secsize; 263 } 264 bscale = bsize / ssize; 265 do { 266 bn = (uio->uio_offset / ssize) &~ (bscale - 1); 267 on = uio->uio_offset % bsize; 268 n = min((unsigned)(bsize - on), uio->uio_resid); 269 if (vp->v_lastr + bscale == bn) { 270 nextbn = bn + bscale; 271 error = breadn(vp, bn, (int)bsize, &nextbn, 272 (int *)&bsize, 1, NOCRED, &bp); 273 } else 274 error = bread(vp, bn, (int)bsize, NOCRED, &bp); 275 vp->v_lastr = bn; 276 n = min(n, bsize - bp->b_resid); 277 if (error) { 278 brelse(bp); 279 return (error); 280 } 281 error = uiomove((char *)bp->b_data + on, n, uio); 282 if (n + on == bsize) 283 bp->b_flags |= B_AGE; 284 brelse(bp); 285 } while (error == 0 && uio->uio_resid > 0 && n != 0); 286 return (error); 287 288 default: 289 panic("spec_read type"); 290 } 291 /* NOTREACHED */ 292 } 293 294 /* 295 * Vnode op for write 296 */ 297 /* ARGSUSED */ 298 spec_write(ap) 299 struct vop_write_args /* { 300 struct vnode *a_vp; 301 struct uio *a_uio; 302 int a_ioflag; 303 struct ucred *a_cred; 304 } */ *ap; 305 { 306 register struct vnode *vp = ap->a_vp; 307 register struct uio *uio = ap->a_uio; 308 struct proc *p = uio->uio_procp; 309 struct buf *bp; 310 daddr_t bn; 311 int bsize, blkmask, ssize; 312 struct partinfo dpart; 313 register int n, on; 314 int error = 0; 315 316 #ifdef DIAGNOSTIC 317 if (uio->uio_rw != UIO_WRITE) 318 panic("spec_write mode"); 319 if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc) 320 panic("spec_write proc"); 321 #endif 322 323 switch (vp->v_type) { 324 325 case VCHR: 326 VOP_UNLOCK(vp); 327 error = (*cdevsw[major(vp->v_rdev)].d_write) 328 (vp->v_rdev, uio, ap->a_ioflag); 329 VOP_LOCK(vp); 330 return (error); 331 332 case VBLK: 333 if (uio->uio_resid == 0) 334 return (0); 335 if (uio->uio_offset < 0) 336 return (EINVAL); 337 bsize = BLKDEV_IOSIZE; 338 ssize = DEV_BSIZE; 339 if ((*bdevsw[major(vp->v_rdev)].d_ioctl)(vp->v_rdev, DIOCGPART, 340 (caddr_t)&dpart, FREAD, p) == 0) { 341 if (dpart.part->p_fstype == FS_BSDFFS && 342 dpart.part->p_frag != 0 && dpart.part->p_fsize != 0) 343 bsize = dpart.part->p_frag * 344 dpart.part->p_fsize; 345 if (dpart.disklab->d_secsize != 0) 346 ssize = dpart.disklab->d_secsize; 347 } 348 blkmask = (bsize / ssize) - 1; 349 do { 350 bn = (uio->uio_offset / ssize) &~ blkmask; 351 on = uio->uio_offset % bsize; 352 n = min((unsigned)(bsize - on), uio->uio_resid); 353 if (n == bsize) 354 bp = getblk(vp, bn, bsize, 0, 0); 355 else 356 error = bread(vp, bn, bsize, NOCRED, &bp); 357 n = min(n, bsize - bp->b_resid); 358 if (error) { 359 brelse(bp); 360 return (error); 361 } 362 error = uiomove((char *)bp->b_data + on, n, uio); 363 if (n + on == bsize) { 364 bp->b_flags |= B_AGE; 365 bawrite(bp); 366 } else 367 bdwrite(bp); 368 } while (error == 0 && uio->uio_resid > 0 && n != 0); 369 return (error); 370 371 default: 372 panic("spec_write type"); 373 } 374 /* NOTREACHED */ 375 } 376 377 /* 378 * Device ioctl operation. 379 */ 380 /* ARGSUSED */ 381 spec_ioctl(ap) 382 struct vop_ioctl_args /* { 383 struct vnode *a_vp; 384 u_long a_command; 385 caddr_t a_data; 386 int a_fflag; 387 struct ucred *a_cred; 388 struct proc *a_p; 389 } */ *ap; 390 { 391 dev_t dev = ap->a_vp->v_rdev; 392 393 switch (ap->a_vp->v_type) { 394 395 case VCHR: 396 return ((*cdevsw[major(dev)].d_ioctl)(dev, ap->a_command, ap->a_data, 397 ap->a_fflag, ap->a_p)); 398 399 case VBLK: 400 if (ap->a_command == 0 && (long)ap->a_data == B_TAPE) 401 if (bdevsw[major(dev)].d_flags & B_TAPE) 402 return (0); 403 else 404 return (1); 405 return ((*bdevsw[major(dev)].d_ioctl)(dev, ap->a_command, ap->a_data, 406 ap->a_fflag, ap->a_p)); 407 408 default: 409 panic("spec_ioctl"); 410 /* NOTREACHED */ 411 } 412 } 413 414 /* ARGSUSED */ 415 spec_select(ap) 416 struct vop_select_args /* { 417 struct vnode *a_vp; 418 int a_which; 419 int a_fflags; 420 struct ucred *a_cred; 421 struct proc *a_p; 422 } */ *ap; 423 { 424 register dev_t dev; 425 426 switch (ap->a_vp->v_type) { 427 428 default: 429 return (1); /* XXX */ 430 431 case VCHR: 432 dev = ap->a_vp->v_rdev; 433 return (*cdevsw[major(dev)].d_select)(dev, ap->a_which, ap->a_p); 434 } 435 } 436 /* 437 * Synch buffers associated with a block device 438 */ 439 /* ARGSUSED */ 440 int 441 spec_fsync(ap) 442 struct vop_fsync_args /* { 443 struct vnode *a_vp; 444 struct ucred *a_cred; 445 int a_waitfor; 446 struct proc *a_p; 447 } */ *ap; 448 { 449 register struct vnode *vp = ap->a_vp; 450 register struct buf *bp; 451 struct buf *nbp; 452 int s; 453 454 if (vp->v_type == VCHR) 455 return (0); 456 /* 457 * Flush all dirty buffers associated with a block device. 458 */ 459 loop: 460 s = splbio(); 461 for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) { 462 nbp = bp->b_vnbufs.le_next; 463 if ((bp->b_flags & B_BUSY)) 464 continue; 465 if ((bp->b_flags & B_DELWRI) == 0) 466 panic("spec_fsync: not dirty"); 467 bremfree(bp); 468 bp->b_flags |= B_BUSY; 469 splx(s); 470 bawrite(bp); 471 goto loop; 472 } 473 if (ap->a_waitfor == MNT_WAIT) { 474 while (vp->v_numoutput) { 475 vp->v_flag |= VBWAIT; 476 sleep((caddr_t)&vp->v_numoutput, PRIBIO + 1); 477 } 478 #ifdef DIAGNOSTIC 479 if (vp->v_dirtyblkhd.lh_first) { 480 vprint("spec_fsync: dirty", vp); 481 goto loop; 482 } 483 #endif 484 } 485 splx(s); 486 return (0); 487 } 488 489 /* 490 * Just call the device strategy routine 491 */ 492 spec_strategy(ap) 493 struct vop_strategy_args /* { 494 struct buf *a_bp; 495 } */ *ap; 496 { 497 498 (*bdevsw[major(ap->a_bp->b_dev)].d_strategy)(ap->a_bp); 499 return (0); 500 } 501 502 /* 503 * This is a noop, simply returning what one has been given. 504 */ 505 spec_bmap(ap) 506 struct vop_bmap_args /* { 507 struct vnode *a_vp; 508 daddr_t a_bn; 509 struct vnode **a_vpp; 510 daddr_t *a_bnp; 511 } */ *ap; 512 { 513 514 if (ap->a_vpp != NULL) 515 *ap->a_vpp = ap->a_vp; 516 if (ap->a_bnp != NULL) 517 *ap->a_bnp = ap->a_bn; 518 return (0); 519 } 520 521 /* 522 * At the moment we do not do any locking. 523 */ 524 /* ARGSUSED */ 525 spec_lock(ap) 526 struct vop_lock_args /* { 527 struct vnode *a_vp; 528 } */ *ap; 529 { 530 531 return (0); 532 } 533 534 /* ARGSUSED */ 535 spec_unlock(ap) 536 struct vop_unlock_args /* { 537 struct vnode *a_vp; 538 } */ *ap; 539 { 540 541 return (0); 542 } 543 544 /* 545 * Device close routine 546 */ 547 /* ARGSUSED */ 548 spec_close(ap) 549 struct vop_close_args /* { 550 struct vnode *a_vp; 551 int a_fflag; 552 struct ucred *a_cred; 553 struct proc *a_p; 554 } */ *ap; 555 { 556 register struct vnode *vp = ap->a_vp; 557 dev_t dev = vp->v_rdev; 558 int (*devclose) __P((dev_t, int, int, struct proc *)); 559 int mode, error; 560 561 switch (vp->v_type) { 562 563 case VCHR: 564 /* 565 * Hack: a tty device that is a controlling terminal 566 * has a reference from the session structure. 567 * We cannot easily tell that a character device is 568 * a controlling terminal, unless it is the closing 569 * process' controlling terminal. In that case, 570 * if the reference count is 2 (this last descriptor 571 * plus the session), release the reference from the session. 572 */ 573 if (vcount(vp) == 2 && ap->a_p && 574 vp == ap->a_p->p_session->s_ttyvp) { 575 vrele(vp); 576 ap->a_p->p_session->s_ttyvp = NULL; 577 } 578 /* 579 * If the vnode is locked, then we are in the midst 580 * of forcably closing the device, otherwise we only 581 * close on last reference. 582 */ 583 if (vcount(vp) > 1 && (vp->v_flag & VXLOCK) == 0) 584 return (0); 585 devclose = cdevsw[major(dev)].d_close; 586 mode = S_IFCHR; 587 break; 588 589 case VBLK: 590 /* 591 * On last close of a block device (that isn't mounted) 592 * we must invalidate any in core blocks, so that 593 * we can, for instance, change floppy disks. 594 */ 595 if (error = vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 0, 0)) 596 return (error); 597 /* 598 * We do not want to really close the device if it 599 * is still in use unless we are trying to close it 600 * forcibly. Since every use (buffer, vnode, swap, cmap) 601 * holds a reference to the vnode, and because we mark 602 * any other vnodes that alias this device, when the 603 * sum of the reference counts on all the aliased 604 * vnodes descends to one, we are on last close. 605 */ 606 if (vcount(vp) > 1 && (vp->v_flag & VXLOCK) == 0) 607 return (0); 608 devclose = bdevsw[major(dev)].d_close; 609 mode = S_IFBLK; 610 break; 611 612 default: 613 panic("spec_close: not special"); 614 } 615 616 return ((*devclose)(dev, ap->a_fflag, mode, ap->a_p)); 617 } 618 619 /* 620 * Print out the contents of a special device vnode. 621 */ 622 spec_print(ap) 623 struct vop_print_args /* { 624 struct vnode *a_vp; 625 } */ *ap; 626 { 627 628 printf("tag VT_NON, dev %d, %d\n", major(ap->a_vp->v_rdev), 629 minor(ap->a_vp->v_rdev)); 630 } 631 632 /* 633 * Return POSIX pathconf information applicable to special devices. 634 */ 635 spec_pathconf(ap) 636 struct vop_pathconf_args /* { 637 struct vnode *a_vp; 638 int a_name; 639 register_t *a_retval; 640 } */ *ap; 641 { 642 643 switch (ap->a_name) { 644 case _PC_LINK_MAX: 645 *ap->a_retval = LINK_MAX; 646 return (0); 647 case _PC_MAX_CANON: 648 *ap->a_retval = MAX_CANON; 649 return (0); 650 case _PC_MAX_INPUT: 651 *ap->a_retval = MAX_INPUT; 652 return (0); 653 case _PC_PIPE_BUF: 654 *ap->a_retval = PIPE_BUF; 655 return (0); 656 case _PC_CHOWN_RESTRICTED: 657 *ap->a_retval = 1; 658 return (0); 659 case _PC_VDISABLE: 660 *ap->a_retval = _POSIX_VDISABLE; 661 return (0); 662 default: 663 return (EINVAL); 664 } 665 /* NOTREACHED */ 666 } 667 668 /* 669 * Special device advisory byte-level locks. 670 */ 671 /* ARGSUSED */ 672 spec_advlock(ap) 673 struct vop_advlock_args /* { 674 struct vnode *a_vp; 675 caddr_t a_id; 676 int a_op; 677 struct flock *a_fl; 678 int a_flags; 679 } */ *ap; 680 { 681 682 return (EOPNOTSUPP); 683 } 684 685 /* 686 * Special device failed operation 687 */ 688 spec_ebadf() 689 { 690 691 return (EBADF); 692 } 693 694 /* 695 * Special device bad operation 696 */ 697 spec_badop() 698 { 699 700 panic("spec_badop called"); 701 /* NOTREACHED */ 702 } 703