1 /* 2 * Copyright (c) 1982, 1986, 1989, 1991 Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * from: @(#)uipc_usrreq.c 7.26 (Berkeley) 6/3/91 34 * $Id: uipc_usrreq.c,v 1.3 1993/05/22 11:41:56 cgd Exp $ 35 */ 36 37 #include "param.h" 38 #include "proc.h" 39 #include "filedesc.h" 40 #include "domain.h" 41 #include "protosw.h" 42 #include "socket.h" 43 #include "socketvar.h" 44 #include "unpcb.h" 45 #include "un.h" 46 #include "namei.h" 47 #include "vnode.h" 48 #include "file.h" 49 #include "stat.h" 50 #include "mbuf.h" 51 52 /* 53 * Unix communications domain. 54 * 55 * TODO: 56 * SEQPACKET, RDM 57 * rethink name space problems 58 * need a proper out-of-band 59 */ 60 struct sockaddr sun_noname = { sizeof(sun_noname), AF_UNIX }; 61 ino_t unp_ino; /* prototype for fake inode numbers */ 62 63 /*ARGSUSED*/ 64 uipc_usrreq(so, req, m, nam, control) 65 struct socket *so; 66 int req; 67 struct mbuf *m, *nam, *control; 68 { 69 struct unpcb *unp = sotounpcb(so); 70 register struct socket *so2; 71 register int error = 0; 72 struct proc *p = curproc; /* XXX */ 73 74 if (req == PRU_CONTROL) 75 return (EOPNOTSUPP); 76 if (req != PRU_SEND && control && control->m_len) { 77 error = EOPNOTSUPP; 78 goto release; 79 } 80 if (unp == 0 && req != PRU_ATTACH) { 81 error = EINVAL; 82 goto release; 83 } 84 switch (req) { 85 86 case PRU_ATTACH: 87 if (unp) { 88 error = EISCONN; 89 break; 90 } 91 error = unp_attach(so); 92 break; 93 94 case PRU_DETACH: 95 unp_detach(unp); 96 break; 97 98 case PRU_BIND: 99 error = unp_bind(unp, nam, p); 100 break; 101 102 case PRU_LISTEN: 103 if (unp->unp_vnode == 0) 104 error = EINVAL; 105 break; 106 107 case PRU_CONNECT: 108 error = unp_connect(so, nam, p); 109 break; 110 111 case PRU_CONNECT2: 112 error = unp_connect2(so, (struct socket *)nam); 113 break; 114 115 case PRU_DISCONNECT: 116 unp_disconnect(unp); 117 break; 118 119 case PRU_ACCEPT: 120 /* 121 * Pass back name of connected socket, 122 * if it was bound and we are still connected 123 * (our peer may have closed already!). 124 */ 125 if (unp->unp_conn && unp->unp_conn->unp_addr) { 126 nam->m_len = unp->unp_conn->unp_addr->m_len; 127 bcopy(mtod(unp->unp_conn->unp_addr, caddr_t), 128 mtod(nam, caddr_t), (unsigned)nam->m_len); 129 } else { 130 nam->m_len = sizeof(sun_noname); 131 *(mtod(nam, struct sockaddr *)) = sun_noname; 132 } 133 break; 134 135 case PRU_SHUTDOWN: 136 socantsendmore(so); 137 unp_shutdown(unp); 138 break; 139 140 case PRU_RCVD: 141 switch (so->so_type) { 142 143 case SOCK_DGRAM: 144 panic("uipc 1"); 145 /*NOTREACHED*/ 146 147 case SOCK_STREAM: 148 #define rcv (&so->so_rcv) 149 #define snd (&so2->so_snd) 150 if (unp->unp_conn == 0) 151 break; 152 so2 = unp->unp_conn->unp_socket; 153 /* 154 * Adjust backpressure on sender 155 * and wakeup any waiting to write. 156 */ 157 snd->sb_mbmax += unp->unp_mbcnt - rcv->sb_mbcnt; 158 unp->unp_mbcnt = rcv->sb_mbcnt; 159 snd->sb_hiwat += unp->unp_cc - rcv->sb_cc; 160 unp->unp_cc = rcv->sb_cc; 161 sowwakeup(so2); 162 #undef snd 163 #undef rcv 164 break; 165 166 default: 167 panic("uipc 2"); 168 } 169 break; 170 171 case PRU_SEND: 172 if (control && (error = unp_internalize(control, p))) 173 break; 174 switch (so->so_type) { 175 176 case SOCK_DGRAM: { 177 struct sockaddr *from; 178 179 if (nam) { 180 if (unp->unp_conn) { 181 error = EISCONN; 182 break; 183 } 184 error = unp_connect(so, nam, p); 185 if (error) 186 break; 187 } else { 188 if (unp->unp_conn == 0) { 189 error = ENOTCONN; 190 break; 191 } 192 } 193 so2 = unp->unp_conn->unp_socket; 194 if (unp->unp_addr) 195 from = mtod(unp->unp_addr, struct sockaddr *); 196 else 197 from = &sun_noname; 198 if (sbappendaddr(&so2->so_rcv, from, m, control)) { 199 sorwakeup(so2); 200 m = 0; 201 control = 0; 202 } else 203 error = ENOBUFS; 204 if (nam) 205 unp_disconnect(unp); 206 break; 207 } 208 209 case SOCK_STREAM: 210 #define rcv (&so2->so_rcv) 211 #define snd (&so->so_snd) 212 if (so->so_state & SS_CANTSENDMORE) { 213 error = EPIPE; 214 break; 215 } 216 if (unp->unp_conn == 0) 217 panic("uipc 3"); 218 so2 = unp->unp_conn->unp_socket; 219 /* 220 * Send to paired receive port, and then reduce 221 * send buffer hiwater marks to maintain backpressure. 222 * Wake up readers. 223 */ 224 if (control) { 225 if (sbappendcontrol(rcv, m, control)) 226 control = 0; 227 } else 228 sbappend(rcv, m); 229 snd->sb_mbmax -= 230 rcv->sb_mbcnt - unp->unp_conn->unp_mbcnt; 231 unp->unp_conn->unp_mbcnt = rcv->sb_mbcnt; 232 snd->sb_hiwat -= rcv->sb_cc - unp->unp_conn->unp_cc; 233 unp->unp_conn->unp_cc = rcv->sb_cc; 234 sorwakeup(so2); 235 m = 0; 236 #undef snd 237 #undef rcv 238 break; 239 240 default: 241 panic("uipc 4"); 242 } 243 break; 244 245 case PRU_ABORT: 246 unp_drop(unp, ECONNABORTED); 247 break; 248 249 case PRU_SENSE: 250 ((struct stat *) m)->st_blksize = so->so_snd.sb_hiwat; 251 if (so->so_type == SOCK_STREAM && unp->unp_conn != 0) { 252 so2 = unp->unp_conn->unp_socket; 253 ((struct stat *) m)->st_blksize += so2->so_rcv.sb_cc; 254 } 255 ((struct stat *) m)->st_dev = NODEV; 256 if (unp->unp_ino == 0) 257 unp->unp_ino = unp_ino++; 258 ((struct stat *) m)->st_ino = unp->unp_ino; 259 return (0); 260 261 case PRU_RCVOOB: 262 return (EOPNOTSUPP); 263 264 case PRU_SENDOOB: 265 error = EOPNOTSUPP; 266 break; 267 268 case PRU_SOCKADDR: 269 if (unp->unp_addr) { 270 nam->m_len = unp->unp_addr->m_len; 271 bcopy(mtod(unp->unp_addr, caddr_t), 272 mtod(nam, caddr_t), (unsigned)nam->m_len); 273 } else 274 nam->m_len = 0; 275 break; 276 277 case PRU_PEERADDR: 278 if (unp->unp_conn && unp->unp_conn->unp_addr) { 279 nam->m_len = unp->unp_conn->unp_addr->m_len; 280 bcopy(mtod(unp->unp_conn->unp_addr, caddr_t), 281 mtod(nam, caddr_t), (unsigned)nam->m_len); 282 } else 283 nam->m_len = 0; 284 break; 285 286 case PRU_SLOWTIMO: 287 break; 288 289 default: 290 panic("piusrreq"); 291 } 292 release: 293 if (control) 294 m_freem(control); 295 if (m) 296 m_freem(m); 297 return (error); 298 } 299 300 /* 301 * Both send and receive buffers are allocated PIPSIZ bytes of buffering 302 * for stream sockets, although the total for sender and receiver is 303 * actually only PIPSIZ. 304 * Datagram sockets really use the sendspace as the maximum datagram size, 305 * and don't really want to reserve the sendspace. Their recvspace should 306 * be large enough for at least one max-size datagram plus address. 307 */ 308 #define PIPSIZ 4096 309 u_long unpst_sendspace = PIPSIZ; 310 u_long unpst_recvspace = PIPSIZ; 311 u_long unpdg_sendspace = 2*1024; /* really max datagram size */ 312 u_long unpdg_recvspace = 4*1024; 313 314 int unp_rights; /* file descriptors in flight */ 315 316 unp_attach(so) 317 struct socket *so; 318 { 319 register struct mbuf *m; 320 register struct unpcb *unp; 321 int error; 322 323 if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { 324 switch (so->so_type) { 325 326 case SOCK_STREAM: 327 error = soreserve(so, unpst_sendspace, unpst_recvspace); 328 break; 329 330 case SOCK_DGRAM: 331 error = soreserve(so, unpdg_sendspace, unpdg_recvspace); 332 break; 333 } 334 if (error) 335 return (error); 336 } 337 m = m_getclr(M_DONTWAIT, MT_PCB); 338 if (m == NULL) 339 return (ENOBUFS); 340 unp = mtod(m, struct unpcb *); 341 so->so_pcb = (caddr_t)unp; 342 unp->unp_socket = so; 343 return (0); 344 } 345 346 unp_detach(unp) 347 register struct unpcb *unp; 348 { 349 350 if (unp->unp_vnode) { 351 unp->unp_vnode->v_socket = 0; 352 vrele(unp->unp_vnode); 353 unp->unp_vnode = 0; 354 } 355 if (unp->unp_conn) 356 unp_disconnect(unp); 357 while (unp->unp_refs) 358 unp_drop(unp->unp_refs, ECONNRESET); 359 soisdisconnected(unp->unp_socket); 360 unp->unp_socket->so_pcb = 0; 361 m_freem(unp->unp_addr); 362 (void) m_free(dtom(unp)); 363 if (unp_rights) 364 unp_gc(); 365 } 366 367 unp_bind(unp, nam, p) 368 struct unpcb *unp; 369 struct mbuf *nam; 370 struct proc *p; 371 { 372 struct sockaddr_un *soun = mtod(nam, struct sockaddr_un *); 373 register struct vnode *vp; 374 register struct nameidata *ndp; 375 struct vattr vattr; 376 int error; 377 struct nameidata nd; 378 379 ndp = &nd; 380 ndp->ni_dirp = soun->sun_path; 381 if (unp->unp_vnode != NULL) 382 return (EINVAL); 383 if (nam->m_len == MLEN) { 384 if (*(mtod(nam, caddr_t) + nam->m_len - 1) != 0) 385 return (EINVAL); 386 } else 387 *(mtod(nam, caddr_t) + nam->m_len) = 0; 388 /* SHOULD BE ABLE TO ADOPT EXISTING AND wakeup() ALA FIFO's */ 389 ndp->ni_nameiop = CREATE | FOLLOW | LOCKPARENT; 390 ndp->ni_segflg = UIO_SYSSPACE; 391 if (error = namei(ndp, p)) 392 return (error); 393 vp = ndp->ni_vp; 394 if (vp != NULL) { 395 VOP_ABORTOP(ndp); 396 if (ndp->ni_dvp == vp) 397 vrele(ndp->ni_dvp); 398 else 399 vput(ndp->ni_dvp); 400 vrele(vp); 401 return (EADDRINUSE); 402 } 403 VATTR_NULL(&vattr); 404 vattr.va_type = VSOCK; 405 vattr.va_mode = 0777; 406 if (error = VOP_CREATE(ndp, &vattr, p)) 407 return (error); 408 vp = ndp->ni_vp; 409 vp->v_socket = unp->unp_socket; 410 unp->unp_vnode = vp; 411 unp->unp_addr = m_copy(nam, 0, (int)M_COPYALL); 412 VOP_UNLOCK(vp); 413 return (0); 414 } 415 416 unp_connect(so, nam, p) 417 struct socket *so; 418 struct mbuf *nam; 419 struct proc *p; 420 { 421 register struct sockaddr_un *soun = mtod(nam, struct sockaddr_un *); 422 register struct vnode *vp; 423 register struct socket *so2, *so3; 424 register struct nameidata *ndp; 425 struct unpcb *unp2, *unp3; 426 int error; 427 struct nameidata nd; 428 429 ndp = &nd; 430 ndp->ni_dirp = soun->sun_path; 431 if (nam->m_data + nam->m_len == &nam->m_dat[MLEN]) { /* XXX */ 432 if (*(mtod(nam, caddr_t) + nam->m_len - 1) != 0) 433 return (EMSGSIZE); 434 } else 435 *(mtod(nam, caddr_t) + nam->m_len) = 0; 436 ndp->ni_nameiop = LOOKUP | FOLLOW | LOCKLEAF; 437 ndp->ni_segflg = UIO_SYSSPACE; 438 if (error = namei(ndp, p)) 439 return (error); 440 vp = ndp->ni_vp; 441 if (vp->v_type != VSOCK) { 442 error = ENOTSOCK; 443 goto bad; 444 } 445 if (error = VOP_ACCESS(vp, VWRITE, p->p_ucred, p)) 446 goto bad; 447 so2 = vp->v_socket; 448 if (so2 == 0) { 449 error = ECONNREFUSED; 450 goto bad; 451 } 452 if (so->so_type != so2->so_type) { 453 error = EPROTOTYPE; 454 goto bad; 455 } 456 if (so->so_proto->pr_flags & PR_CONNREQUIRED) { 457 if ((so2->so_options & SO_ACCEPTCONN) == 0 || 458 (so3 = sonewconn(so2, 0)) == 0) { 459 error = ECONNREFUSED; 460 goto bad; 461 } 462 unp2 = sotounpcb(so2); 463 unp3 = sotounpcb(so3); 464 if (unp2->unp_addr) 465 unp3->unp_addr = 466 m_copy(unp2->unp_addr, 0, (int)M_COPYALL); 467 so2 = so3; 468 } 469 error = unp_connect2(so, so2); 470 bad: 471 vput(vp); 472 return (error); 473 } 474 475 unp_connect2(so, so2) 476 register struct socket *so; 477 register struct socket *so2; 478 { 479 register struct unpcb *unp = sotounpcb(so); 480 register struct unpcb *unp2; 481 482 if (so2->so_type != so->so_type) 483 return (EPROTOTYPE); 484 unp2 = sotounpcb(so2); 485 unp->unp_conn = unp2; 486 switch (so->so_type) { 487 488 case SOCK_DGRAM: 489 unp->unp_nextref = unp2->unp_refs; 490 unp2->unp_refs = unp; 491 soisconnected(so); 492 break; 493 494 case SOCK_STREAM: 495 unp2->unp_conn = unp; 496 soisconnected(so); 497 soisconnected(so2); 498 break; 499 500 default: 501 panic("unp_connect2"); 502 } 503 return (0); 504 } 505 506 unp_disconnect(unp) 507 struct unpcb *unp; 508 { 509 register struct unpcb *unp2 = unp->unp_conn; 510 511 if (unp2 == 0) 512 return; 513 unp->unp_conn = 0; 514 switch (unp->unp_socket->so_type) { 515 516 case SOCK_DGRAM: 517 if (unp2->unp_refs == unp) 518 unp2->unp_refs = unp->unp_nextref; 519 else { 520 unp2 = unp2->unp_refs; 521 for (;;) { 522 if (unp2 == 0) 523 panic("unp_disconnect"); 524 if (unp2->unp_nextref == unp) 525 break; 526 unp2 = unp2->unp_nextref; 527 } 528 unp2->unp_nextref = unp->unp_nextref; 529 } 530 unp->unp_nextref = 0; 531 unp->unp_socket->so_state &= ~SS_ISCONNECTED; 532 break; 533 534 case SOCK_STREAM: 535 soisdisconnected(unp->unp_socket); 536 unp2->unp_conn = 0; 537 soisdisconnected(unp2->unp_socket); 538 break; 539 } 540 } 541 542 #ifdef notdef 543 unp_abort(unp) 544 struct unpcb *unp; 545 { 546 547 unp_detach(unp); 548 } 549 #endif 550 551 unp_shutdown(unp) 552 struct unpcb *unp; 553 { 554 struct socket *so; 555 556 if (unp->unp_socket->so_type == SOCK_STREAM && unp->unp_conn && 557 (so = unp->unp_conn->unp_socket)) 558 socantrcvmore(so); 559 } 560 561 unp_drop(unp, errno) 562 struct unpcb *unp; 563 int errno; 564 { 565 struct socket *so = unp->unp_socket; 566 567 so->so_error = errno; 568 unp_disconnect(unp); 569 if (so->so_head) { 570 so->so_pcb = (caddr_t) 0; 571 m_freem(unp->unp_addr); 572 (void) m_free(dtom(unp)); 573 sofree(so); 574 } 575 } 576 577 #ifdef notdef 578 unp_drain() 579 { 580 581 } 582 #endif 583 584 unp_externalize(rights) 585 struct mbuf *rights; 586 { 587 struct proc *p = curproc; /* XXX */ 588 register int i; 589 register struct cmsghdr *cm = mtod(rights, struct cmsghdr *); 590 register struct file **rp = (struct file **)(cm + 1); 591 register struct file *fp; 592 int newfds = (cm->cmsg_len - sizeof(*cm)) / sizeof (int); 593 int f; 594 595 if (fdavail(p, newfds)) { 596 for (i = 0; i < newfds; i++) { 597 fp = *rp; 598 unp_discard(fp); 599 *rp++ = 0; 600 } 601 return (EMSGSIZE); 602 } 603 for (i = 0; i < newfds; i++) { 604 if (fdalloc(p, 0, &f)) 605 panic("unp_externalize"); 606 fp = *rp; 607 p->p_fd->fd_ofiles[f] = fp; 608 fp->f_msgcount--; 609 unp_rights--; 610 *(int *)rp++ = f; 611 } 612 return (0); 613 } 614 615 unp_internalize(control, p) 616 struct mbuf *control; 617 struct proc *p; 618 { 619 struct filedesc *fdp = p->p_fd; 620 register struct cmsghdr *cm = mtod(control, struct cmsghdr *); 621 register struct file **rp; 622 register struct file *fp; 623 register int i, fd; 624 int oldfds; 625 626 if (cm->cmsg_type != SCM_RIGHTS || cm->cmsg_level != SOL_SOCKET || 627 cm->cmsg_len != control->m_len) 628 return (EINVAL); 629 oldfds = (cm->cmsg_len - sizeof (*cm)) / sizeof (int); 630 rp = (struct file **)(cm + 1); 631 for (i = 0; i < oldfds; i++) { 632 fd = *(int *)rp++; 633 if ((unsigned)fd >= fdp->fd_nfiles || 634 fdp->fd_ofiles[fd] == NULL) 635 return (EBADF); 636 } 637 rp = (struct file **)(cm + 1); 638 for (i = 0; i < oldfds; i++) { 639 fp = fdp->fd_ofiles[*(int *)rp]; 640 *rp++ = fp; 641 fp->f_count++; 642 fp->f_msgcount++; 643 unp_rights++; 644 } 645 return (0); 646 } 647 648 int unp_defer, unp_gcing; 649 int unp_mark(); 650 extern struct domain unixdomain; 651 652 unp_gc() 653 { 654 register struct file *fp; 655 register struct socket *so; 656 657 if (unp_gcing) 658 return; 659 unp_gcing = 1; 660 restart: 661 unp_defer = 0; 662 for (fp = filehead; fp; fp = fp->f_filef) 663 fp->f_flag &= ~(FMARK|FDEFER); 664 do { 665 for (fp = filehead; fp; fp = fp->f_filef) { 666 if (fp->f_count == 0) 667 continue; 668 if (fp->f_flag & FDEFER) { 669 fp->f_flag &= ~FDEFER; 670 unp_defer--; 671 } else { 672 if (fp->f_flag & FMARK) 673 continue; 674 if (fp->f_count == fp->f_msgcount) 675 continue; 676 fp->f_flag |= FMARK; 677 } 678 if (fp->f_type != DTYPE_SOCKET || 679 (so = (struct socket *)fp->f_data) == 0) 680 continue; 681 if (so->so_proto->pr_domain != &unixdomain || 682 (so->so_proto->pr_flags&PR_RIGHTS) == 0) 683 continue; 684 #ifdef notdef 685 if (so->so_rcv.sb_flags & SB_LOCK) { 686 /* 687 * This is problematical; it's not clear 688 * we need to wait for the sockbuf to be 689 * unlocked (on a uniprocessor, at least), 690 * and it's also not clear what to do 691 * if sbwait returns an error due to receipt 692 * of a signal. If sbwait does return 693 * an error, we'll go into an infinite 694 * loop. Delete all of this for now. 695 */ 696 (void) sbwait(&so->so_rcv); 697 goto restart; 698 } 699 #endif 700 unp_scan(so->so_rcv.sb_mb, unp_mark); 701 } 702 } while (unp_defer); 703 for (fp = filehead; fp; fp = fp->f_filef) { 704 if (fp->f_count == 0) 705 continue; 706 if (fp->f_count == fp->f_msgcount && (fp->f_flag & FMARK) == 0) 707 while (fp->f_msgcount) 708 unp_discard(fp); 709 } 710 unp_gcing = 0; 711 } 712 713 unp_dispose(m) 714 struct mbuf *m; 715 { 716 int unp_discard(); 717 718 if (m) 719 unp_scan(m, unp_discard); 720 } 721 722 unp_scan(m0, op) 723 register struct mbuf *m0; 724 int (*op)(); 725 { 726 register struct mbuf *m; 727 register struct file **rp; 728 register struct cmsghdr *cm; 729 register int i; 730 int qfds; 731 732 while (m0) { 733 for (m = m0; m; m = m->m_next) 734 if (m->m_type == MT_CONTROL && 735 m->m_len >= sizeof(*cm)) { 736 cm = mtod(m, struct cmsghdr *); 737 if (cm->cmsg_level != SOL_SOCKET || 738 cm->cmsg_type != SCM_RIGHTS) 739 continue; 740 qfds = (cm->cmsg_len - sizeof *cm) 741 / sizeof (struct file *); 742 rp = (struct file **)(cm + 1); 743 for (i = 0; i < qfds; i++) 744 (*op)(*rp++); 745 break; /* XXX, but saves time */ 746 } 747 m0 = m0->m_act; 748 } 749 } 750 751 unp_mark(fp) 752 struct file *fp; 753 { 754 755 if (fp->f_flag & FMARK) 756 return; 757 unp_defer++; 758 fp->f_flag |= (FMARK|FDEFER); 759 } 760 761 unp_discard(fp) 762 struct file *fp; 763 { 764 765 fp->f_msgcount--; 766 unp_rights--; 767 (void) closef(fp, curproc); 768 } 769