summaryrefslogtreecommitdiffstats
path: root/sys/kern/uipc_usrreq.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/uipc_usrreq.c')
-rw-r--r--sys/kern/uipc_usrreq.c437
1 files changed, 265 insertions, 172 deletions
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
index 52c60cf..7480773 100644
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -90,7 +90,8 @@ static void unp_gc __P((void));
static void unp_scan __P((struct mbuf *, void (*)(struct file *)));
static void unp_mark __P((struct file *));
static void unp_discard __P((struct file *));
-static int unp_internalize __P((struct mbuf *, struct thread *));
+static void unp_freerights __P((struct file **, int));
+static int unp_internalize __P((struct mbuf **, struct thread *));
static int unp_listen __P((struct unpcb *, struct proc *));
static int
@@ -274,7 +275,7 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
goto release;
}
- if (control && (error = unp_internalize(control, td)))
+ if (control && (error = unp_internalize(&control, td)))
goto release;
switch (so->so_type) {
@@ -952,80 +953,127 @@ unp_drain()
}
#endif
+static void
+unp_freerights(rp, fdcount)
+ struct file **rp;
+ int fdcount;
+{
+ int i;
+ struct file *fp;
+
+ for (i = 0; i < fdcount; i++) {
+ fp = *rp;
+ /*
+ * zero the pointer before calling
+ * unp_discard since it may end up
+ * in unp_gc()..
+ */
+ *rp++ = 0;
+ unp_discard(fp);
+ }
+}
+
int
-unp_externalize(rights)
- struct mbuf *rights;
+unp_externalize(control, controlp)
+ struct mbuf *control, **controlp;
{
struct thread *td = curthread; /* XXX */
- register int i;
- register struct cmsghdr *cm = mtod(rights, struct cmsghdr *);
- register int *fdp;
- register struct file **rp;
- register struct file *fp;
- int newfds = (cm->cmsg_len - (CMSG_DATA(cm) - (u_char *)cm))
- / sizeof (struct file *);
+ struct cmsghdr *cm = mtod(control, struct cmsghdr *);
+ int i;
+ int *fdp;
+ struct file **rp;
+ struct file *fp;
+ void *data;
+ socklen_t clen = control->m_len, datalen;
+ int error, newfds;
int f;
+ u_int newlen;
- /*
- * if the new FD's will not fit, then we free them all
- */
- if (!fdavail(td, newfds)) {
- rp = (struct file **)CMSG_DATA(cm);
- for (i = 0; i < newfds; i++) {
- fp = *rp;
+ error = 0;
+ if (controlp != NULL) /* controlp == NULL => free control messages */
+ *controlp = NULL;
+
+ while (cm != NULL) {
+ if (sizeof(*cm) > clen || cm->cmsg_len > clen) {
+ error = EINVAL;
+ break;
+ }
+
+ data = CMSG_DATA(cm);
+ datalen = (caddr_t)cm + cm->cmsg_len - (caddr_t)data;
+
+ if (cm->cmsg_level == SOL_SOCKET
+ && cm->cmsg_type == SCM_RIGHTS) {
+ newfds = datalen / sizeof(struct file *);
+ rp = data;
+
+ /* If we're not outputting the discriptors free them. */
+ if (error || controlp == NULL) {
+ unp_freerights(rp, newfds);
+ goto next;
+ }
+ /* if the new FD's will not fit free them. */
+ if (!fdavail(td, newfds)) {
+ error = EMSGSIZE;
+ unp_freerights(rp, newfds);
+ goto next;
+ }
/*
- * zero the pointer before calling unp_discard,
- * since it may end up in unp_gc()..
+ * now change each pointer to an fd in the global
+ * table to an integer that is the index to the
+ * local fd table entry that we set up to point
+ * to the global one we are transferring.
*/
- *rp++ = 0;
- unp_discard(fp);
- }
- return (EMSGSIZE);
- }
- /*
- * now change each pointer to an fd in the global table to
- * an integer that is the index to the local fd table entry
- * that we set up to point to the global one we are transferring.
- * If sizeof (struct file *) is bigger than or equal to sizeof int,
- * then do it in forward order. In that case, an integer will
- * always come in the same place or before its corresponding
- * struct file pointer.
- * If sizeof (struct file *) is smaller than sizeof int, then
- * do it in reverse order.
- */
- if (sizeof (struct file *) >= sizeof (int)) {
- fdp = (int *)(cm + 1);
- rp = (struct file **)CMSG_DATA(cm);
- for (i = 0; i < newfds; i++) {
- if (fdalloc(td, 0, &f))
- panic("unp_externalize");
- fp = *rp++;
- td->td_proc->p_fd->fd_ofiles[f] = fp;
- fp->f_msgcount--;
- unp_rights--;
- *fdp++ = f;
+ newlen = newfds * sizeof(int);
+ *controlp = sbcreatecontrol(NULL, newlen,
+ SCM_RIGHTS, SOL_SOCKET);
+ if (*controlp == NULL) {
+ error = E2BIG;
+ unp_freerights(rp, newfds);
+ goto next;
+ }
+
+ fdp = (int *)
+ CMSG_DATA(mtod(*controlp, struct cmsghdr *));
+ for (i = 0; i < newfds; i++) {
+ if (fdalloc(td, 0, &f))
+ panic("unp_externalize fdalloc failed");
+ fp = *rp++;
+ td->td_proc->p_fd->fd_ofiles[f] = fp;
+ fp->f_msgcount--;
+ unp_rights--;
+ *fdp++ = f;
+ }
+ } else { /* We can just copy anything else across */
+ if (error || controlp == NULL)
+ goto next;
+ *controlp = sbcreatecontrol(NULL, datalen,
+ cm->cmsg_type, cm->cmsg_level);
+ if (*controlp == NULL) {
+ error = ENOBUFS;
+ goto next;
+ }
+ bcopy(data,
+ CMSG_DATA(mtod(*controlp, struct cmsghdr *)),
+ datalen);
}
- } else {
- fdp = (int *)(cm + 1) + newfds - 1;
- rp = (struct file **)CMSG_DATA(cm) + newfds - 1;
- for (i = 0; i < newfds; i++) {
- if (fdalloc(td, 0, &f))
- panic("unp_externalize");
- fp = *rp--;
- td->td_proc->p_fd->fd_ofiles[f] = fp;
- fp->f_msgcount--;
- unp_rights--;
- *fdp-- = f;
+
+ controlp = &(*controlp)->m_next;
+
+next:
+ if (CMSG_SPACE(datalen) < clen) {
+ clen -= CMSG_SPACE(datalen);
+ cm = (struct cmsghdr *)
+ ((caddr_t)cm + CMSG_SPACE(datalen));
+ } else {
+ clen = 0;
+ cm = NULL;
}
}
- /*
- * Adjust length, in case sizeof(struct file *) and sizeof(int)
- * differs.
- */
- cm->cmsg_len = CMSG_LEN(newfds * sizeof(int));
- rights->m_len = cm->cmsg_len;
- return (0);
+ m_freem(control);
+
+ return (error);
}
void
@@ -1043,109 +1091,134 @@ unp_init(void)
#endif
static int
-unp_internalize(control, td)
- struct mbuf *control;
+unp_internalize(controlp, td)
+ struct mbuf **controlp;
struct thread *td;
{
+ struct mbuf *control = *controlp;
struct proc *p = td->td_proc;
struct filedesc *fdescp = p->p_fd;
- register struct cmsghdr *cm = mtod(control, struct cmsghdr *);
- register struct file **rp;
- register struct file *fp;
- register int i, fd, *fdp;
- register struct cmsgcred *cmcred;
- int oldfds;
+ struct cmsghdr *cm = mtod(control, struct cmsghdr *);
+ struct cmsgcred *cmcred;
+ struct file **rp;
+ struct file *fp;
+ struct timeval *tv;
+ int i, fd, *fdp;
+ void *data;
+ socklen_t clen = control->m_len, datalen;
+ int error, oldfds;
u_int newlen;
- if ((cm->cmsg_type != SCM_RIGHTS && cm->cmsg_type != SCM_CREDS) ||
- cm->cmsg_level != SOL_SOCKET || cm->cmsg_len != control->m_len)
- return (EINVAL);
+ error = 0;
+ *controlp = NULL;
- /*
- * Fill in credential information.
- */
- if (cm->cmsg_type == SCM_CREDS) {
- cmcred = (struct cmsgcred *)(cm + 1);
- cmcred->cmcred_pid = p->p_pid;
- cmcred->cmcred_uid = p->p_ucred->cr_ruid;
- cmcred->cmcred_gid = p->p_ucred->cr_rgid;
- cmcred->cmcred_euid = p->p_ucred->cr_uid;
- cmcred->cmcred_ngroups = MIN(p->p_ucred->cr_ngroups,
+ while (cm != NULL) {
+ if (sizeof(*cm) > clen || cm->cmsg_level != SOL_SOCKET
+ || cm->cmsg_len > clen) {
+ error = EINVAL;
+ goto out;
+ }
+
+ data = CMSG_DATA(cm);
+ datalen = (caddr_t)cm + cm->cmsg_len - (caddr_t)data;
+
+ switch (cm->cmsg_type) {
+ /*
+ * Fill in credential information.
+ */
+ case SCM_CREDS:
+ *controlp = sbcreatecontrol(NULL, sizeof(*cmcred),
+ SCM_CREDS, SOL_SOCKET);
+ if (*controlp == NULL) {
+ error = ENOBUFS;
+ goto out;
+ }
+
+ cmcred = (struct cmsgcred *)
+ CMSG_DATA(mtod(*controlp, struct cmsghdr *));
+ cmcred->cmcred_pid = p->p_pid;
+ cmcred->cmcred_uid = p->p_ucred->cr_ruid;
+ cmcred->cmcred_gid = p->p_ucred->cr_rgid;
+ cmcred->cmcred_euid = p->p_ucred->cr_uid;
+ cmcred->cmcred_ngroups = MIN(p->p_ucred->cr_ngroups,
CMGROUP_MAX);
- for (i = 0; i < cmcred->cmcred_ngroups; i++)
- cmcred->cmcred_groups[i] = p->p_ucred->cr_groups[i];
- return(0);
- }
+ for (i = 0; i < cmcred->cmcred_ngroups; i++)
+ cmcred->cmcred_groups[i] =
+ p->p_ucred->cr_groups[i];
+ break;
- oldfds = (cm->cmsg_len - sizeof (*cm)) / sizeof (int);
- /*
- * check that all the FDs passed in refer to legal OPEN files
- * If not, reject the entire operation.
- */
- fdp = (int *)(cm + 1);
- for (i = 0; i < oldfds; i++) {
- fd = *fdp++;
- if ((unsigned)fd >= fdescp->fd_nfiles ||
- fdescp->fd_ofiles[fd] == NULL)
- return (EBADF);
- }
- /*
- * Now replace the integer FDs with pointers to
- * the associated global file table entry..
- * Allocate a bigger buffer as necessary. But if an cluster is not
- * enough, return E2BIG.
- */
- newlen = CMSG_LEN(oldfds * sizeof(struct file *));
- if (newlen > MCLBYTES)
- return (E2BIG);
- if (newlen - control->m_len > M_TRAILINGSPACE(control)) {
- if (control->m_flags & M_EXT)
- return (E2BIG);
- MCLGET(control, M_TRYWAIT);
- if ((control->m_flags & M_EXT) == 0)
- return (ENOBUFS);
-
- /* copy the data to the cluster */
- memcpy(mtod(control, char *), cm, cm->cmsg_len);
- cm = mtod(control, struct cmsghdr *);
- }
+ case SCM_RIGHTS:
+ oldfds = datalen / sizeof (int);
+ /*
+ * check that all the FDs passed in refer to legal files
+ * If not, reject the entire operation.
+ */
+ fdp = data;
+ for (i = 0; i < oldfds; i++) {
+ fd = *fdp++;
+ if ((unsigned)fd >= fdescp->fd_nfiles ||
+ fdescp->fd_ofiles[fd] == NULL) {
+ error = EBADF;
+ goto out;
+ }
+ }
+ /*
+ * Now replace the integer FDs with pointers to
+ * the associated global file table entry..
+ */
+ newlen = oldfds * sizeof(struct file *);
+ *controlp = sbcreatecontrol(NULL, newlen,
+ SCM_RIGHTS, SOL_SOCKET);
+ if (*controlp == NULL) {
+ error = E2BIG;
+ goto out;
+ }
- /*
- * Adjust length, in case sizeof(struct file *) and sizeof(int)
- * differs.
- */
- control->m_len = cm->cmsg_len = newlen;
+ fdp = data;
+ rp = (struct file **)
+ CMSG_DATA(mtod(*controlp, struct cmsghdr *));
+ for (i = 0; i < oldfds; i++) {
+ fp = fdescp->fd_ofiles[*fdp++];
+ *rp++ = fp;
+ fp->f_count++;
+ fp->f_msgcount++;
+ unp_rights++;
+ }
+ break;
- /*
- * Transform the file descriptors into struct file pointers.
- * If sizeof (struct file *) is bigger than or equal to sizeof int,
- * then do it in reverse order so that the int won't get until
- * we're done.
- * If sizeof (struct file *) is smaller than sizeof int, then
- * do it in forward order.
- */
- if (sizeof (struct file *) >= sizeof (int)) {
- fdp = (int *)(cm + 1) + oldfds - 1;
- rp = (struct file **)CMSG_DATA(cm) + oldfds - 1;
- for (i = 0; i < oldfds; i++) {
- fp = fdescp->fd_ofiles[*fdp--];
- *rp-- = fp;
- fp->f_count++;
- fp->f_msgcount++;
- unp_rights++;
+ case SCM_TIMESTAMP:
+ *controlp = sbcreatecontrol(NULL, sizeof(*tv),
+ SCM_TIMESTAMP, SOL_SOCKET);
+ if (*controlp == NULL) {
+ error = ENOBUFS;
+ goto out;
+ }
+ tv = (struct timeval *)
+ CMSG_DATA(mtod(*controlp, struct cmsghdr *));
+ microtime(tv);
+ break;
+
+ default:
+ error = EINVAL;
+ goto out;
}
- } else {
- fdp = (int *)(cm + 1);
- rp = (struct file **)CMSG_DATA(cm);
- for (i = 0; i < oldfds; i++) {
- fp = fdescp->fd_ofiles[*fdp++];
- *rp++ = fp;
- fp->f_count++;
- fp->f_msgcount++;
- unp_rights++;
+
+ controlp = &(*controlp)->m_next;
+
+ if (CMSG_SPACE(datalen) < clen) {
+ clen -= CMSG_SPACE(datalen);
+ cm = (struct cmsghdr *)
+ ((caddr_t)cm + CMSG_SPACE(datalen));
+ } else {
+ clen = 0;
+ cm = NULL;
}
}
- return (0);
+
+out:
+ m_freem(control);
+
+ return (error);
}
static int unp_defer, unp_gcing;
@@ -1343,28 +1416,48 @@ unp_scan(m0, op)
register struct mbuf *m0;
void (*op) __P((struct file *));
{
- register struct mbuf *m;
- register struct file **rp;
- register struct cmsghdr *cm;
- register int i;
+ struct mbuf *m;
+ struct file **rp;
+ struct cmsghdr *cm;
+ void *data;
+ int i;
+ socklen_t clen, datalen;
int qfds;
while (m0) {
- for (m = m0; m; m = m->m_next)
- if (m->m_type == MT_CONTROL &&
- m->m_len >= sizeof(*cm)) {
- cm = mtod(m, struct cmsghdr *);
- if (cm->cmsg_level != SOL_SOCKET ||
- cm->cmsg_type != SCM_RIGHTS)
- continue;
- qfds = (cm->cmsg_len -
- (CMSG_DATA(cm) - (u_char *)cm))
- / sizeof (struct file *);
- rp = (struct file **)CMSG_DATA(cm);
- for (i = 0; i < qfds; i++)
- (*op)(*rp++);
- break; /* XXX, but saves time */
+ for (m = m0; m; m = m->m_next) {
+ if (m->m_type == MT_CONTROL)
+ continue;
+
+ cm = mtod(m, struct cmsghdr *);
+ clen = m->m_len;
+
+ while (cm != NULL) {
+ if (sizeof(*cm) > clen || cm->cmsg_len > clen)
+ break;
+
+ data = CMSG_DATA(cm);
+ datalen = (caddr_t)cm + cm->cmsg_len
+ - (caddr_t)data;
+
+ if (cm->cmsg_level == SOL_SOCKET &&
+ cm->cmsg_type == SCM_RIGHTS) {
+ qfds = datalen / sizeof (struct file *);
+ rp = data;
+ for (i = 0; i < qfds; i++)
+ (*op)(*rp++);
+ }
+
+ if (CMSG_SPACE(datalen) < clen) {
+ clen -= CMSG_SPACE(datalen);
+ cm = (struct cmsghdr *)
+ ((caddr_t)cm + CMSG_SPACE(datalen));
+ } else {
+ clen = 0;
+ cm = NULL;
+ }
}
+ }
m0 = m0->m_act;
}
}
OpenPOWER on IntegriCloud