diff options
author | Artur Grabowski <art@cvs.openbsd.org> | 2001-10-26 10:39:32 +0000 |
---|---|---|
committer | Artur Grabowski <art@cvs.openbsd.org> | 2001-10-26 10:39:32 +0000 |
commit | 75c1c8b71842dc76405c489942667f39aa641b68 (patch) | |
tree | dce181a6c0891d4ce7b5ce4d1b140a1408c7d97d /sys/kern | |
parent | 54aadc9b1285ef7536f4365800867aa535f1460d (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.
Diffstat (limited to 'sys/kern')
-rw-r--r-- | sys/kern/kern_descrip.c | 243 | ||||
-rw-r--r-- | sys/kern/uipc_usrreq.c | 119 |
2 files changed, 247 insertions, 115 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 |