-/* kern_descrip.c 5.5 82/08/22 */
-
-#include "../h/param.h"
-#include "../h/systm.h"
-#include "../h/dir.h"
-#include "../h/user.h"
-#include "../h/inode.h"
-#include "../h/proc.h"
-#include "../h/conf.h"
-#include "../h/file.h"
-#include "../h/inline.h"
-#include "../h/socket.h"
-#include "../h/socketvar.h"
-#include "../h/mount.h"
-
-#include "../h/descrip.h"
-
/*
- * Descriptor management.
+ * Copyright (c) 1982, 1986, 1989, 1991 Regents of the University of California.
+ * All rights reserved.
+ *
+ * %sccs.include.redist.c%
+ *
+ * @(#)kern_descrip.c 7.37 (Berkeley) %G%
*/
+#include "param.h"
+#include "systm.h"
+#include "filedesc.h"
+#include "kernel.h"
+#include "vnode.h"
+#include "proc.h"
+#include "file.h"
+#include "socket.h"
+#include "socketvar.h"
+#include "stat.h"
+#include "ioctl.h"
+#include "fcntl.h"
+#include "malloc.h"
+#include "syslog.h"
+#include "resourcevar.h"
+
/*
- * TODO:
- * getf should be renamed
- * ufalloc side effects are gross
+ * Descriptor management.
*/
+struct file *filehead; /* head of list of open files */
+int nfiles; /* actual number of open files */
/*
* System calls on descriptors.
*/
-dstd()
+struct getdtablesize_args {
+ int dummy;
+};
+/* ARGSUSED */
+getdtablesize(p, uap, retval)
+ struct proc *p;
+ struct getdtablesize_args *uap;
+ int *retval;
{
- u.u_r.r_val1 = NOFILE;
+ *retval = p->p_rlimit[RLIMIT_OFILE].rlim_cur;
+ return (0);
}
-dup()
+/*
+ * Duplicate a file descriptor.
+ */
+struct dup_args {
+ int i;
+};
+/* ARGSUSED */
+dup(p, uap, retval)
+ struct proc *p;
+ struct dup_args *uap;
+ int *retval;
{
- register struct a {
- int i;
- } *uap = (struct a *) u.u_ap;
+ register struct filedesc *fdp = p->p_fd;
struct file *fp;
- int j;
-
- if (uap->i &~ 077) { uap->i &= 077; dup2(); return; } /* XXX */
-
- fp = getf(uap->i);
- if (fp == 0)
- return;
- j = ufalloc();
- if (j < 0)
- return;
- dupit(j, fp, u.u_pofile[uap->i] & (RDLOCK|WRLOCK));
+ int fd, error;
+
+ /*
+ * XXX Compatibility
+ */
+ if (uap->i &~ 077) { uap->i &= 077; return (dup2(p, uap, retval)); }
+
+ if ((unsigned)uap->i >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[uap->i]) == NULL)
+ return (EBADF);
+ if (error = fdalloc(p, 0, &fd))
+ return (error);
+ fdp->fd_ofiles[fd] = fp;
+ fdp->fd_ofileflags[fd] = fdp->fd_ofileflags[uap->i] &~ UF_EXCLOSE;
+ fp->f_count++;
+ if (fd > fdp->fd_lastfile)
+ fdp->fd_lastfile = fd;
+ *retval = fd;
+ return (0);
}
-dup2()
+/*
+ * Duplicate a file descriptor to a particular value.
+ */
+struct dup2_args {
+ u_int from;
+ u_int to;
+};
+/* ARGSUSED */
+dup2(p, uap, retval)
+ struct proc *p;
+ struct dup2_args *uap;
+ int *retval;
{
- register struct a {
- int i, j;
- } *uap = (struct a *) u.u_ap;
+ register struct filedesc *fdp = p->p_fd;
register struct file *fp;
-
- fp = getf(uap->i);
- if (fp == 0)
- return;
- if (uap->j < 0 || uap->j >= NOFILE) {
- u.u_error = EBADF;
- return;
- }
- u.u_r.r_val1 = uap->j;
- if (uap->i == uap->j)
- return;
- if (u.u_ofile[uap->j]) {
- closef(u.u_ofile[uap->j], 0, u.u_pofile[uap->j]);
- if (u.u_error)
- return;
- /* u.u_ofile[uap->j] = 0; */
- /* u.u_pofile[uap->j] = 0; */
+ register u_int old = uap->from, new = uap->to;
+ int i, error;
+
+ if (old >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[old]) == NULL ||
+ new >= p->p_rlimit[RLIMIT_OFILE].rlim_cur)
+ return (EBADF);
+ *retval = new;
+ if (old == new)
+ return (0);
+ if (new >= fdp->fd_nfiles) {
+ if (error = fdalloc(p, new, &i))
+ return (error);
+ if (new != i)
+ panic("dup2: fdalloc");
+ } else if (fdp->fd_ofiles[new]) {
+ if (fdp->fd_ofileflags[new] & UF_MAPPED)
+ (void) munmapfd(p, new);
+ /*
+ * dup2() must succeed even if the close has an error.
+ */
+ (void) closef(fdp->fd_ofiles[new], p);
}
- dupit(uap->j, fp, u.u_pofile[uap->i] & (RDLOCK|WRLOCK));
-}
-
-dupit(fd, fp, lockflags)
- int fd;
- register struct file *fp;
- register int lockflags;
-{
-
- u.u_ofile[fd] = fp;
- u.u_pofile[fd] = lockflags;
+ fdp->fd_ofiles[new] = fp;
+ fdp->fd_ofileflags[new] = fdp->fd_ofileflags[old] &~ UF_EXCLOSE;
fp->f_count++;
- if (lockflags&RDLOCK)
- fp->f_inode->i_rdlockc++;
- if (lockflags&WRLOCK)
- fp->f_inode->i_wrlockc++;
+ if (new > fdp->fd_lastfile)
+ fdp->fd_lastfile = new;
+ return (0);
}
-close()
+/*
+ * The file control system call.
+ */
+struct fcntl_args {
+ int fd;
+ int cmd;
+ int arg;
+};
+/* ARGSUSED */
+fcntl(p, uap, retval)
+ struct proc *p;
+ register struct fcntl_args *uap;
+ int *retval;
{
- register struct a {
- int i;
- } *uap = (struct a *)u.u_ap;
+ register struct filedesc *fdp = p->p_fd;
register struct file *fp;
+ register char *pop;
+ struct vnode *vp;
+ int i, tmp, error, flg = F_POSIX;
+ struct flock fl;
+
+ if ((unsigned)uap->fd >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[uap->fd]) == NULL)
+ return (EBADF);
+ pop = &fdp->fd_ofileflags[uap->fd];
+ switch(uap->cmd) {
+ case F_DUPFD:
+ if ((unsigned)uap->arg >= p->p_rlimit[RLIMIT_OFILE].rlim_cur)
+ return (EINVAL);
+ if (error = fdalloc(p, uap->arg, &i))
+ return (error);
+ fdp->fd_ofiles[i] = fp;
+ fdp->fd_ofileflags[i] = *pop &~ UF_EXCLOSE;
+ fp->f_count++;
+ if (i > fdp->fd_lastfile)
+ fdp->fd_lastfile = i;
+ *retval = i;
+ return (0);
+
+ case F_GETFD:
+ *retval = *pop & 1;
+ return (0);
+
+ case F_SETFD:
+ *pop = (*pop &~ 1) | (uap->arg & 1);
+ return (0);
+
+ case F_GETFL:
+ *retval = OFLAGS(fp->f_flag);
+ return (0);
+
+ case F_SETFL:
+ fp->f_flag &= ~FCNTLFLAGS;
+ fp->f_flag |= FFLAGS(uap->arg) & FCNTLFLAGS;
+ tmp = fp->f_flag & FNONBLOCK;
+ error = (*fp->f_ops->fo_ioctl)(fp, FIONBIO, (caddr_t)&tmp, p);
+ if (error)
+ return (error);
+ tmp = fp->f_flag & FASYNC;
+ error = (*fp->f_ops->fo_ioctl)(fp, FIOASYNC, (caddr_t)&tmp, p);
+ if (!error)
+ return (0);
+ fp->f_flag &= ~FNONBLOCK;
+ tmp = 0;
+ (void) (*fp->f_ops->fo_ioctl)(fp, FIONBIO, (caddr_t)&tmp, p);
+ return (error);
+
+ case F_GETOWN:
+ if (fp->f_type == DTYPE_SOCKET) {
+ *retval = ((struct socket *)fp->f_data)->so_pgid;
+ return (0);
+ }
+ error = (*fp->f_ops->fo_ioctl)
+ (fp, (int)TIOCGPGRP, (caddr_t)retval, p);
+ *retval = -*retval;
+ return (error);
+
+ case F_SETOWN:
+ if (fp->f_type == DTYPE_SOCKET) {
+ ((struct socket *)fp->f_data)->so_pgid = uap->arg;
+ return (0);
+ }
+ if (uap->arg <= 0) {
+ uap->arg = -uap->arg;
+ } else {
+ struct proc *p1 = pfind(uap->arg);
+ if (p1 == 0)
+ return (ESRCH);
+ uap->arg = p1->p_pgrp->pg_id;
+ }
+ return ((*fp->f_ops->fo_ioctl)
+ (fp, (int)TIOCSPGRP, (caddr_t)&uap->arg, p));
+
+ case F_SETLKW:
+ flg |= F_WAIT;
+ /* Fall into F_SETLK */
+
+ case F_SETLK:
+ if (fp->f_type != DTYPE_VNODE)
+ return (EBADF);
+ vp = (struct vnode *)fp->f_data;
+ /* Copy in the lock structure */
+ error = copyin((caddr_t)uap->arg, (caddr_t)&fl, sizeof (fl));
+ if (error)
+ return (error);
+ if (fl.l_whence == SEEK_CUR)
+ fl.l_start += fp->f_offset;
+ switch (fl.l_type) {
+
+ case F_RDLCK:
+ if ((fp->f_flag & FREAD) == 0)
+ return (EBADF);
+ p->p_flag |= SADVLCK;
+ return (VOP_ADVLOCK(vp, (caddr_t)p, F_SETLK, &fl, flg));
+
+ case F_WRLCK:
+ if ((fp->f_flag & FWRITE) == 0)
+ return (EBADF);
+ p->p_flag |= SADVLCK;
+ return (VOP_ADVLOCK(vp, (caddr_t)p, F_SETLK, &fl, flg));
+
+ case F_UNLCK:
+ return (VOP_ADVLOCK(vp, (caddr_t)p, F_UNLCK, &fl,
+ F_POSIX));
+
+ default:
+ return (EINVAL);
+ }
- fp = getf(uap->i);
- if (fp == 0)
- return;
- closef(fp, 0, u.u_pofile[uap->i]);
- /* WHAT IF u.u_error ? */
- u.u_ofile[uap->i] = NULL;
- u.u_pofile[uap->i] = 0;
-}
-
-dtype()
-{
- register struct a {
- int d;
- struct dtype *dtypeb;
- } *uap = (struct a *)u.u_ap;
- register struct file *fp;
- struct dtype adtype;
+ case F_GETLK:
+ if (fp->f_type != DTYPE_VNODE)
+ return (EBADF);
+ vp = (struct vnode *)fp->f_data;
+ /* Copy in the lock structure */
+ error = copyin((caddr_t)uap->arg, (caddr_t)&fl, sizeof (fl));
+ if (error)
+ return (error);
+ if (fl.l_whence == SEEK_CUR)
+ fl.l_start += fp->f_offset;
+ if (error = VOP_ADVLOCK(vp, (caddr_t)p, F_GETLK, &fl, F_POSIX))
+ return (error);
+ return (copyout((caddr_t)&fl, (caddr_t)uap->arg, sizeof (fl)));
- fp = getf(uap->d);
- if (fp == 0)
- return;
- adtype.dt_type = 0; /* XXX */
- adtype.dt_protocol = 0; /* XXX */
- if (copyout((caddr_t)&adtype, (caddr_t)uap->dtypeb,
- sizeof (struct dtype)) < 0) {
- u.u_error = EFAULT;
- return;
+ default:
+ return (EINVAL);
}
+ /* NOTREACHED */
}
-dwrap()
+/*
+ * Close a file descriptor.
+ */
+struct close_args {
+ int fd;
+};
+/* ARGSUSED */
+close(p, uap, retval)
+ struct proc *p;
+ struct close_args *uap;
+ int *retval;
{
- register struct a {
- int d;
- struct dtype *dtypeb;
- } *uap = (struct a *)u.u_ap;
+ register struct filedesc *fdp = p->p_fd;
register struct file *fp;
- struct dtype adtype;
-
- fp = getf(uap->d);
- if (fp == 0)
- return;
- if (copyin((caddr_t)uap->dtypeb, (caddr_t)&adtype,
- sizeof (struct dtype)) < 0) {
- u.u_error = EFAULT;
- return;
- }
- /* DO WRAP */
+ register int fd = uap->fd;
+ register u_char *pf;
+
+ if ((unsigned)fd >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[fd]) == NULL)
+ return (EBADF);
+ pf = (u_char *)&fdp->fd_ofileflags[fd];
+ if (*pf & UF_MAPPED)
+ (void) munmapfd(p, fd);
+ fdp->fd_ofiles[fd] = NULL;
+ while (fdp->fd_lastfile > 0 && fdp->fd_ofiles[fdp->fd_lastfile] == NULL)
+ fdp->fd_lastfile--;
+ if (fd < fdp->fd_freefile)
+ fdp->fd_freefile = fd;
+ *pf = 0;
+ return (closef(fp, p));
}
-dselect()
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+/*
+ * Return status information about a file descriptor.
+ */
+struct ofstat_args {
+ int fd;
+ struct ostat *sb;
+};
+/* ARGSUSED */
+ofstat(p, uap, retval)
+ struct proc *p;
+ register struct ofstat_args *uap;
+ int *retval;
{
+ register struct filedesc *fdp = p->p_fd;
+ register struct file *fp;
+ struct stat ub;
+ struct ostat oub;
+ int error;
-}
-
-dnblock()
-{
-#ifdef notdef
- register struct a {
- int d;
- int how;
- } *uap = (struct a *)u.u_ap;
+ if ((unsigned)uap->fd >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[uap->fd]) == NULL)
+ return (EBADF);
+ switch (fp->f_type) {
- /* XXX */
-#endif
-}
+ case DTYPE_VNODE:
+ error = vn_stat((struct vnode *)fp->f_data, &ub, p);
+ break;
-dsignal()
-{
-#ifdef notdef
- register struct a {
- int d;
- int how;
- } *uap = (struct a *)u.u_ap;
+ case DTYPE_SOCKET:
+ error = soo_stat((struct socket *)fp->f_data, &ub);
+ break;
- /* XXX */
-#endif
+ default:
+ panic("ofstat");
+ /*NOTREACHED*/
+ }
+ cvtstat(&ub, &oub);
+ if (error == 0)
+ error = copyout((caddr_t)&oub, (caddr_t)uap->sb, sizeof (oub));
+ return (error);
}
+#endif /* COMPAT_43 || COMPAT_SUNOS */
-int nselcoll;
/*
- * Select system call.
+ * Return status information about a file descriptor.
*/
-oselect()
+struct fstat_args {
+ int fd;
+ struct stat *sb;
+};
+/* ARGSUSED */
+fstat(p, uap, retval)
+ struct proc *p;
+ register struct fstat_args *uap;
+ int *retval;
{
- register struct uap {
- int nfd;
- fd_set *rp, *wp;
- int timo;
- } *ap = (struct uap *)u.u_ap;
- fd_set rd, wr;
- int nfds = 0, readable = 0, writeable = 0;
- time_t t = time;
- int s, tsel, ncoll, rem;
-
- if (ap->nfd > NOFILE)
- ap->nfd = NOFILE;
- if (ap->nfd < 0) {
- u.u_error = EBADF;
- return;
- }
- if (ap->rp && copyin((caddr_t)ap->rp,(caddr_t)&rd,sizeof(fd_set)))
- return;
- if (ap->wp && copyin((caddr_t)ap->wp,(caddr_t)&wr,sizeof(fd_set)))
- return;
-retry:
- ncoll = nselcoll;
- u.u_procp->p_flag |= SSEL;
- if (ap->rp)
- readable = selscan(ap->nfd, rd, &nfds, FREAD);
- if (ap->wp)
- writeable = selscan(ap->nfd, wr, &nfds, FWRITE);
- if (u.u_error)
- goto done;
- if (readable || writeable)
- goto done;
- rem = (ap->timo+999)/1000 - (time - t);
- if (ap->timo == 0 || rem <= 0)
- goto done;
- s = spl6();
- if ((u.u_procp->p_flag & SSEL) == 0 || nselcoll != ncoll) {
- u.u_procp->p_flag &= ~SSEL;
- splx(s);
- goto retry;
- }
- u.u_procp->p_flag &= ~SSEL;
- tsel = tsleep((caddr_t)&selwait, PZERO+1, rem);
- splx(s);
- switch (tsel) {
+ register struct filedesc *fdp = p->p_fd;
+ register struct file *fp;
+ struct stat ub;
+ int error;
- case TS_OK:
- goto retry;
+ if ((unsigned)uap->fd >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[uap->fd]) == NULL)
+ return (EBADF);
+ switch (fp->f_type) {
- case TS_SIG:
- u.u_error = EINTR;
- return;
+ case DTYPE_VNODE:
+ error = vn_stat((struct vnode *)fp->f_data, &ub, p);
+ break;
- case TS_TIME:
+ case DTYPE_SOCKET:
+ error = soo_stat((struct socket *)fp->f_data, &ub);
break;
+
+ default:
+ panic("fstat");
+ /*NOTREACHED*/
}
-done:
- rd.fds_bits[0] = readable;
- wr.fds_bits[0] = writeable;
- s = sizeof (fd_set);
- if (s * NBBY > ap->nfd)
- s = (ap->nfd + NBBY - 1) / NBBY;
- u.u_r.r_val1 = nfds;
- if (ap->rp)
- (void) copyout((caddr_t)&rd, (caddr_t)ap->rp, sizeof(fd_set));
- if (ap->wp)
- (void) copyout((caddr_t)&wr, (caddr_t)ap->wp, sizeof(fd_set));
+ if (error == 0)
+ error = copyout((caddr_t)&ub, (caddr_t)uap->sb, sizeof (ub));
+ return (error);
}
-selscan(nfd, fds, nfdp, flag)
- int nfd;
- fd_set fds;
- int *nfdp, flag;
+/*
+ * Allocate a file descriptor for the process.
+ */
+int fdexpand;
+
+fdalloc(p, want, result)
+ struct proc *p;
+ int want;
+ int *result;
{
- struct file *fp;
- struct inode *ip;
- register int bits;
- int i, able, res = 0;
-
- bits = fds.fds_bits[0];
- while (i = ffs(bits)) {
- if (i > nfd)
- break;
- bits &= ~(1<<(i-1));
- fp = u.u_ofile[i-1];
- if (fp == NULL) {
- u.u_error = EBADF;
- return (0);
- }
- if (fp->f_type == DTYPE_SOCKET)
- able = soselect(fp->f_socket, flag);
- else {
- ip = fp->f_inode;
- switch (ip->i_mode & IFMT) {
-
- case IFCHR:
- able =
- (*cdevsw[major(ip->i_rdev)].d_select)
- (ip->i_rdev, flag);
- break;
-
- case IFBLK:
- case IFREG:
- case IFDIR:
- able = 1;
- break;
+ register struct filedesc *fdp = p->p_fd;
+ register int i;
+ int lim, last, nfiles;
+ struct file **newofile;
+ char *newofileflags;
+
+ /*
+ * Search for a free descriptor starting at the higher
+ * of want or fd_freefile. If that fails, consider
+ * expanding the ofile array.
+ */
+ lim = p->p_rlimit[RLIMIT_OFILE].rlim_cur;
+ for (;;) {
+ last = min(fdp->fd_nfiles, lim);
+ if ((i = want) < fdp->fd_freefile)
+ i = fdp->fd_freefile;
+ for (; i < last; i++) {
+ if (fdp->fd_ofiles[i] == NULL) {
+ fdp->fd_ofileflags[i] = 0;
+ if (i > fdp->fd_lastfile)
+ fdp->fd_lastfile = i;
+ if (want <= fdp->fd_freefile)
+ fdp->fd_freefile = i;
+ *result = i;
+ return (0);
}
-
- }
- if (able) {
- res |= (1<<(i-1));
- (*nfdp)++;
}
+
+ /*
+ * 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;
+ MALLOC(newofile, struct file **, 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);
+ fdp->fd_ofiles = newofile;
+ fdp->fd_ofileflags = newofileflags;
+ fdp->fd_nfiles = nfiles;
+ fdexpand++;
}
- return (res);
}
-/*ARGSUSED*/
-seltrue(dev, flag)
- dev_t dev;
- int flag;
+/*
+ * Check to see whether n user file descriptors
+ * are available to the process p.
+ */
+fdavail(p, n)
+ struct proc *p;
+ register int n;
{
-
- return (1);
+ register struct filedesc *fdp = p->p_fd;
+ register struct file **fpp;
+ register int i;
+
+ if ((i = p->p_rlimit[RLIMIT_OFILE].rlim_cur - fdp->fd_nfiles) > 0 &&
+ (n -= i) <= 0)
+ return (1);
+ fpp = &fdp->fd_ofiles[fdp->fd_freefile];
+ for (i = fdp->fd_nfiles - fdp->fd_freefile; --i >= 0; fpp++)
+ if (*fpp == NULL && --n <= 0)
+ return (1);
+ return (0);
}
-selwakeup(p, coll)
+/*
+ * Create a new open file structure and allocate
+ * a file decriptor for the process that refers to it.
+ */
+falloc(p, resultfp, resultfd)
register struct proc *p;
- int coll;
+ struct file **resultfp;
+ int *resultfd;
{
- int s;
-
- if (coll) {
- nselcoll++;
- wakeup((caddr_t)&selwait);
- }
- if (p) {
- if (p->p_wchan == (caddr_t)&selwait)
- setrun(p);
- else {
- s = spl6();
- if (p->p_flag & SSEL)
- p->p_flag &= ~SSEL;
- splx(s);
- }
+ register struct file *fp, *fq, **fpp;
+ int error, i;
+
+ if (error = fdalloc(p, 0, &i))
+ return (error);
+ if (nfiles >= maxfiles) {
+ tablefull("file");
+ return (ENFILE);
}
+ /*
+ * Allocate a new file descriptor.
+ * If the process has file descriptor zero open, add to the list
+ * of open files at that point, otherwise put it at the front of
+ * the list of open files.
+ */
+ nfiles++;
+ MALLOC(fp, struct file *, sizeof(struct file), M_FILE, M_WAITOK);
+ if (fq = p->p_fd->fd_ofiles[0])
+ fpp = &fq->f_filef;
+ else
+ fpp = &filehead;
+ p->p_fd->fd_ofiles[i] = fp;
+ if (fq = *fpp)
+ fq->f_fileb = &fp->f_filef;
+ fp->f_filef = fq;
+ fp->f_fileb = fpp;
+ *fpp = fp;
+ fp->f_count = 1;
+ fp->f_msgcount = 0;
+ fp->f_offset = 0;
+ fp->f_cred = p->p_ucred;
+ crhold(fp->f_cred);
+ if (resultfp)
+ *resultfp = fp;
+ if (resultfd)
+ *resultfd = i;
+ return (0);
}
/*
- * Allocate a user file descriptor.
+ * Free a file descriptor.
*/
-ufalloc()
+ffree(fp)
+ register struct file *fp;
{
- register i;
-
- for (i=0; i<NOFILE; i++)
- if (u.u_ofile[i] == NULL) {
- u.u_r.r_val1 = i;
- u.u_pofile[i] = 0;
- return (i);
- }
- u.u_error = EMFILE;
- return (-1);
+ register struct file *fq;
+
+ if (fq = fp->f_filef)
+ fq->f_fileb = fp->f_fileb;
+ *fp->f_fileb = fq;
+ crfree(fp->f_cred);
+#ifdef DIAGNOSTIC
+ fp->f_filef = NULL;
+ fp->f_fileb = NULL;
+ fp->f_count = 0;
+#endif
+ nfiles--;
+ FREE(fp, M_FILE);
}
-struct file *lastf;
/*
- * Allocate a user file descriptor
- * and a file structure.
- * Initialize the descriptor
- * to point at the file structure.
+ * Copy a filedesc structure.
*/
-struct file *
-falloc()
+struct filedesc *
+fdcopy(p)
+ struct proc *p;
{
- register struct file *fp;
- register i;
-
- i = ufalloc();
- if (i < 0)
- return (NULL);
- if (lastf == 0)
- lastf = file;
- for (fp = lastf; fp < fileNFILE; fp++)
- if (fp->f_count == 0)
- goto slot;
- for (fp = file; fp < lastf; fp++)
- if (fp->f_count == 0)
- goto slot;
- tablefull("file");
- u.u_error = ENFILE;
- return (NULL);
-slot:
- u.u_ofile[i] = fp;
- fp->f_count++;
- fp->f_offset = 0;
- fp->f_inode = 0;
- lastf = fp + 1;
- return (fp);
+ register struct filedesc *newfdp, *fdp = p->p_fd;
+ register struct file **fpp;
+ register int i;
+
+ MALLOC(newfdp, struct filedesc *, sizeof(struct filedesc0),
+ M_FILEDESC, M_WAITOK);
+ bcopy(fdp, newfdp, sizeof(struct filedesc));
+ VREF(newfdp->fd_cdir);
+ if (newfdp->fd_rdir)
+ VREF(newfdp->fd_rdir);
+ newfdp->fd_refcnt = 1;
+
+ /*
+ * If the number of open files fits in the internal arrays
+ * of the open file structure, use them, otherwise allocate
+ * additional memory for the number of descriptors currently
+ * in use.
+ */
+ if (newfdp->fd_lastfile < NDFILE) {
+ newfdp->fd_ofiles = ((struct filedesc0 *) newfdp)->fd_dfiles;
+ newfdp->fd_ofileflags =
+ ((struct filedesc0 *) newfdp)->fd_dfileflags;
+ i = NDFILE;
+ } else {
+ /*
+ * Compute the smallest multiple of NDEXTENT needed
+ * for the file descriptors currently in use,
+ * allowing the table to shrink.
+ */
+ i = newfdp->fd_nfiles;
+ while (i > 2 * NDEXTENT && i >= newfdp->fd_lastfile * 2)
+ i /= 2;
+ MALLOC(newfdp->fd_ofiles, struct file **, i * OFILESIZE,
+ M_FILEDESC, M_WAITOK);
+ newfdp->fd_ofileflags = (char *) &newfdp->fd_ofiles[i];
+ }
+ newfdp->fd_nfiles = i;
+ bcopy(fdp->fd_ofiles, newfdp->fd_ofiles, i * sizeof(struct file **));
+ bcopy(fdp->fd_ofileflags, newfdp->fd_ofileflags, i * sizeof(char));
+ fpp = newfdp->fd_ofiles;
+ for (i = newfdp->fd_lastfile; i-- >= 0; fpp++)
+ if (*fpp != NULL)
+ (*fpp)->f_count++;
+ return (newfdp);
}
+
/*
- * Convert a user supplied file descriptor into a pointer
- * to a file structure. Only task is to check range of the descriptor.
- * Critical paths should use the GETF macro, defined in inline.h.
+ * Release a filedesc structure.
*/
-struct file *
-getf(f)
- register int f;
+void
+fdfree(p)
+ struct proc *p;
{
- register struct file *fp;
+ register struct filedesc *fdp = p->p_fd;
+ struct file **fpp;
+ register int i;
- if ((unsigned)f >= NOFILE || (fp = u.u_ofile[f]) == NULL) {
- u.u_error = EBADF;
- return (NULL);
- }
- return (fp);
+ if (--fdp->fd_refcnt > 0)
+ return;
+ fpp = fdp->fd_ofiles;
+ for (i = fdp->fd_lastfile; i-- >= 0; fpp++)
+ if (*fpp)
+ (void) closef(*fpp, p);
+ if (fdp->fd_nfiles > NDFILE)
+ FREE(fdp->fd_ofiles, M_FILEDESC);
+ vrele(fdp->fd_cdir);
+ if (fdp->fd_rdir)
+ vrele(fdp->fd_rdir);
+ FREE(fdp, M_FILEDESC);
}
/*
* Internal form of close.
- * Decrement reference count on
- * file structure.
- * Also make sure the pipe protocol
- * does not constipate.
- *
- * Decrement reference count on the inode following
- * removal to the referencing file structure.
- * Call device handler on last close.
- * Nouser indicates that the user isn't available to present
- * errors to.
- *
- * Handling locking at this level is RIDICULOUS.
+ * Decrement reference count on file structure.
+ * Note: p may be NULL when closing a file
+ * that was being passed in a message.
*/
-closef(fp, nouser, flags)
+closef(fp, p)
register struct file *fp;
- int nouser, flags;
+ register struct proc *p;
{
- register struct inode *ip;
- register struct mount *mp;
- int flag, mode;
- dev_t dev;
- register int (*cfunc)();
+ struct vnode *vp;
+ struct flock lf;
+ int error;
if (fp == NULL)
- return;
- if (fp->f_count > 1) {
- fp->f_count--;
- return;
+ return (0);
+ /*
+ * POSIX record locking dictates that any close releases ALL
+ * locks owned by this process. This is handled by setting
+ * a flag in the unlock to free ONLY locks obeying POSIX
+ * semantics, and not to free BSD-style file locks.
+ * If the descriptor was in a message, POSIX-style locks
+ * aren't passed with the descriptor.
+ */
+ if (p && (p->p_flag & SADVLCK) && fp->f_type == DTYPE_VNODE) {
+ lf.l_whence = SEEK_SET;
+ lf.l_start = 0;
+ lf.l_len = 0;
+ lf.l_type = F_UNLCK;
+ vp = (struct vnode *)fp->f_data;
+ (void) VOP_ADVLOCK(vp, (caddr_t)p, F_UNLCK, &lf, F_POSIX);
}
- if (fp->f_type == DTYPE_SOCKET) {
- u.u_error = 0; /* XXX */
- soclose(fp->f_socket, nouser);
- if (nouser == 0 && u.u_error)
- return;
- fp->f_socket = 0;
- fp->f_count = 0;
- return;
+ if (--fp->f_count > 0)
+ return (0);
+ if (fp->f_count < 0)
+ panic("closef: count < 0");
+ if ((fp->f_flag & FHASLOCK) && fp->f_type == DTYPE_VNODE) {
+ lf.l_whence = SEEK_SET;
+ lf.l_start = 0;
+ lf.l_len = 0;
+ lf.l_type = F_UNLCK;
+ vp = (struct vnode *)fp->f_data;
+ (void) VOP_ADVLOCK(vp, (caddr_t)fp, F_UNLCK, &lf, F_FLOCK);
}
- flag = fp->f_flag;
- ip = fp->f_inode;
- dev = (dev_t)ip->i_rdev;
- mode = ip->i_mode & IFMT;
- flags &= RDLOCK|WRLOCK; /* conservative */
- if (flags)
- funlocki(ip, flags);
- ilock(ip);
- iput(ip);
- fp->f_count = 0;
+ error = (*fp->f_ops->fo_close)(fp, p);
+ ffree(fp);
+ return (error);
+}
- switch (mode) {
+/*
+ * Apply an advisory lock on a file descriptor.
+ *
+ * Just attempt to get a record lock of the requested type on
+ * the entire file (l_whence = SEEK_SET, l_start = 0, l_len = 0).
+ */
+struct flock_args {
+ int fd;
+ int how;
+};
+/* ARGSUSED */
+flock(p, uap, retval)
+ struct proc *p;
+ register struct flock_args *uap;
+ int *retval;
+{
+ register struct filedesc *fdp = p->p_fd;
+ register struct file *fp;
+ struct vnode *vp;
+ struct flock lf;
+ int error;
+
+ if ((unsigned)uap->fd >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[uap->fd]) == NULL)
+ return (EBADF);
+ if (fp->f_type != DTYPE_VNODE)
+ return (EOPNOTSUPP);
+ vp = (struct vnode *)fp->f_data;
+ lf.l_whence = SEEK_SET;
+ lf.l_start = 0;
+ lf.l_len = 0;
+ if (uap->how & LOCK_UN) {
+ lf.l_type = F_UNLCK;
+ fp->f_flag &= ~FHASLOCK;
+ return (VOP_ADVLOCK(vp, (caddr_t)fp, F_UNLCK, &lf, F_FLOCK));
+ }
+ if (uap->how & LOCK_EX)
+ lf.l_type = F_WRLCK;
+ else if (uap->how & LOCK_SH)
+ lf.l_type = F_RDLCK;
+ else
+ return (EBADF);
+ fp->f_flag |= FHASLOCK;
+ if (uap->how & LOCK_NB)
+ return (VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, F_FLOCK));
+ return (VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, F_FLOCK|F_WAIT));
+}
- case IFCHR:
- cfunc = cdevsw[major(dev)].d_close;
- break;
+/*
+ * File Descriptor pseudo-device driver (/dev/fd/).
+ *
+ * Opening minor device N dup()s the file (if any) connected to file
+ * descriptor N belonging to the calling process. Note that this driver
+ * consists of only the ``open()'' routine, because all subsequent
+ * references to this file will be direct to the other driver.
+ */
+/* ARGSUSED */
+fdopen(dev, mode, type, p)
+ dev_t dev;
+ int mode, type;
+ struct proc *p;
+{
- case IFBLK:
+ /*
+ * XXX Kludge: set curproc->p_dupfd to contain the value of the
+ * the file descriptor being sought for duplication. The error
+ * return ensures that the vnode for this device will be released
+ * by vn_open. Open will detect this special error and take the
+ * actions in dupfdopen below. Other callers of vn_open or VOP_OPEN
+ * will simply report the error.
+ */
+ p->p_dupfd = minor(dev);
+ return (ENODEV);
+}
+
+/*
+ * Duplicate the specified descriptor to a free descriptor.
+ */
+dupfdopen(fdp, indx, dfd, mode, error)
+ register struct filedesc *fdp;
+ register int indx, dfd;
+ int mode;
+ int error;
+{
+ register struct file *wfp;
+ struct file *fp;
+
+ /*
+ * If the to-be-dup'd fd number is greater than the allowed number
+ * of file descriptors, or the fd to be dup'd has already been
+ * closed, reject. Note, check for new == old is necessary as
+ * falloc could allocate an already closed to-be-dup'd descriptor
+ * as the new descriptor.
+ */
+ fp = fdp->fd_ofiles[indx];
+ if ((u_int)dfd >= fdp->fd_nfiles ||
+ (wfp = fdp->fd_ofiles[dfd]) == NULL || fp == wfp)
+ return (EBADF);
+
+ /*
+ * There are two cases of interest here.
+ *
+ * For ENODEV simply dup (dfd) to file descriptor
+ * (indx) and return.
+ *
+ * For ENXIO steal away the file structure from (dfd) and
+ * store it in (indx). (dfd) is effectively closed by
+ * this operation.
+ *
+ * Any other error code is just returned.
+ */
+ switch (error) {
+ case ENODEV:
/*
- * We don't want to really close the device if it is mounted
+ * Check that the mode the file is being opened for is a
+ * subset of the mode of the existing descriptor.
*/
- for (mp = mount; mp < &mount[NMOUNT]; mp++)
- if (mp->m_bufp != NULL && mp->m_dev == dev)
- return;
- cfunc = bdevsw[major(dev)].d_close;
- break;
-
- default:
- return;
- }
- for (fp = file; fp < fileNFILE; fp++) {
- if (fp->f_type == DTYPE_SOCKET) /* XXX */
- continue;
- if (fp->f_count && (ip = fp->f_inode) &&
- ip->i_rdev == dev && (ip->i_mode&IFMT) == mode)
- return;
- }
- if (mode == IFBLK) {
+ if (((mode & (FREAD|FWRITE)) | wfp->f_flag) != wfp->f_flag)
+ return (EACCES);
+ fdp->fd_ofiles[indx] = wfp;
+ fdp->fd_ofileflags[indx] = fdp->fd_ofileflags[dfd];
+ wfp->f_count++;
+ if (indx > fdp->fd_lastfile)
+ fdp->fd_lastfile = indx;
+ return (0);
+
+ case ENXIO:
/*
- * On last close of a block device (that isn't mounted)
- * we must invalidate any in core blocks
+ * Steal away the file pointer from dfd, and stuff it into indx.
*/
- bflush(dev);
- binval(dev);
+ fdp->fd_ofiles[indx] = fdp->fd_ofiles[dfd];
+ fdp->fd_ofiles[dfd] = NULL;
+ fdp->fd_ofileflags[indx] = fdp->fd_ofileflags[dfd];
+ fdp->fd_ofileflags[dfd] = 0;
+ /*
+ * Complete the clean up of the filedesc structure by
+ * recomputing the various hints.
+ */
+ if (indx > fdp->fd_lastfile)
+ fdp->fd_lastfile = indx;
+ else
+ while (fdp->fd_lastfile > 0 &&
+ fdp->fd_ofiles[fdp->fd_lastfile] == NULL)
+ fdp->fd_lastfile--;
+ if (dfd < fdp->fd_freefile)
+ fdp->fd_freefile = dfd;
+ return (0);
+
+ default:
+ return (error);
}
- (*cfunc)(dev, flag, fp);
+ /* NOTREACHED */
}