summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/kern/uipc_usrreq.c142
1 files changed, 93 insertions, 49 deletions
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
index 56e17d9dbf3..619cc8fcc64 100644
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: uipc_usrreq.c,v 1.18 2002/02/08 13:53:28 art Exp $ */
+/* $OpenBSD: uipc_usrreq.c,v 1.19 2002/02/11 12:35:53 art Exp $ */
/* $NetBSD: uipc_usrreq.c,v 1.18 1996/02/09 19:00:50 christos Exp $ */
/*
@@ -660,8 +660,8 @@ unp_externalize(rights)
#endif
restart:
- rp = (struct file **)CMSG_DATA(cm);
if (error != 0) {
+ rp = ((struct file **)CMSG_DATA(cm));
for (i = 0; i < nfds; i++) {
fp = *rp;
/*
@@ -678,8 +678,10 @@ restart:
* First loop -- allocate file descriptor table slots for the
* new descriptors.
*/
+ rp = ((struct file **)CMSG_DATA(cm)) + nfds - 1;
for (i = 0; i < nfds; i++) {
- fp = *rp++;
+ bcopy(rp, &fp, sizeof(fp));
+ rp--;
if ((error = fdalloc(p, 0, &fdp[i])) != 0) {
/*
* Back out what we've done so far.
@@ -738,49 +740,75 @@ unp_internalize(control, p)
struct proc *p;
{
struct filedesc *fdp = p->p_fd;
- register struct cmsghdr *cm = mtod(control, struct cmsghdr *);
+ struct cmsghdr *cm = mtod(control, struct cmsghdr *);
struct file **rp, *fp;
- register int i;
+ int i, error;
struct mbuf *n = NULL;
- int oldfds, *ip, fd;
+ int nfds, *ip, fd, neededspace;
if (cm->cmsg_type != SCM_RIGHTS || cm->cmsg_level != SOL_SOCKET ||
cm->cmsg_len != control->m_len)
return (EINVAL);
- oldfds = (cm->cmsg_len - sizeof (*cm)) / sizeof (int);
- ip = (int *)(cm + 1);
- for (i = 0; i < oldfds; i++) {
- struct file *fp;
- fd = *ip++;
- if ((fp = fd_getfile(fdp, fd)) == NULL)
- return (EBADF);
- if (fp->f_count == LONG_MAX-2 ||
- fp->f_msgcount == LONG_MAX-2)
- return (EDEADLK);
+ nfds = (cm->cmsg_len - CMSG_ALIGN(sizeof(*cm))) / sizeof (int);
+
+ /* Make sure we have room for the struct file pointers */
+morespace:
+ neededspace = CMSG_SPACE(nfds * sizeof(struct file *)) -
+ control->m_len;
+ if (neededspace > M_TRAILINGSPACE(control)) {
+ /* if we already have a cluster, the message is just too big */
+ if (control->m_flags & M_EXT)
+ return (E2BIG);
+
+ /* allocate a cluster and try again */
+ MCLGET(control, M_WAIT);
+ if ((control->m_flags & M_EXT) == 0)
+ return (ENOBUFS); /* allocation failed */
+
+ /* copy the data to the cluster */
+ memcpy(mtod(control, char *), cm, cm->cmsg_len);
+ cm = mtod(control, struct cmsghdr *);
+ goto morespace;
}
- ip = (int *)(cm + 1);
- if (sizeof(int) != sizeof(struct file *)) {
- MGET(n, M_WAIT, MT_DATA);
- rp = (struct file **)mtod(n, caddr_t);
- } else
- rp = (struct file **)ip;
- for (i = 0; i < oldfds; i++) {
+
+ /* adjust message & mbuf to note amount of space actually used. */
+ cm->cmsg_len = CMSG_LEN(nfds * sizeof(struct file *));
+ control->m_len = CMSG_SPACE(nfds * sizeof(struct file *));
+
+ ip = ((int *)CMSG_DATA(cm));
+ rp = ((struct file **)CMSG_DATA(cm)) + nfds - 1;
+ for (i = 0; i < nfds; i++) {
bcopy(ip, &fd, sizeof fd);
ip++;
- fp = fd_getfile(fdp, fd);
- /* XXX - are ew really sure that this will never fail? */
+ if ((fp = fd_getfile(fdp, fd)) == NULL) {
+ error = EBADF;
+ goto fail;
+ }
+ if (fp->f_count == LONG_MAX-2 ||
+ fp->f_msgcount == LONG_MAX-2) {
+ error = EDEADLK;
+ goto fail;
+ }
bcopy(&fp, rp, sizeof fp);
- rp++;
+ rp--;
fp->f_count++;
fp->f_msgcount++;
unp_rights++;
}
- if (n) {
- m_adj(control, -(oldfds * sizeof(int)));
- n->m_len = oldfds * sizeof(struct file *);
- m_cat(control, n);
- }
return (0);
+fail:
+ /* Back out what we just did. */
+ for ( ; i > 0; i--) {
+ bcopy(rp, &fp, sizeof(fp));
+ rp++;
+ fp->f_count--;
+ fp->f_msgcount--;
+ unp_rights--;
+ }
+ if (n)
+ m_freem(n);
+
+ return (error);
}
int unp_defer, unp_gcing;
@@ -802,18 +830,19 @@ unp_gc()
fp->f_flag &= ~(FMARK|FDEFER);
do {
for (fp = filehead.lh_first; fp != 0; fp = fp->f_list.le_next) {
- if (fp->f_count == 0)
- continue;
if (fp->f_flag & FDEFER) {
fp->f_flag &= ~FDEFER;
unp_defer--;
} else {
+ if (fp->f_count == 0)
+ continue;
if (fp->f_flag & FMARK)
continue;
if (fp->f_count == fp->f_msgcount)
continue;
- fp->f_flag |= FMARK;
}
+ fp->f_flag |= FMARK;
+
if (fp->f_type != DTYPE_SOCKET ||
(so = (struct socket *)fp->f_data) == 0)
continue;
@@ -836,7 +865,7 @@ unp_gc()
goto restart;
}
#endif
- unp_scan(so->so_rcv.sb_mb, unp_mark);
+ unp_scan(so->so_rcv.sb_mb, unp_mark, 0);
}
} while (unp_defer);
/*
@@ -906,38 +935,43 @@ unp_dispose(m)
{
if (m)
- unp_scan(m, unp_discard);
+ unp_scan(m, unp_discard, 1);
}
void
-unp_scan(m0, op)
- register struct mbuf *m0;
+unp_scan(m0, op, discard)
+ struct mbuf *m0;
void (*op) __P((struct file *));
+ int discard;
{
- register struct mbuf *m;
+ struct mbuf *m;
struct file **rp, *fp;
- register struct cmsghdr *cm;
- register int i;
+ struct cmsghdr *cm;
+ int i;
int qfds;
while (m0) {
- for (m = m0; m; m = m->m_next)
+ 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 - sizeof *cm) / sizeof (int);
- rp = (struct file **)(cm + 1);
+ qfds = (cm->cmsg_len - CMSG_ALIGN(sizeof *cm))
+ / sizeof(struct file *);
+ rp = (struct file **)CMSG_DATA(cm);
for (i = 0; i < qfds; i++) {
- bcopy(rp, &fp, sizeof fp);
- rp++;
+ fp = *rp;
+ if (discard)
+ *rp = 0;
(*op)(fp);
+ rp++;
}
break; /* XXX, but saves time */
}
- m0 = m0->m_act;
+ }
+ m0 = m0->m_nextpkt;
}
}
@@ -948,8 +982,16 @@ unp_mark(fp)
if (fp->f_flag & FMARK)
return;
- unp_defer++;
- fp->f_flag |= (FMARK|FDEFER);
+
+ if (fp->f_flag & FDEFER)
+ return;
+
+ if (fp->f_type == DTYPE_SOCKET) {
+ unp_defer++;
+ fp->f_flag |= FDEFER;
+ } else {
+ fp->f_flag |= FMARK;
+ }
}
void
@@ -957,6 +999,8 @@ unp_discard(fp)
struct file *fp;
{
+ if (fp == NULL)
+ return;
FREF(fp);
fp->f_msgcount--;
unp_rights--;