summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/kern/kern_descrip.c243
-rw-r--r--sys/kern/uipc_usrreq.c119
-rw-r--r--sys/sys/filedesc.h5
3 files changed, 251 insertions, 116 deletions
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
index 571e24a4946..77d7d8fdc6a 100644
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_descrip.c,v 1.38 2001/10/07 23:12:06 art Exp $ */
+/* $OpenBSD: kern_descrip.c,v 1.39 2001/10/26 10:39:31 art Exp $ */
/* $NetBSD: kern_descrip.c,v 1.42 1996/03/30 22:24:38 christos Exp $ */
/*
@@ -205,10 +205,16 @@ sys_dup(p, v, retval)
int new;
int error;
+restart:
if ((u_int)old >= fdp->fd_nfiles || fdp->fd_ofiles[old] == NULL)
return (EBADF);
- if ((error = fdalloc(p, 0, &new)) != 0)
+ if ((error = fdalloc(p, 0, &new)) != 0) {
+ if (error == ENOSPC) {
+ fdexpand(p);
+ goto restart;
+ }
return (error);
+ }
return (finishdup(fdp, old, new, retval));
}
@@ -230,6 +236,7 @@ sys_dup2(p, v, retval)
int old = SCARG(uap, from), new = SCARG(uap, to);
int i, error;
+restart:
if ((u_int)old >= fdp->fd_nfiles || fdp->fd_ofiles[old] == NULL ||
(u_int)new >= p->p_rlimit[RLIMIT_NOFILE].rlim_cur ||
(u_int)new >= maxfiles)
@@ -239,12 +246,15 @@ sys_dup2(p, v, retval)
return (0);
}
if (new >= fdp->fd_nfiles) {
- if ((error = fdalloc(p, new, &i)) != 0)
+ if ((error = fdalloc(p, new, &i)) != 0) {
+ if (error == ENOSPC) {
+ fdexpand(p);
+ goto restart;
+ }
return (error);
+ }
if (new != i)
panic("dup2: fdalloc");
- } else {
- (void) fdrelease(p, new);
}
return (finishdup(fdp, old, new, retval));
}
@@ -272,6 +282,7 @@ sys_fcntl(p, v, retval)
struct flock fl;
int newmin;
+restart:
if ((u_int)fd >= fdp->fd_nfiles ||
(fp = fdp->fd_ofiles[fd]) == NULL)
return (EBADF);
@@ -282,8 +293,13 @@ sys_fcntl(p, v, retval)
if ((u_int)newmin >= p->p_rlimit[RLIMIT_NOFILE].rlim_cur ||
(u_int)newmin >= maxfiles)
return (EINVAL);
- if ((error = fdalloc(p, newmin, &i)) != 0)
+ if ((error = fdalloc(p, newmin, &i)) != 0) {
+ if (error == ENOSPC) {
+ fdexpand(p);
+ goto restart;
+ }
return (error);
+ }
return (finishdup(fdp, fd, i, retval));
case F_GETFD:
@@ -426,6 +442,8 @@ sys_fcntl(p, v, retval)
/*
* Common code for dup, dup2, and fcntl(F_DUPFD).
+ *
+ * XXX - should take a proc as an argument.
*/
int
finishdup(fdp, old, new, retval)
@@ -433,16 +451,26 @@ finishdup(fdp, old, new, retval)
register int old, new;
register_t *retval;
{
- register struct file *fp;
+ struct file *fp, *oldfp;
+
+ oldfp = fdp->fd_ofiles[new];
fp = fdp->fd_ofiles[old];
if (fp->f_count == LONG_MAX-2)
return (EDEADLK);
fdp->fd_ofiles[new] = fp;
- fdp->fd_ofileflags[new] = fdp->fd_ofileflags[old] &~ UF_EXCLOSE;
+ fdp->fd_ofileflags[new] = fdp->fd_ofileflags[old] & ~UF_EXCLOSE;
fp->f_count++;
- fd_used(fdp, new);
+ if (oldfp == NULL)
+ fd_used(fdp, new);
*retval = new;
+
+ if (oldfp != NULL) {
+ if (new < fdp->fd_knlistsize)
+ knote_fdclose(curproc, new);
+ closef(oldfp, curproc);
+ }
+
return (0);
}
@@ -574,110 +602,126 @@ sys_fpathconf(p, v, retval)
/*
* Allocate a file descriptor for the process.
*/
-int fdexpand;
-
int
fdalloc(p, want, result)
struct proc *p;
int want;
int *result;
{
- register struct filedesc *fdp = p->p_fd;
- register int i;
- int lim, last, nfiles;
- struct file **newofile;
- char *newofileflags;
- u_int *newhimap, *newlomap, new, off;
+ struct filedesc *fdp = p->p_fd;
+ int lim, last, i;
+ u_int new, off;
/*
* Search for a free descriptor starting at the higher
* of want or fd_freefile. If that fails, consider
* expanding the ofile array.
*/
+restart:
lim = min((int)p->p_rlimit[RLIMIT_NOFILE].rlim_cur, maxfiles);
- for (;;) {
- last = min(fdp->fd_nfiles, lim);
- if ((i = want) < fdp->fd_freefile)
- i = fdp->fd_freefile;
- off = i >> NDENTRYSHIFT;
- new = find_next_zero(fdp->fd_himap, off,
- (last + NDENTRIES - 1) >> NDENTRYSHIFT);
- if (new != -1) {
- i = find_next_zero(&fdp->fd_lomap[new],
- new > off ? 0 : i & NDENTRYMASK,
- NDENTRIES);
- if (i == -1) {
- /* free file descriptor in this block was
- * below want, try again with higher want.
- */
- want = (new + 1) << NDENTRYSHIFT;
- continue;
- }
- i += (new << NDENTRYSHIFT);
- if (i < last) {
- fd_used(fdp, i);
- if (want <= fdp->fd_freefile)
- fdp->fd_freefile = i;
- *result = i;
- return (0);
- }
+ last = min(fdp->fd_nfiles, lim);
+ if ((i = want) < fdp->fd_freefile)
+ i = fdp->fd_freefile;
+ off = i >> NDENTRYSHIFT;
+ new = find_next_zero(fdp->fd_himap, off,
+ (last + NDENTRIES - 1) >> NDENTRYSHIFT);
+ if (new != -1) {
+ i = find_next_zero(&fdp->fd_lomap[new],
+ new > off ? 0 : i & NDENTRYMASK,
+ NDENTRIES);
+ if (i == -1) {
+ /*
+ * Free file descriptor in this block was
+ * below want, try again with higher want.
+ */
+ want = (new + 1) << NDENTRYSHIFT;
+ goto restart;
+ }
+ i += (new << NDENTRYSHIFT);
+ if (i < last) {
+ fd_used(fdp, i);
+ if (want <= fdp->fd_freefile)
+ fdp->fd_freefile = i;
+ *result = i;
+ return (0);
}
+ }
+ if (fdp->fd_nfiles >= lim)
+ return (EMFILE);
- /*
- * No space in current array. Expand?
- */
- if (fdp->fd_nfiles >= lim)
- return (EMFILE);
- if (fdp->fd_nfiles < NDEXTENT)
- nfiles = NDEXTENT;
- else
- nfiles = 2 * fdp->fd_nfiles;
- nfiles = min(lim, nfiles);
- newofile = malloc(nfiles * OFILESIZE, M_FILEDESC, M_WAITOK);
- newofileflags = (char *) &newofile[nfiles];
+ return (ENOSPC);
+}
- /*
- * Copy the existing ofile and ofileflags arrays
- * and zero the new portion of each array.
- */
- bcopy(fdp->fd_ofiles, newofile,
- (i = sizeof(struct file *) * fdp->fd_nfiles));
- bzero((char *)newofile + i, nfiles * sizeof(struct file *) - i);
- bcopy(fdp->fd_ofileflags, newofileflags,
- (i = sizeof(char) * fdp->fd_nfiles));
- bzero(newofileflags + i, nfiles * sizeof(char) - i);
-
- if (fdp->fd_nfiles > NDFILE)
- free(fdp->fd_ofiles, M_FILEDESC);
-
- if (NDHISLOTS(nfiles) > NDHISLOTS(fdp->fd_nfiles)) {
- newhimap = malloc(NDHISLOTS(nfiles) * sizeof(u_int),
- M_FILEDESC, M_WAITOK);
- newlomap = malloc( NDLOSLOTS(nfiles) * sizeof(u_int),
- M_FILEDESC, M_WAITOK);
-
- bcopy(fdp->fd_himap, newhimap,
- (i = NDHISLOTS(fdp->fd_nfiles) * sizeof(u_int)));
- bzero((char *)newhimap + i,
- NDHISLOTS(nfiles) * sizeof(u_int) - i);
-
- bcopy(fdp->fd_lomap, newlomap,
- (i = NDLOSLOTS(fdp->fd_nfiles) * sizeof(u_int)));
- bzero((char *)newlomap + i,
- NDLOSLOTS(nfiles) * sizeof(u_int) - i);
-
- if (NDHISLOTS(fdp->fd_nfiles) > NDHISLOTS(NDFILE)) {
- free(fdp->fd_himap, M_FILEDESC);
- free(fdp->fd_lomap, M_FILEDESC);
- }
- fdp->fd_himap = newhimap;
- fdp->fd_lomap = newlomap;
+void
+fdexpand(p)
+ struct proc *p;
+{
+ struct filedesc *fdp = p->p_fd;
+ int nfiles, i;
+ struct file **newofile;
+ char *newofileflags;
+ u_int *newhimap, *newlomap;
+
+ /*
+ * If it's already expanding just wait for that expansion to finish
+ * and return and let the caller retry the operation.
+ */
+ if (lockmgr(&fdp->fd_lock, LK_EXCLUSIVE|LK_SLEEPFAIL, NULL, p))
+ return;
+
+ /*
+ * No space in current array.
+ */
+ if (fdp->fd_nfiles < NDEXTENT)
+ nfiles = NDEXTENT;
+ else
+ nfiles = 2 * fdp->fd_nfiles;
+
+ newofile = malloc(nfiles * OFILESIZE, M_FILEDESC, M_WAITOK);
+ newofileflags = (char *) &newofile[nfiles];
+
+ /*
+ * Copy the existing ofile and ofileflags arrays
+ * and zero the new portion of each array.
+ */
+ bcopy(fdp->fd_ofiles, newofile,
+ (i = sizeof(struct file *) * fdp->fd_nfiles));
+ bzero((char *)newofile + i, nfiles * sizeof(struct file *) - i);
+ bcopy(fdp->fd_ofileflags, newofileflags,
+ (i = sizeof(char) * fdp->fd_nfiles));
+ bzero(newofileflags + i, nfiles * sizeof(char) - i);
+
+ if (fdp->fd_nfiles > NDFILE)
+ free(fdp->fd_ofiles, M_FILEDESC);
+
+ if (NDHISLOTS(nfiles) > NDHISLOTS(fdp->fd_nfiles)) {
+ newhimap = malloc(NDHISLOTS(nfiles) * sizeof(u_int),
+ M_FILEDESC, M_WAITOK);
+ newlomap = malloc(NDLOSLOTS(nfiles) * sizeof(u_int),
+ M_FILEDESC, M_WAITOK);
+
+ bcopy(fdp->fd_himap, newhimap,
+ (i = NDHISLOTS(fdp->fd_nfiles) * sizeof(u_int)));
+ bzero((char *)newhimap + i,
+ NDHISLOTS(nfiles) * sizeof(u_int) - i);
+
+ bcopy(fdp->fd_lomap, newlomap,
+ (i = NDLOSLOTS(fdp->fd_nfiles) * sizeof(u_int)));
+ bzero((char *)newlomap + i,
+ NDLOSLOTS(nfiles) * sizeof(u_int) - i);
+
+ if (NDHISLOTS(fdp->fd_nfiles) > NDHISLOTS(NDFILE)) {
+ free(fdp->fd_himap, M_FILEDESC);
+ free(fdp->fd_lomap, M_FILEDESC);
}
- fdp->fd_ofiles = newofile;
- fdp->fd_ofileflags = newofileflags;
- fdp->fd_nfiles = nfiles;
- fdexpand++;
+ fdp->fd_himap = newhimap;
+ fdp->fd_lomap = newlomap;
}
+ fdp->fd_ofiles = newofile;
+ fdp->fd_ofileflags = newofileflags;
+ fdp->fd_nfiles = nfiles;
+
+ lockmgr(&fdp->fd_lock, LK_RELEASE, NULL, p);
}
/*
@@ -716,8 +760,14 @@ falloc(p, resultfp, resultfd)
register struct file *fp, *fq;
int error, i;
- if ((error = fdalloc(p, 0, &i)) != 0)
+restart:
+ if ((error = fdalloc(p, 0, &i)) != 0) {
+ if (error == ENOSPC) {
+ fdexpand(p);
+ goto restart;
+ }
return (error);
+ }
if (nfiles >= maxfiles) {
fd_unused(p->p_fd, i);
tablefull("file");
@@ -782,6 +832,7 @@ fdinit(p)
newfdp->fd_fd.fd_rdir = fdp->fd_rdir;
if (newfdp->fd_fd.fd_rdir)
VREF(newfdp->fd_fd.fd_rdir);
+ lockinit(&newfdp->fd_fd.fd_lock, PLOCK, "fdexpand", 0, 0);
/* Create the file descriptor table. */
newfdp->fd_fd.fd_refcnt = 1;
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
index 6d8c193fddb..bb11b3d6a0f 100644
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: uipc_usrreq.c,v 1.13 2001/06/26 19:56:52 dugsong Exp $ */
+/* $OpenBSD: uipc_usrreq.c,v 1.14 2001/10/26 10:39:31 art Exp $ */
/* $NetBSD: uipc_usrreq.c,v 1.18 1996/02/09 19:00:50 christos Exp $ */
/*
@@ -623,32 +623,113 @@ unp_externalize(rights)
struct mbuf *rights;
{
struct proc *p = curproc; /* XXX */
- register struct cmsghdr *cm = mtod(rights, struct cmsghdr *);
- struct file **rp = (struct file **)(cm + 1), *fp;
- int newfds = (cm->cmsg_len - sizeof(*cm)) / sizeof (int);
- int i, f, *ip;
+ struct cmsghdr *cm = mtod(rights, struct cmsghdr *);
+ int i, *fdp;
+ struct file **rp;
+ struct file *fp;
+ int nfds, error = 0;
+
+ nfds = (cm->cmsg_len - CMSG_ALIGN(sizeof(*cm))) /
+ sizeof(struct file *);
+ rp = (struct file **)CMSG_DATA(cm);
+
+ fdp = malloc(nfds * sizeof(int), M_TEMP, M_WAITOK);
- if (!fdavail(p, newfds)) {
- for (i = 0; i < newfds; i++) {
+#ifdef notyet
+ /* Make sure the recipient should be able to see the descriptors.. */
+ if (p->p_cwdi->cwdi_rdir != NULL) {
+ rp = (struct file **)CMSG_DATA(cm);
+ for (i = 0; i < nfds; i++) {
+ fp = *rp++;
+ /*
+ * If we are in a chroot'ed directory, and
+ * someone wants to pass us a directory, make
+ * sure it's inside the subtree we're allowed
+ * to access.
+ */
+ if (fp->f_type == DTYPE_VNODE) {
+ struct vnode *vp = (struct vnode *)fp->f_data;
+ if ((vp->v_type == VDIR) &&
+ !vn_isunder(vp, p->p_cwdi->cwdi_rdir, p)) {
+ error = EPERM;
+ break;
+ }
+ }
+ }
+ }
+#endif
+
+restart:
+ rp = (struct file **)CMSG_DATA(cm);
+ if (error != 0) {
+ for (i = 0; i < nfds; i++) {
fp = *rp;
- unp_discard(fp);
+ /*
+ * zero the pointer before calling unp_discard,
+ * since it may end up in unp_gc()..
+ */
*rp++ = 0;
+ unp_discard(fp);
}
- return (EMSGSIZE);
+ goto out;
}
- ip = (int *)rp;
- for (i = 0; i < newfds; i++) {
- if (fdalloc(p, 0, &f))
- panic("unp_externalize");
- bcopy(rp, &fp, sizeof fp);
- rp++;
- p->p_fd->fd_ofiles[f] = fp;
+
+ /*
+ * First loop -- allocate file descriptor table slots for the
+ * new descriptors.
+ */
+ for (i = 0; i < nfds; i++) {
+ fp = *rp++;
+ if ((error = fdalloc(p, 0, &fdp[i])) != 0) {
+ /*
+ * Back out what we've done so far.
+ */
+ for (--i; i >= 0; i--)
+ fdremove(p->p_fd, fdp[i]);
+
+ if (error == ENOSPC) {
+ fdexpand(p);
+ error = 0;
+ } else {
+ /*
+ * This is the error that has historically
+ * been returned, and some callers may
+ * expect it.
+ */
+ error = EMSGSIZE;
+ }
+ goto restart;
+ }
+
+ /*
+ * Make the slot reference the descriptor so that
+ * fdalloc() works properly.. We finalize it all
+ * in the loop below.
+ */
+ p->p_fd->fd_ofiles[fdp[i]] = fp;
+ }
+
+ /*
+ * Now that adding them has succeeded, update all of the
+ * descriptor passing state.
+ */
+ rp = (struct file **)CMSG_DATA(cm);
+ for (i = 0; i < nfds; i++) {
+ fp = *rp++;
fp->f_msgcount--;
unp_rights--;
- bcopy(&f, ip, sizeof f);
- ip++;
}
- return (0);
+
+ /*
+ * Copy temporary array to message and adjust length, in case of
+ * transition from large struct file pointers to ints.
+ */
+ memcpy(CMSG_DATA(cm), fdp, nfds * sizeof(int));
+ cm->cmsg_len = CMSG_LEN(nfds * sizeof(int));
+ rights->m_len = CMSG_SPACE(nfds * sizeof(int));
+ out:
+ free(fdp, M_TEMP);
+ return (error);
}
int
diff --git a/sys/sys/filedesc.h b/sys/sys/filedesc.h
index ba7b21b2f3a..dc576fce90c 100644
--- a/sys/sys/filedesc.h
+++ b/sys/sys/filedesc.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: filedesc.h,v 1.11 2001/10/07 22:42:07 art Exp $ */
+/* $OpenBSD: filedesc.h,v 1.12 2001/10/26 10:39:31 art Exp $ */
/* $NetBSD: filedesc.h,v 1.14 1996/04/09 20:55:28 cgd Exp $ */
/*
@@ -36,6 +36,7 @@
* @(#)filedesc.h 8.1 (Berkeley) 6/2/93
*/
+#include <sys/lock.h>
/*
* This structure is used for the management of descriptors. It may be
* shared by multiple processes.
@@ -71,6 +72,7 @@ struct filedesc {
int fd_freefile; /* approx. next free file */
u_short fd_cmask; /* mask for file creation */
u_short fd_refcnt; /* reference count */
+ struct lock fd_lock; /* lock for growing the structure */
int fd_knlistsize; /* size of knlist */
struct klist *fd_knlist; /* list of attached knotes */
@@ -116,6 +118,7 @@ void filedesc_init __P((void));
int dupfdopen __P((struct filedesc *fdp, int indx, int dfd, int mode,
int error));
int fdalloc __P((struct proc *p, int want, int *result));
+void fdexpand __P((struct proc *));
int fdavail __P((struct proc *p, int n));
int falloc __P((struct proc *p, struct file **resultfp, int *resultfd));
void ffree __P((struct file *));