Annotation of sys/kern/uipc_usrreq.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: uipc_usrreq.c,v 1.34 2007/01/16 17:52:18 thib Exp $ */
! 2: /* $NetBSD: uipc_usrreq.c,v 1.18 1996/02/09 19:00:50 christos Exp $ */
! 3:
! 4: /*
! 5: * Copyright (c) 1982, 1986, 1989, 1991, 1993
! 6: * The Regents of the University of California. All rights reserved.
! 7: *
! 8: * Redistribution and use in source and binary forms, with or without
! 9: * modification, are permitted provided that the following conditions
! 10: * are met:
! 11: * 1. Redistributions of source code must retain the above copyright
! 12: * notice, this list of conditions and the following disclaimer.
! 13: * 2. Redistributions in binary form must reproduce the above copyright
! 14: * notice, this list of conditions and the following disclaimer in the
! 15: * documentation and/or other materials provided with the distribution.
! 16: * 3. Neither the name of the University nor the names of its contributors
! 17: * may be used to endorse or promote products derived from this software
! 18: * without specific prior written permission.
! 19: *
! 20: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 21: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 22: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 23: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 24: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 25: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 26: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 27: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 28: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 29: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 30: * SUCH DAMAGE.
! 31: *
! 32: * @(#)uipc_usrreq.c 8.3 (Berkeley) 1/4/94
! 33: */
! 34:
! 35: #include <sys/param.h>
! 36: #include <sys/systm.h>
! 37: #include <sys/proc.h>
! 38: #include <sys/filedesc.h>
! 39: #include <sys/domain.h>
! 40: #include <sys/protosw.h>
! 41: #include <sys/socket.h>
! 42: #include <sys/socketvar.h>
! 43: #include <sys/unpcb.h>
! 44: #include <sys/un.h>
! 45: #include <sys/namei.h>
! 46: #include <sys/vnode.h>
! 47: #include <sys/file.h>
! 48: #include <sys/stat.h>
! 49: #include <sys/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: int
! 64: uipc_usrreq(struct socket *so, int req, struct mbuf *m, struct mbuf *nam,
! 65: struct mbuf *control)
! 66: {
! 67: struct unpcb *unp = sotounpcb(so);
! 68: struct socket *so2;
! 69: int error = 0;
! 70: struct proc *p = curproc; /* XXX */
! 71:
! 72: if (req == PRU_CONTROL)
! 73: return (EOPNOTSUPP);
! 74: if (req != PRU_SEND && control && control->m_len) {
! 75: error = EOPNOTSUPP;
! 76: goto release;
! 77: }
! 78: if (unp == NULL && req != PRU_ATTACH) {
! 79: error = EINVAL;
! 80: goto release;
! 81: }
! 82: switch (req) {
! 83:
! 84: case PRU_ATTACH:
! 85: if (unp) {
! 86: error = EISCONN;
! 87: break;
! 88: }
! 89: error = unp_attach(so);
! 90: break;
! 91:
! 92: case PRU_DETACH:
! 93: unp_detach(unp);
! 94: break;
! 95:
! 96: case PRU_BIND:
! 97: error = unp_bind(unp, nam, p);
! 98: break;
! 99:
! 100: case PRU_LISTEN:
! 101: if (unp->unp_vnode == NULL)
! 102: error = EINVAL;
! 103: break;
! 104:
! 105: case PRU_CONNECT:
! 106: error = unp_connect(so, nam, p);
! 107: break;
! 108:
! 109: case PRU_CONNECT2:
! 110: error = unp_connect2(so, (struct socket *)nam);
! 111: break;
! 112:
! 113: case PRU_DISCONNECT:
! 114: unp_disconnect(unp);
! 115: break;
! 116:
! 117: case PRU_ACCEPT:
! 118: /*
! 119: * Pass back name of connected socket,
! 120: * if it was bound and we are still connected
! 121: * (our peer may have closed already!).
! 122: */
! 123: if (unp->unp_conn && unp->unp_conn->unp_addr) {
! 124: nam->m_len = unp->unp_conn->unp_addr->m_len;
! 125: bcopy(mtod(unp->unp_conn->unp_addr, caddr_t),
! 126: mtod(nam, caddr_t), (unsigned)nam->m_len);
! 127: } else {
! 128: nam->m_len = sizeof(sun_noname);
! 129: *(mtod(nam, struct sockaddr *)) = sun_noname;
! 130: }
! 131: break;
! 132:
! 133: case PRU_SHUTDOWN:
! 134: socantsendmore(so);
! 135: unp_shutdown(unp);
! 136: break;
! 137:
! 138: case PRU_RCVD:
! 139: switch (so->so_type) {
! 140:
! 141: case SOCK_DGRAM:
! 142: panic("uipc 1");
! 143: /*NOTREACHED*/
! 144:
! 145: case SOCK_STREAM:
! 146: #define rcv (&so->so_rcv)
! 147: #define snd (&so2->so_snd)
! 148: if (unp->unp_conn == NULL)
! 149: break;
! 150: so2 = unp->unp_conn->unp_socket;
! 151: /*
! 152: * Adjust backpressure on sender
! 153: * and wakeup any waiting to write.
! 154: */
! 155: snd->sb_mbmax += unp->unp_mbcnt - rcv->sb_mbcnt;
! 156: unp->unp_mbcnt = rcv->sb_mbcnt;
! 157: snd->sb_hiwat += unp->unp_cc - rcv->sb_cc;
! 158: unp->unp_cc = rcv->sb_cc;
! 159: sowwakeup(so2);
! 160: #undef snd
! 161: #undef rcv
! 162: break;
! 163:
! 164: default:
! 165: panic("uipc 2");
! 166: }
! 167: break;
! 168:
! 169: case PRU_SEND:
! 170: if (control && (error = unp_internalize(control, p)))
! 171: break;
! 172: switch (so->so_type) {
! 173:
! 174: case SOCK_DGRAM: {
! 175: struct sockaddr *from;
! 176:
! 177: if (nam) {
! 178: if (unp->unp_conn) {
! 179: error = EISCONN;
! 180: break;
! 181: }
! 182: error = unp_connect(so, nam, p);
! 183: if (error)
! 184: break;
! 185: } else {
! 186: if (unp->unp_conn == NULL) {
! 187: error = ENOTCONN;
! 188: break;
! 189: }
! 190: }
! 191: so2 = unp->unp_conn->unp_socket;
! 192: if (unp->unp_addr)
! 193: from = mtod(unp->unp_addr, struct sockaddr *);
! 194: else
! 195: from = &sun_noname;
! 196: if (sbappendaddr(&so2->so_rcv, from, m, control)) {
! 197: sorwakeup(so2);
! 198: m = NULL;
! 199: control = NULL;
! 200: } else
! 201: error = ENOBUFS;
! 202: if (nam)
! 203: unp_disconnect(unp);
! 204: break;
! 205: }
! 206:
! 207: case SOCK_STREAM:
! 208: #define rcv (&so2->so_rcv)
! 209: #define snd (&so->so_snd)
! 210: if (so->so_state & SS_CANTSENDMORE) {
! 211: error = EPIPE;
! 212: break;
! 213: }
! 214: if (unp->unp_conn == NULL) {
! 215: error = ENOTCONN;
! 216: break;
! 217: }
! 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 = NULL;
! 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 = NULL;
! 236: #undef snd
! 237: #undef rcv
! 238: break;
! 239:
! 240: default:
! 241: panic("uipc 4");
! 242: }
! 243: /* we need to undo unp_internalize in case of errors */
! 244: if (control && error)
! 245: unp_dispose(control);
! 246: break;
! 247:
! 248: case PRU_ABORT:
! 249: unp_drop(unp, ECONNABORTED);
! 250: break;
! 251:
! 252: case PRU_SENSE:
! 253: ((struct stat *) m)->st_blksize = so->so_snd.sb_hiwat;
! 254: if (so->so_type == SOCK_STREAM && unp->unp_conn != NULL) {
! 255: so2 = unp->unp_conn->unp_socket;
! 256: ((struct stat *) m)->st_blksize += so2->so_rcv.sb_cc;
! 257: }
! 258: ((struct stat *) m)->st_dev = NODEV;
! 259: if (unp->unp_ino == 0)
! 260: unp->unp_ino = unp_ino++;
! 261: ((struct stat *) m)->st_atimespec =
! 262: ((struct stat *) m)->st_mtimespec =
! 263: ((struct stat *) m)->st_ctimespec = unp->unp_ctime;
! 264: ((struct stat *) m)->st_ino = unp->unp_ino;
! 265: return (0);
! 266:
! 267: case PRU_RCVOOB:
! 268: return (EOPNOTSUPP);
! 269:
! 270: case PRU_SENDOOB:
! 271: error = EOPNOTSUPP;
! 272: break;
! 273:
! 274: case PRU_SOCKADDR:
! 275: if (unp->unp_addr) {
! 276: nam->m_len = unp->unp_addr->m_len;
! 277: bcopy(mtod(unp->unp_addr, caddr_t),
! 278: mtod(nam, caddr_t), (unsigned)nam->m_len);
! 279: } else
! 280: nam->m_len = 0;
! 281: break;
! 282:
! 283: case PRU_PEERADDR:
! 284: if (unp->unp_conn && unp->unp_conn->unp_addr) {
! 285: nam->m_len = unp->unp_conn->unp_addr->m_len;
! 286: bcopy(mtod(unp->unp_conn->unp_addr, caddr_t),
! 287: mtod(nam, caddr_t), (unsigned)nam->m_len);
! 288: } else
! 289: nam->m_len = 0;
! 290: break;
! 291:
! 292: case PRU_PEEREID:
! 293: if (unp->unp_flags & UNP_FEIDS) {
! 294: nam->m_len = sizeof(struct unpcbid);
! 295: bcopy((caddr_t)(&(unp->unp_connid)),
! 296: mtod(nam, caddr_t), (unsigned)nam->m_len);
! 297: } else
! 298: nam->m_len = 0;
! 299: break;
! 300:
! 301: case PRU_SLOWTIMO:
! 302: break;
! 303:
! 304: default:
! 305: panic("piusrreq");
! 306: }
! 307: release:
! 308: if (control)
! 309: m_freem(control);
! 310: if (m)
! 311: m_freem(m);
! 312: return (error);
! 313: }
! 314:
! 315: /*
! 316: * Both send and receive buffers are allocated PIPSIZ bytes of buffering
! 317: * for stream sockets, although the total for sender and receiver is
! 318: * actually only PIPSIZ.
! 319: * Datagram sockets really use the sendspace as the maximum datagram size,
! 320: * and don't really want to reserve the sendspace. Their recvspace should
! 321: * be large enough for at least one max-size datagram plus address.
! 322: */
! 323: #define PIPSIZ 4096
! 324: u_long unpst_sendspace = PIPSIZ;
! 325: u_long unpst_recvspace = PIPSIZ;
! 326: u_long unpdg_sendspace = 2*1024; /* really max datagram size */
! 327: u_long unpdg_recvspace = 4*1024;
! 328:
! 329: int unp_rights; /* file descriptors in flight */
! 330:
! 331: int
! 332: unp_attach(struct socket *so)
! 333: {
! 334: struct unpcb *unp;
! 335: int error;
! 336:
! 337: if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
! 338: switch (so->so_type) {
! 339:
! 340: case SOCK_STREAM:
! 341: error = soreserve(so, unpst_sendspace, unpst_recvspace);
! 342: break;
! 343:
! 344: case SOCK_DGRAM:
! 345: error = soreserve(so, unpdg_sendspace, unpdg_recvspace);
! 346: break;
! 347:
! 348: default:
! 349: panic("unp_attach");
! 350: }
! 351: if (error)
! 352: return (error);
! 353: }
! 354: unp = malloc(sizeof(*unp), M_PCB, M_NOWAIT);
! 355: if (unp == NULL)
! 356: return (ENOBUFS);
! 357: bzero((caddr_t)unp, sizeof(*unp));
! 358: unp->unp_socket = so;
! 359: so->so_pcb = unp;
! 360: nanotime(&unp->unp_ctime);
! 361: return (0);
! 362: }
! 363:
! 364: void
! 365: unp_detach(struct unpcb *unp)
! 366: {
! 367:
! 368: if (unp->unp_vnode) {
! 369: unp->unp_vnode->v_socket = NULL;
! 370: vrele(unp->unp_vnode);
! 371: unp->unp_vnode = NULL;
! 372: }
! 373: if (unp->unp_conn)
! 374: unp_disconnect(unp);
! 375: while (unp->unp_refs)
! 376: unp_drop(unp->unp_refs, ECONNRESET);
! 377: soisdisconnected(unp->unp_socket);
! 378: unp->unp_socket->so_pcb = NULL;
! 379: m_freem(unp->unp_addr);
! 380: if (unp_rights) {
! 381: /*
! 382: * Normally the receive buffer is flushed later,
! 383: * in sofree, but if our receive buffer holds references
! 384: * to descriptors that are now garbage, we will dispose
! 385: * of those descriptor references after the garbage collector
! 386: * gets them (resulting in a "panic: closef: count < 0").
! 387: */
! 388: sorflush(unp->unp_socket);
! 389: free(unp, M_PCB);
! 390: unp_gc();
! 391: } else
! 392: free(unp, M_PCB);
! 393: }
! 394:
! 395: int
! 396: unp_bind(struct unpcb *unp, struct mbuf *nam, struct proc *p)
! 397: {
! 398: struct sockaddr_un *soun = mtod(nam, struct sockaddr_un *);
! 399: struct vnode *vp;
! 400: struct vattr vattr;
! 401: int error, namelen;
! 402: struct nameidata nd;
! 403: char buf[MLEN];
! 404:
! 405: if (unp->unp_vnode != NULL)
! 406: return (EINVAL);
! 407: namelen = soun->sun_len - offsetof(struct sockaddr_un, sun_path);
! 408: if (namelen <= 0 || namelen >= MLEN)
! 409: return EINVAL;
! 410: strncpy(buf, soun->sun_path, namelen);
! 411: buf[namelen] = 0; /* null-terminate the string */
! 412: NDINIT(&nd, CREATE, NOFOLLOW | LOCKPARENT, UIO_SYSSPACE, buf, p);
! 413: /* SHOULD BE ABLE TO ADOPT EXISTING AND wakeup() ALA FIFO's */
! 414: if ((error = namei(&nd)) != 0)
! 415: return (error);
! 416: vp = nd.ni_vp;
! 417: if (vp != NULL) {
! 418: VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
! 419: if (nd.ni_dvp == vp)
! 420: vrele(nd.ni_dvp);
! 421: else
! 422: vput(nd.ni_dvp);
! 423: vrele(vp);
! 424: return (EADDRINUSE);
! 425: }
! 426: VATTR_NULL(&vattr);
! 427: vattr.va_type = VSOCK;
! 428: vattr.va_mode = ACCESSPERMS &~ p->p_fd->fd_cmask;
! 429: error = VOP_CREATE(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr);
! 430: if (error)
! 431: return (error);
! 432: vp = nd.ni_vp;
! 433: vp->v_socket = unp->unp_socket;
! 434: unp->unp_vnode = vp;
! 435: unp->unp_addr = m_copy(nam, 0, (int)M_COPYALL);
! 436: unp->unp_connid.unp_euid = p->p_ucred->cr_uid;
! 437: unp->unp_connid.unp_egid = p->p_ucred->cr_gid;
! 438: unp->unp_flags |= UNP_FEIDSBIND;
! 439: VOP_UNLOCK(vp, 0, p);
! 440: return (0);
! 441: }
! 442:
! 443: int
! 444: unp_connect(struct socket *so, struct mbuf *nam, struct proc *p)
! 445: {
! 446: struct sockaddr_un *soun = mtod(nam, struct sockaddr_un *);
! 447: struct vnode *vp;
! 448: struct socket *so2, *so3;
! 449: struct unpcb *unp, *unp2, *unp3;
! 450: int error;
! 451: struct nameidata nd;
! 452:
! 453: NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, soun->sun_path, p);
! 454: if (nam->m_data + nam->m_len == &nam->m_dat[MLEN]) { /* XXX */
! 455: if (*(mtod(nam, caddr_t) + nam->m_len - 1) != 0)
! 456: return (EMSGSIZE);
! 457: } else
! 458: *(mtod(nam, caddr_t) + nam->m_len) = 0;
! 459: if ((error = namei(&nd)) != 0)
! 460: return (error);
! 461: vp = nd.ni_vp;
! 462: if (vp->v_type != VSOCK) {
! 463: error = ENOTSOCK;
! 464: goto bad;
! 465: }
! 466: if ((error = VOP_ACCESS(vp, VWRITE, p->p_ucred, p)) != 0)
! 467: goto bad;
! 468: so2 = vp->v_socket;
! 469: if (so2 == NULL) {
! 470: error = ECONNREFUSED;
! 471: goto bad;
! 472: }
! 473: if (so->so_type != so2->so_type) {
! 474: error = EPROTOTYPE;
! 475: goto bad;
! 476: }
! 477: if (so->so_proto->pr_flags & PR_CONNREQUIRED) {
! 478: if ((so2->so_options & SO_ACCEPTCONN) == 0 ||
! 479: (so3 = sonewconn(so2, 0)) == 0) {
! 480: error = ECONNREFUSED;
! 481: goto bad;
! 482: }
! 483: unp = sotounpcb(so);
! 484: unp2 = sotounpcb(so2);
! 485: unp3 = sotounpcb(so3);
! 486: if (unp2->unp_addr)
! 487: unp3->unp_addr =
! 488: m_copy(unp2->unp_addr, 0, (int)M_COPYALL);
! 489: unp3->unp_connid.unp_euid = p->p_ucred->cr_uid;
! 490: unp3->unp_connid.unp_egid = p->p_ucred->cr_gid;
! 491: unp3->unp_flags |= UNP_FEIDS;
! 492: so2 = so3;
! 493: if (unp2->unp_flags & UNP_FEIDSBIND) {
! 494: unp->unp_connid.unp_euid = unp2->unp_connid.unp_euid;
! 495: unp->unp_connid.unp_egid = unp2->unp_connid.unp_egid;
! 496: unp->unp_flags |= UNP_FEIDS;
! 497: }
! 498: }
! 499: error = unp_connect2(so, so2);
! 500: bad:
! 501: vput(vp);
! 502: return (error);
! 503: }
! 504:
! 505: int
! 506: unp_connect2(struct socket *so, struct socket *so2)
! 507: {
! 508: struct unpcb *unp = sotounpcb(so);
! 509: struct unpcb *unp2;
! 510:
! 511: if (so2->so_type != so->so_type)
! 512: return (EPROTOTYPE);
! 513: unp2 = sotounpcb(so2);
! 514: unp->unp_conn = unp2;
! 515: switch (so->so_type) {
! 516:
! 517: case SOCK_DGRAM:
! 518: unp->unp_nextref = unp2->unp_refs;
! 519: unp2->unp_refs = unp;
! 520: soisconnected(so);
! 521: break;
! 522:
! 523: case SOCK_STREAM:
! 524: unp2->unp_conn = unp;
! 525: soisconnected(so);
! 526: soisconnected(so2);
! 527: break;
! 528:
! 529: default:
! 530: panic("unp_connect2");
! 531: }
! 532: return (0);
! 533: }
! 534:
! 535: void
! 536: unp_disconnect(struct unpcb *unp)
! 537: {
! 538: struct unpcb *unp2 = unp->unp_conn;
! 539:
! 540: if (unp2 == NULL)
! 541: return;
! 542: unp->unp_conn = NULL;
! 543: switch (unp->unp_socket->so_type) {
! 544:
! 545: case SOCK_DGRAM:
! 546: if (unp2->unp_refs == unp)
! 547: unp2->unp_refs = unp->unp_nextref;
! 548: else {
! 549: unp2 = unp2->unp_refs;
! 550: for (;;) {
! 551: if (unp2 == NULL)
! 552: panic("unp_disconnect");
! 553: if (unp2->unp_nextref == unp)
! 554: break;
! 555: unp2 = unp2->unp_nextref;
! 556: }
! 557: unp2->unp_nextref = unp->unp_nextref;
! 558: }
! 559: unp->unp_nextref = NULL;
! 560: unp->unp_socket->so_state &= ~SS_ISCONNECTED;
! 561: break;
! 562:
! 563: case SOCK_STREAM:
! 564: soisdisconnected(unp->unp_socket);
! 565: unp2->unp_conn = NULL;
! 566: soisdisconnected(unp2->unp_socket);
! 567: break;
! 568: }
! 569: }
! 570:
! 571: #ifdef notdef
! 572: unp_abort(struct unpcb *unp)
! 573: {
! 574: unp_detach(unp);
! 575: }
! 576: #endif
! 577:
! 578: void
! 579: unp_shutdown(struct unpcb *unp)
! 580: {
! 581: struct socket *so;
! 582:
! 583: if (unp->unp_socket->so_type == SOCK_STREAM && unp->unp_conn &&
! 584: (so = unp->unp_conn->unp_socket))
! 585: socantrcvmore(so);
! 586: }
! 587:
! 588: void
! 589: unp_drop(struct unpcb *unp, int errno)
! 590: {
! 591: struct socket *so = unp->unp_socket;
! 592:
! 593: so->so_error = errno;
! 594: unp_disconnect(unp);
! 595: if (so->so_head) {
! 596: so->so_pcb = NULL;
! 597: sofree(so);
! 598: m_freem(unp->unp_addr);
! 599: free(unp, M_PCB);
! 600: }
! 601: }
! 602:
! 603: #ifdef notdef
! 604: unp_drain(void)
! 605: {
! 606:
! 607: }
! 608: #endif
! 609:
! 610: int
! 611: unp_externalize(struct mbuf *rights)
! 612: {
! 613: struct proc *p = curproc; /* XXX */
! 614: struct cmsghdr *cm = mtod(rights, struct cmsghdr *);
! 615: int i, *fdp;
! 616: struct file **rp;
! 617: struct file *fp;
! 618: int nfds, error = 0;
! 619:
! 620: nfds = (cm->cmsg_len - CMSG_ALIGN(sizeof(*cm))) /
! 621: sizeof(struct file *);
! 622: rp = (struct file **)CMSG_DATA(cm);
! 623:
! 624: fdp = malloc(nfds * sizeof(int), M_TEMP, M_WAITOK);
! 625:
! 626: #ifdef notyet
! 627: /* Make sure the recipient should be able to see the descriptors.. */
! 628: if (p->p_cwdi->cwdi_rdir != NULL) {
! 629: rp = (struct file **)CMSG_DATA(cm);
! 630: for (i = 0; i < nfds; i++) {
! 631: fp = *rp++;
! 632: /*
! 633: * If we are in a chroot'ed directory, and
! 634: * someone wants to pass us a directory, make
! 635: * sure it's inside the subtree we're allowed
! 636: * to access.
! 637: */
! 638: if (fp->f_type == DTYPE_VNODE) {
! 639: struct vnode *vp = (struct vnode *)fp->f_data;
! 640: if ((vp->v_type == VDIR) &&
! 641: !vn_isunder(vp, p->p_cwdi->cwdi_rdir, p)) {
! 642: error = EPERM;
! 643: break;
! 644: }
! 645: }
! 646: }
! 647: }
! 648: #endif
! 649:
! 650: restart:
! 651: fdplock(p->p_fd);
! 652: if (error != 0) {
! 653: rp = ((struct file **)CMSG_DATA(cm));
! 654: for (i = 0; i < nfds; i++) {
! 655: fp = *rp;
! 656: /*
! 657: * zero the pointer before calling unp_discard,
! 658: * since it may end up in unp_gc()..
! 659: */
! 660: *rp++ = NULL;
! 661: unp_discard(fp);
! 662: }
! 663: goto out;
! 664: }
! 665:
! 666: /*
! 667: * First loop -- allocate file descriptor table slots for the
! 668: * new descriptors.
! 669: */
! 670: rp = ((struct file **)CMSG_DATA(cm));
! 671: for (i = 0; i < nfds; i++) {
! 672: bcopy(rp, &fp, sizeof(fp));
! 673: rp++;
! 674: if ((error = fdalloc(p, 0, &fdp[i])) != 0) {
! 675: /*
! 676: * Back out what we've done so far.
! 677: */
! 678: for (--i; i >= 0; i--)
! 679: fdremove(p->p_fd, fdp[i]);
! 680:
! 681: if (error == ENOSPC) {
! 682: fdexpand(p);
! 683: error = 0;
! 684: } else {
! 685: /*
! 686: * This is the error that has historically
! 687: * been returned, and some callers may
! 688: * expect it.
! 689: */
! 690: error = EMSGSIZE;
! 691: }
! 692: fdpunlock(p->p_fd);
! 693: goto restart;
! 694: }
! 695:
! 696: /*
! 697: * Make the slot reference the descriptor so that
! 698: * fdalloc() works properly.. We finalize it all
! 699: * in the loop below.
! 700: */
! 701: p->p_fd->fd_ofiles[fdp[i]] = fp;
! 702: }
! 703:
! 704: /*
! 705: * Now that adding them has succeeded, update all of the
! 706: * descriptor passing state.
! 707: */
! 708: rp = (struct file **)CMSG_DATA(cm);
! 709: for (i = 0; i < nfds; i++) {
! 710: fp = *rp++;
! 711: fp->f_msgcount--;
! 712: unp_rights--;
! 713: }
! 714:
! 715: /*
! 716: * Copy temporary array to message and adjust length, in case of
! 717: * transition from large struct file pointers to ints.
! 718: */
! 719: memcpy(CMSG_DATA(cm), fdp, nfds * sizeof(int));
! 720: cm->cmsg_len = CMSG_LEN(nfds * sizeof(int));
! 721: rights->m_len = CMSG_SPACE(nfds * sizeof(int));
! 722: out:
! 723: fdpunlock(p->p_fd);
! 724: free(fdp, M_TEMP);
! 725: return (error);
! 726: }
! 727:
! 728: int
! 729: unp_internalize(struct mbuf *control, struct proc *p)
! 730: {
! 731: struct filedesc *fdp = p->p_fd;
! 732: struct cmsghdr *cm = mtod(control, struct cmsghdr *);
! 733: struct file **rp, *fp;
! 734: int i, error;
! 735: int nfds, *ip, fd, neededspace;
! 736:
! 737: if (cm->cmsg_type != SCM_RIGHTS || cm->cmsg_level != SOL_SOCKET ||
! 738: cm->cmsg_len != control->m_len)
! 739: return (EINVAL);
! 740: nfds = (cm->cmsg_len - CMSG_ALIGN(sizeof(*cm))) / sizeof (int);
! 741:
! 742: /* Make sure we have room for the struct file pointers */
! 743: morespace:
! 744: neededspace = CMSG_SPACE(nfds * sizeof(struct file *)) -
! 745: control->m_len;
! 746: if (neededspace > M_TRAILINGSPACE(control)) {
! 747: /* if we already have a cluster, the message is just too big */
! 748: if (control->m_flags & M_EXT)
! 749: return (E2BIG);
! 750:
! 751: /* allocate a cluster and try again */
! 752: MCLGET(control, M_WAIT);
! 753: if ((control->m_flags & M_EXT) == 0)
! 754: return (ENOBUFS); /* allocation failed */
! 755:
! 756: /* copy the data to the cluster */
! 757: memcpy(mtod(control, char *), cm, cm->cmsg_len);
! 758: cm = mtod(control, struct cmsghdr *);
! 759: goto morespace;
! 760: }
! 761:
! 762: /* adjust message & mbuf to note amount of space actually used. */
! 763: cm->cmsg_len = CMSG_LEN(nfds * sizeof(struct file *));
! 764: control->m_len = CMSG_SPACE(nfds * sizeof(struct file *));
! 765:
! 766: ip = ((int *)CMSG_DATA(cm)) + nfds - 1;
! 767: rp = ((struct file **)CMSG_DATA(cm)) + nfds - 1;
! 768: for (i = 0; i < nfds; i++) {
! 769: bcopy(ip, &fd, sizeof fd);
! 770: ip--;
! 771: if ((fp = fd_getfile(fdp, fd)) == NULL) {
! 772: error = EBADF;
! 773: goto fail;
! 774: }
! 775: if (fp->f_count == LONG_MAX-2 ||
! 776: fp->f_msgcount == LONG_MAX-2) {
! 777: error = EDEADLK;
! 778: goto fail;
! 779: }
! 780: bcopy(&fp, rp, sizeof fp);
! 781: rp--;
! 782: fp->f_count++;
! 783: fp->f_msgcount++;
! 784: unp_rights++;
! 785: }
! 786: return (0);
! 787: fail:
! 788: /* Back out what we just did. */
! 789: for ( ; i > 0; i--) {
! 790: bcopy(rp, &fp, sizeof(fp));
! 791: rp++;
! 792: fp->f_count--;
! 793: fp->f_msgcount--;
! 794: unp_rights--;
! 795: }
! 796:
! 797: return (error);
! 798: }
! 799:
! 800: int unp_defer, unp_gcing;
! 801: extern struct domain unixdomain;
! 802:
! 803: void
! 804: unp_gc(void)
! 805: {
! 806: struct file *fp, *nextfp;
! 807: struct socket *so;
! 808: struct file **extra_ref, **fpp;
! 809: int nunref, i;
! 810:
! 811: if (unp_gcing)
! 812: return;
! 813: unp_gcing = 1;
! 814: unp_defer = 0;
! 815: LIST_FOREACH(fp, &filehead, f_list)
! 816: fp->f_flag &= ~(FMARK|FDEFER);
! 817: do {
! 818: LIST_FOREACH(fp, &filehead, f_list) {
! 819: if (fp->f_flag & FDEFER) {
! 820: fp->f_flag &= ~FDEFER;
! 821: unp_defer--;
! 822: } else {
! 823: if (fp->f_count == 0)
! 824: continue;
! 825: if (fp->f_flag & FMARK)
! 826: continue;
! 827: if (fp->f_count == fp->f_msgcount)
! 828: continue;
! 829: }
! 830: fp->f_flag |= FMARK;
! 831:
! 832: if (fp->f_type != DTYPE_SOCKET ||
! 833: (so = (struct socket *)fp->f_data) == NULL)
! 834: continue;
! 835: if (so->so_proto->pr_domain != &unixdomain ||
! 836: (so->so_proto->pr_flags&PR_RIGHTS) == 0)
! 837: continue;
! 838: #ifdef notdef
! 839: if (so->so_rcv.sb_flags & SB_LOCK) {
! 840: /*
! 841: * This is problematical; it's not clear
! 842: * we need to wait for the sockbuf to be
! 843: * unlocked (on a uniprocessor, at least),
! 844: * and it's also not clear what to do
! 845: * if sbwait returns an error due to receipt
! 846: * of a signal. If sbwait does return
! 847: * an error, we'll go into an infinite
! 848: * loop. Delete all of this for now.
! 849: */
! 850: (void) sbwait(&so->so_rcv);
! 851: goto restart;
! 852: }
! 853: #endif
! 854: unp_scan(so->so_rcv.sb_mb, unp_mark, 0);
! 855: }
! 856: } while (unp_defer);
! 857: /*
! 858: * We grab an extra reference to each of the file table entries
! 859: * that are not otherwise accessible and then free the rights
! 860: * that are stored in messages on them.
! 861: *
! 862: * The bug in the original code is a little tricky, so I'll describe
! 863: * what's wrong with it here.
! 864: *
! 865: * It is incorrect to simply unp_discard each entry for f_msgcount
! 866: * times -- consider the case of sockets A and B that contain
! 867: * references to each other. On a last close of some other socket,
! 868: * we trigger a gc since the number of outstanding rights (unp_rights)
! 869: * is non-zero. If during the sweep phase the gc code un_discards,
! 870: * we end up doing a (full) closef on the descriptor. A closef on A
! 871: * results in the following chain. Closef calls soo_close, which
! 872: * calls soclose. Soclose calls first (through the switch
! 873: * uipc_usrreq) unp_detach, which re-invokes unp_gc. Unp_gc simply
! 874: * returns because the previous instance had set unp_gcing, and
! 875: * we return all the way back to soclose, which marks the socket
! 876: * with SS_NOFDREF, and then calls sofree. Sofree calls sorflush
! 877: * to free up the rights that are queued in messages on the socket A,
! 878: * i.e., the reference on B. The sorflush calls via the dom_dispose
! 879: * switch unp_dispose, which unp_scans with unp_discard. This second
! 880: * instance of unp_discard just calls closef on B.
! 881: *
! 882: * Well, a similar chain occurs on B, resulting in a sorflush on B,
! 883: * which results in another closef on A. Unfortunately, A is already
! 884: * being closed, and the descriptor has already been marked with
! 885: * SS_NOFDREF, and soclose panics at this point.
! 886: *
! 887: * Here, we first take an extra reference to each inaccessible
! 888: * descriptor. Then, we call sorflush ourself, since we know
! 889: * it is a Unix domain socket anyhow. After we destroy all the
! 890: * rights carried in messages, we do a last closef to get rid
! 891: * of our extra reference. This is the last close, and the
! 892: * unp_detach etc will shut down the socket.
! 893: *
! 894: * 91/09/19, bsy@cs.cmu.edu
! 895: */
! 896: extra_ref = malloc(nfiles * sizeof(struct file *), M_FILE, M_WAITOK);
! 897: for (nunref = 0, fp = LIST_FIRST(&filehead), fpp = extra_ref;
! 898: fp != NULL; fp = nextfp) {
! 899: nextfp = LIST_NEXT(fp, f_list);
! 900: if (fp->f_count == 0)
! 901: continue;
! 902: if (fp->f_count == fp->f_msgcount && !(fp->f_flag & FMARK)) {
! 903: *fpp++ = fp;
! 904: nunref++;
! 905: FREF(fp);
! 906: fp->f_count++;
! 907: }
! 908: }
! 909: for (i = nunref, fpp = extra_ref; --i >= 0; ++fpp)
! 910: if ((*fpp)->f_type == DTYPE_SOCKET && (*fpp)->f_data != NULL)
! 911: sorflush((struct socket *)(*fpp)->f_data);
! 912: for (i = nunref, fpp = extra_ref; --i >= 0; ++fpp)
! 913: (void) closef(*fpp, NULL);
! 914: free((caddr_t)extra_ref, M_FILE);
! 915: unp_gcing = 0;
! 916: }
! 917:
! 918: void
! 919: unp_dispose(struct mbuf *m)
! 920: {
! 921:
! 922: if (m)
! 923: unp_scan(m, unp_discard, 1);
! 924: }
! 925:
! 926: void
! 927: unp_scan(struct mbuf *m0, void (*op)(struct file *), int discard)
! 928: {
! 929: struct mbuf *m;
! 930: struct file **rp, *fp;
! 931: struct cmsghdr *cm;
! 932: int i;
! 933: int qfds;
! 934:
! 935: while (m0) {
! 936: for (m = m0; m; m = m->m_next) {
! 937: if (m->m_type == MT_CONTROL &&
! 938: m->m_len >= sizeof(*cm)) {
! 939: cm = mtod(m, struct cmsghdr *);
! 940: if (cm->cmsg_level != SOL_SOCKET ||
! 941: cm->cmsg_type != SCM_RIGHTS)
! 942: continue;
! 943: qfds = (cm->cmsg_len - CMSG_ALIGN(sizeof *cm))
! 944: / sizeof(struct file *);
! 945: rp = (struct file **)CMSG_DATA(cm);
! 946: for (i = 0; i < qfds; i++) {
! 947: fp = *rp;
! 948: if (discard)
! 949: *rp = 0;
! 950: (*op)(fp);
! 951: rp++;
! 952: }
! 953: break; /* XXX, but saves time */
! 954: }
! 955: }
! 956: m0 = m0->m_nextpkt;
! 957: }
! 958: }
! 959:
! 960: void
! 961: unp_mark(struct file *fp)
! 962: {
! 963: if (fp == NULL)
! 964: return;
! 965:
! 966: if (fp->f_flag & FMARK)
! 967: return;
! 968:
! 969: if (fp->f_flag & FDEFER)
! 970: return;
! 971:
! 972: if (fp->f_type == DTYPE_SOCKET) {
! 973: unp_defer++;
! 974: fp->f_flag |= FDEFER;
! 975: } else {
! 976: fp->f_flag |= FMARK;
! 977: }
! 978: }
! 979:
! 980: void
! 981: unp_discard(struct file *fp)
! 982: {
! 983:
! 984: if (fp == NULL)
! 985: return;
! 986: FREF(fp);
! 987: fp->f_msgcount--;
! 988: unp_rights--;
! 989: (void) closef(fp, NULL);
! 990: }
CVSweb