diff options
-rw-r--r-- | sys/kern/kern_descrip.c | 243 | ||||
-rw-r--r-- | sys/kern/uipc_usrreq.c | 119 | ||||
-rw-r--r-- | sys/sys/filedesc.h | 5 |
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 *)); |