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