summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArtur Grabowski <art@cvs.openbsd.org>2001-10-26 10:39:32 +0000
committerArtur Grabowski <art@cvs.openbsd.org>2001-10-26 10:39:32 +0000
commit75c1c8b71842dc76405c489942667f39aa641b68 (patch)
treedce181a6c0891d4ce7b5ce4d1b140a1408c7d97d
parent54aadc9b1285ef7536f4365800867aa535f1460d (diff)
- split fdalloc into two functions. fdalloc now returns ENOSPC if it
would have to grow the table and fdexpand that grows the table. Since fdexpand can sleep we have to restart all operations if we have to call it. - dup2 changed so that finishdup frees the destination descriptor. - unp_externalize is a completly rewritten version from NetBSD. Changes mostly inspired by NetBSD.
-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 *));