#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/sysmsg.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/vnode.h>
#include <sys/proc.h>
#include <sys/nlookup.h>
#include <sys/stat.h>
#include <sys/filio.h>
#include <sys/fcntl.h>
#include <sys/unistd.h>
#include <sys/resourcevar.h>
#include <sys/event.h>
#include <sys/kern_syscall.h>
#include <sys/kcore.h>
#include <sys/kinfo.h>
#include <sys/un.h>
#include <sys/objcache.h>
#include <vm/vm.h>
#include <vm/vm_extern.h>
#include <sys/file2.h>
#include <sys/spinlock2.h>
static int fdalloc_locked(struct proc *p, struct filedesc *fdp,
int want, int *result);
static void fsetfd_locked(struct filedesc *fdp, struct file *fp, int fd);
static void fdreserve_locked (struct filedesc *fdp, int fd0, int incr);
static struct file *funsetfd_locked (struct filedesc *fdp, int fd);
static void ffree(struct file *fp);
static MALLOC_DEFINE(M_FILEDESC, "file desc", "Open file descriptor table");
static MALLOC_DEFINE(M_FILEDESC_TO_LEADER, "file desc to leader",
"file desc to leader structures");
static MALLOC_DEFINE_OBJ(M_FILE, sizeof(struct file),
"file", "Open file structure");
static MALLOC_DEFINE(M_SIGIO, "sigio", "sigio structures");
static struct krate krate_uidinfo = { .freq = 1 };
static d_open_t fdopen;
#define NUMFDESC 64
#define CDEV_MAJOR 22
static struct dev_ops fildesc_ops = {
{ "FD", 0, 0 },
.d_open = fdopen,
};
#ifndef NFILELIST_HEADS
#define NFILELIST_HEADS 257
#endif
struct filelist_head {
struct spinlock spin;
struct filelist list;
} __cachealign;
static struct filelist_head filelist_heads[NFILELIST_HEADS];
static int nfiles;
extern int cmask;
struct lwkt_token revoke_token = LWKT_TOKEN_INITIALIZER(revoke_token);
static __inline
void
fdfixup_locked(struct filedesc *fdp, int fd)
{
if (fd < fdp->fd_freefile) {
fdp->fd_freefile = fd;
}
while (fdp->fd_lastfile >= 0 &&
fdp->fd_files[fdp->fd_lastfile].fp == NULL &&
fdp->fd_files[fdp->fd_lastfile].reserved == 0
) {
--fdp->fd_lastfile;
}
}
static
void
fclearcache(struct fdnode *fdn, struct fdcache *match_fdc, int status)
{
struct fdcache *fdc;
struct file *fp;
int i;
fp = fdn->fp;
for (i = 0; i < NTDCACHEFD; ++i) {
if ((fdc = fdn->tdcache[i]) == NULL)
continue;
if (match_fdc) {
if (fdc != match_fdc)
continue;
fdn->tdcache[i] = NULL;
KASSERT(fp == fdc->fp,
("fclearcache(1): fp mismatch %p/%p\n",
fp, fdc->fp));
fdc->fp = NULL;
fdc->fd = -1;
if (status == 0)
atomic_add_int(&fp->f_count, -1);
fdn->isfull = 0;
return;
}
for (;;) {
status = atomic_swap_int(&fdc->locked, 1);
if (status == 1) {
cpu_pause();
continue;
}
fdn->tdcache[i] = NULL;
KASSERT(fp == fdc->fp,
("fclearcache(2): fp mismatch %p/%p\n",
fp, fdc->fp));
fdc->fp = NULL;
fdc->fd = -1;
if (status == 0)
atomic_add_int(&fp->f_count, -1);
fdn->isfull = 0;
atomic_swap_int(&fdc->locked, 0);
break;
}
}
KKASSERT(match_fdc == NULL);
}
struct file *
holdfp_fdp(struct filedesc *fdp, int fd, int flag)
{
struct file *fp;
spin_lock_shared(&fdp->fd_spin);
if (((u_int)fd) < fdp->fd_nfiles) {
fp = fdp->fd_files[fd].fp;
if (fp) {
if ((fp->f_flag & flag) == 0 && flag != -1) {
fp = NULL;
} else {
fhold(fp);
}
}
} else {
fp = NULL;
}
spin_unlock_shared(&fdp->fd_spin);
return fp;
}
struct file *
holdfp_fdp_locked(struct filedesc *fdp, int fd, int flag)
{
struct file *fp;
if (((u_int)fd) < fdp->fd_nfiles) {
fp = fdp->fd_files[fd].fp;
if (fp) {
if ((fp->f_flag & flag) == 0 && flag != -1) {
fp = NULL;
} else {
fhold(fp);
}
}
} else {
fp = NULL;
}
return fp;
}
static
struct file *
_holdfp_cache(thread_t td, int fd)
{
struct filedesc *fdp;
struct fdcache *fdc;
struct fdcache *best;
struct fdnode *fdn;
struct file *fp;
int status;
int delta;
int i;
for (fdc = &td->td_fdcache[0]; fdc < &td->td_fdcache[NFDCACHE]; ++fdc) {
if (fdc->fd != fd || fdc->fp == NULL)
continue;
status = atomic_swap_int(&fdc->locked, 1);
if (status == 1)
continue;
if (fdc->fd != fd || fdc->fp == NULL) {
atomic_swap_int(&fdc->locked, status);
continue;
}
if (status == 0) {
fp = fdc->fp;
fdc->lru = ++td->td_fdcache_lru;
atomic_swap_int(&fdc->locked, 2);
return fp;
}
if (status == 2) {
fp = fdc->fp;
fhold(fp);
fdc->lru = ++td->td_fdcache_lru;
atomic_swap_int(&fdc->locked, 2);
return fp;
}
KKASSERT(0);
}
fdp = td->td_proc->p_fd;
spin_lock_shared(&fdp->fd_spin);
if (((u_int)fd) < fdp->fd_nfiles) {
fp = fdp->fd_files[fd].fp;
if (fp) {
fhold(fp);
if (fdp->fd_files[fd].isfull == 0)
goto enter;
}
} else {
fp = NULL;
}
spin_unlock_shared(&fdp->fd_spin);
return fp;
enter:
best = &td->td_fdcache[0];
for (fdc = &td->td_fdcache[0]; fdc < &td->td_fdcache[NFDCACHE]; ++fdc) {
if (fdc->fp == NULL) {
best = fdc;
break;
}
delta = fdc->lru - best->lru;
if (delta < 0)
best = fdc;
}
status = atomic_swap_int(&best->locked, 1);
if (status == 1)
goto done;
if (best->fp) {
KKASSERT(best->fd >= 0);
fclearcache(&fdp->fd_files[best->fd], best, status);
}
best->fd = fd;
best->fp = fp;
best->lru = ++td->td_fdcache_lru;
best->locked = 2;
fdn = &fdp->fd_files[fd];
for (i = 0; i < NTDCACHEFD; ++i) {
if (fdn->tdcache[i] == NULL &&
atomic_cmpset_ptr((void **)&fdn->tdcache[i], NULL, best)) {
goto done;
}
}
fdn->isfull = 1;
best->fd = -1;
best->fp = NULL;
best->locked = 0;
done:
spin_unlock_shared(&fdp->fd_spin);
return fp;
}
static
struct file *
_holdfp2(thread_t td, int fd, char *fflagsp)
{
struct filedesc *fdp;
struct file *fp;
fdp = td->td_proc->p_fd;
spin_lock_shared(&fdp->fd_spin);
if (((u_int)fd) < fdp->fd_nfiles) {
fp = fdp->fd_files[fd].fp;
if (fp) {
*fflagsp = fdp->fd_files[fd].fileflags;
fhold(fp);
}
} else {
fp = NULL;
}
spin_unlock_shared(&fdp->fd_spin);
return fp;
}
void
dropfp(thread_t td, int fd, struct file *fp)
{
struct filedesc *fdp;
struct fdcache *fdc;
int status;
fdp = td->td_proc->p_fd;
for (fdc = &td->td_fdcache[0]; fdc < &td->td_fdcache[NFDCACHE]; ++fdc) {
if (fdc->fp != fp || fdc->fd != fd)
continue;
status = atomic_swap_int(&fdc->locked, 1);
switch(status) {
case 0:
atomic_swap_int(&fdc->locked, 0);
break;
case 1:
break;
case 2:
if (fdc->fp == fp && fdc->fd == fd) {
atomic_swap_int(&fdc->locked, 0);
return;
}
atomic_swap_int(&fdc->locked, 2);
break;
}
}
fdrop(fp);
}
void
fexitcache(thread_t td)
{
struct filedesc *fdp;
struct fdcache *fdc;
int status;
int i;
if (td->td_proc == NULL)
return;
fdp = td->td_proc->p_fd;
if (fdp == NULL)
return;
spin_lock_shared(&fdp->fd_spin);
for (i = 0; i < NFDCACHE; ++i) {
fdc = &td->td_fdcache[i];
if (fdc->fp) {
status = atomic_swap_int(&fdc->locked, 1);
if (status == 1) {
cpu_pause();
--i;
continue;
}
if (fdc->fp) {
KKASSERT(fdc->fd >= 0);
fclearcache(&fdp->fd_files[fdc->fd], fdc,
status);
}
atomic_swap_int(&fdc->locked, 0);
}
}
spin_unlock_shared(&fdp->fd_spin);
}
static __inline struct filelist_head *
fp2filelist(const struct file *fp)
{
u_int i;
i = (u_int)(uintptr_t)fp % NFILELIST_HEADS;
return &filelist_heads[i];
}
static __inline
struct plimit *
readplimits(struct proc *p)
{
thread_t td = curthread;
struct plimit *limit;
limit = td->td_limit;
if (limit != p->p_limit) {
spin_lock_shared(&p->p_spin);
limit = p->p_limit;
atomic_add_int(&limit->p_refcnt, 1);
spin_unlock_shared(&p->p_spin);
if (td->td_limit)
plimit_free(td->td_limit);
td->td_limit = limit;
}
return limit;
}
int
sys_getdtablesize(struct sysmsg *sysmsg, const struct getdtablesize_args *uap)
{
struct proc *p = curproc;
struct plimit *limit = readplimits(p);
int dtsize;
if (limit->pl_rlimit[RLIMIT_NOFILE].rlim_cur > INT_MAX)
dtsize = INT_MAX;
else
dtsize = (int)limit->pl_rlimit[RLIMIT_NOFILE].rlim_cur;
if (dtsize > maxfilesperproc)
dtsize = maxfilesperproc;
if (dtsize < minfilesperproc)
dtsize = minfilesperproc;
if (p->p_ucred->cr_uid && dtsize > maxfilesperuser)
dtsize = maxfilesperuser;
sysmsg->sysmsg_result = dtsize;
return (0);
}
int
sys_dup2(struct sysmsg *sysmsg, const struct dup2_args *uap)
{
int error;
int fd = 0;
error = kern_dup(DUP_FIXED, uap->from, uap->to, &fd);
sysmsg->sysmsg_fds[0] = fd;
return (error);
}
int
sys_dup(struct sysmsg *sysmsg, const struct dup_args *uap)
{
int error;
int fd = 0;
error = kern_dup(DUP_VARIABLE, uap->fd, 0, &fd);
sysmsg->sysmsg_fds[0] = fd;
return (error);
}
int
kern_fcntl(int fd, int cmd, union fcntl_dat *dat, struct ucred *cred)
{
struct thread *td = curthread;
struct proc *p = td->td_proc;
struct file *fp;
struct vnode *vp;
u_int newmin;
u_int oflags;
u_int nflags;
int closedcounter;
int tmp, error, flg = F_POSIX;
KKASSERT(p);
switch (cmd) {
case F_GETFD:
error = fgetfdflags(p->p_fd, fd, &tmp);
if (error == 0)
dat->fc_fdflags = ((tmp & UF_EXCLOSE) ? FD_CLOEXEC : 0) |
((tmp & UF_FOCLOSE) ? FD_CLOFORK : 0);
return (error);
case F_SETFD:
error = fsetfdflags(p->p_fd, fd,
((dat->fc_fdflags & FD_CLOEXEC) ? UF_EXCLOSE : 0) |
((dat->fc_fdflags & FD_CLOFORK) ? UF_FOCLOSE : 0));
return (error);
case F_DUPFD:
newmin = dat->fc_fd;
error = kern_dup(DUP_VARIABLE | DUP_FCNTL, fd, newmin,
&dat->fc_fd);
return (error);
case F_DUPFD_CLOEXEC:
newmin = dat->fc_fd;
error = kern_dup(DUP_VARIABLE | DUP_CLOEXEC | DUP_FCNTL,
fd, newmin, &dat->fc_fd);
return (error);
case F_DUPFD_CLOFORK:
newmin = dat->fc_fd;
error = kern_dup(DUP_VARIABLE | DUP_CLOFORK | DUP_FCNTL,
fd, newmin, &dat->fc_fd);
return (error);
case F_DUP2FD:
newmin = dat->fc_fd;
error = kern_dup(DUP_FIXED, fd, newmin, &dat->fc_fd);
return (error);
case F_DUP2FD_CLOEXEC:
newmin = dat->fc_fd;
error = kern_dup(DUP_FIXED | DUP_CLOEXEC, fd, newmin,
&dat->fc_fd);
return (error);
case F_DUP2FD_CLOFORK:
newmin = dat->fc_fd;
error = kern_dup(DUP_FIXED | DUP_CLOFORK, fd, newmin,
&dat->fc_fd);
return (error);
default:
if ((cmd & ((1u << F_DUP3FD_SHIFT) - 1)) != F_DUP3FD)
break;
nflags = (cmd >> F_DUP3FD_SHIFT);
if ((nflags & ~(FD_CLOEXEC | FD_CLOFORK)) != 0) {
error = EINVAL;
break;
}
newmin = dat->fc_fd;
error = kern_dup(DUP_FIXED |
((nflags & FD_CLOEXEC) != 0 ? DUP_CLOEXEC : 0) |
((nflags & FD_CLOFORK) != 0 ? DUP_CLOFORK : 0),
fd, newmin, &dat->fc_fd);
return (error);
}
closedcounter = p->p_fd->fd_closedcounter;
if ((fp = holdfp(td, fd, -1)) == NULL)
return (EBADF);
switch (cmd) {
case F_GETFL:
dat->fc_flags = OFLAGS(fp->f_flag);
error = 0;
break;
case F_SETFL:
oflags = fp->f_flag;
nflags = FFLAGS(dat->fc_flags & ~O_ACCMODE) & FCNTLFLAGS;
nflags |= oflags & ~FCNTLFLAGS;
error = 0;
if (((nflags ^ oflags) & O_APPEND) && (oflags & FAPPENDONLY))
error = EINVAL;
if (error == 0 && ((nflags ^ oflags) & FASYNC)) {
tmp = nflags & FASYNC;
error = fo_ioctl(fp, FIOASYNC, (caddr_t)&tmp,
cred, NULL);
}
while (error == 0) {
oflags = fp->f_flag;
cpu_ccfence();
nflags = (oflags & ~FCNTLFLAGS) | (nflags & FCNTLFLAGS);
if (atomic_cmpset_int(&fp->f_flag, oflags, nflags))
break;
cpu_pause();
}
break;
case F_GETOWN:
error = fo_ioctl(fp, FIOGETOWN, (caddr_t)&dat->fc_owner,
cred, NULL);
break;
case F_SETOWN:
error = fo_ioctl(fp, FIOSETOWN, (caddr_t)&dat->fc_owner,
cred, NULL);
break;
case F_SETLKW:
flg |= F_WAIT;
case F_SETLK:
if (fp->f_type != DTYPE_VNODE) {
error = EBADF;
break;
}
vp = (struct vnode *)fp->f_data;
if (dat->fc_flock.l_whence == SEEK_CUR)
dat->fc_flock.l_start += fp->f_offset;
switch (dat->fc_flock.l_type) {
case F_RDLCK:
if ((fp->f_flag & FREAD) == 0) {
error = EBADF;
break;
}
if (p->p_leader->p_advlock_flag == 0)
p->p_leader->p_advlock_flag = 1;
error = VOP_ADVLOCK(vp, (caddr_t)p->p_leader, F_SETLK,
&dat->fc_flock, flg);
break;
case F_WRLCK:
if ((fp->f_flag & FWRITE) == 0) {
error = EBADF;
break;
}
if (p->p_leader->p_advlock_flag == 0)
p->p_leader->p_advlock_flag = 1;
error = VOP_ADVLOCK(vp, (caddr_t)p->p_leader, F_SETLK,
&dat->fc_flock, flg);
break;
case F_UNLCK:
error = VOP_ADVLOCK(vp, (caddr_t)p->p_leader, F_UNLCK,
&dat->fc_flock, F_POSIX);
break;
default:
error = EINVAL;
break;
}
if (checkfdclosed(td, p->p_fd, fd, fp, closedcounter)) {
dat->fc_flock.l_whence = SEEK_SET;
dat->fc_flock.l_start = 0;
dat->fc_flock.l_len = 0;
dat->fc_flock.l_type = F_UNLCK;
VOP_ADVLOCK(vp, (caddr_t)p->p_leader,
F_UNLCK, &dat->fc_flock, F_POSIX);
}
break;
case F_GETLK:
if (fp->f_type != DTYPE_VNODE) {
error = EBADF;
break;
}
vp = (struct vnode *)fp->f_data;
if (dat->fc_flock.l_type != F_RDLCK &&
dat->fc_flock.l_type != F_WRLCK &&
dat->fc_flock.l_type != F_UNLCK) {
error = EINVAL;
break;
}
if (dat->fc_flock.l_whence == SEEK_CUR)
dat->fc_flock.l_start += fp->f_offset;
error = VOP_ADVLOCK(vp, (caddr_t)p->p_leader, F_GETLK,
&dat->fc_flock, F_POSIX);
break;
case F_GETPATH:
if (fp->f_type != DTYPE_VNODE) {
error = EBADF;
break;
}
error = cache_fullpath(p, &fp->f_nchandle, NULL,
&dat->fc_path.ptr, &dat->fc_path.buf, 1);
if (error == ENOMEM)
error = ERANGE;
break;
default:
error = EINVAL;
break;
}
fdrop(fp);
return (error);
}
int
sys_fcntl(struct sysmsg *sysmsg, const struct fcntl_args *uap)
{
union fcntl_dat dat;
int error;
switch (uap->cmd) {
case F_MAXFD: {
struct thread *td = curthread;
struct proc *p = td->td_proc;
struct filedesc *fdp;
KKASSERT(p);
fdp = p->p_fd;
sysmsg->sysmsg_result = fdp->fd_lastfile;
return (0);
}
case F_DUPFD:
case F_DUP2FD:
case F_DUPFD_CLOEXEC:
case F_DUP2FD_CLOEXEC:
case F_DUPFD_CLOFORK:
case F_DUP2FD_CLOFORK:
dat.fc_fd = uap->arg;
break;
case F_SETFD:
dat.fc_fdflags = uap->arg;
break;
case F_SETFL:
dat.fc_flags = uap->arg;
break;
case F_SETOWN:
dat.fc_owner = uap->arg;
break;
case F_SETLKW:
case F_SETLK:
case F_GETLK:
error = copyin((caddr_t)uap->arg, &dat.fc_flock,
sizeof(struct flock));
if (error)
return (error);
break;
default:
if ((uap->cmd & ((1u << F_DUP3FD_SHIFT) - 1)) == F_DUP3FD)
dat.fc_fd = uap->arg;
break;
}
error = kern_fcntl(uap->fd, uap->cmd, &dat, curthread->td_ucred);
if (error == 0) {
switch (uap->cmd) {
case F_DUPFD:
case F_DUP2FD:
case F_DUPFD_CLOEXEC:
case F_DUP2FD_CLOEXEC:
case F_DUPFD_CLOFORK:
case F_DUP2FD_CLOFORK:
sysmsg->sysmsg_result = dat.fc_fd;
break;
case F_GETFD:
sysmsg->sysmsg_result = dat.fc_fdflags;
break;
case F_GETFL:
sysmsg->sysmsg_result = dat.fc_flags;
break;
case F_GETOWN:
sysmsg->sysmsg_result = dat.fc_owner;
break;
case F_GETLK:
error = copyout(&dat.fc_flock, (caddr_t)uap->arg,
sizeof(struct flock));
break;
case F_GETPATH:
error = copyout(dat.fc_path.ptr, (caddr_t)uap->arg,
strlen(dat.fc_path.ptr) + 1);
kfree(dat.fc_path.buf, M_TEMP);
break;
default:
if ((uap->cmd & ((1u << F_DUP3FD_SHIFT) - 1)) == F_DUP3FD)
sysmsg->sysmsg_result = dat.fc_fd;
break;
}
}
return (error);
}
int
kern_dup(int flags, int old, int new, int *res)
{
struct thread *td = curthread;
struct proc *p = td->td_proc;
struct plimit *limit = readplimits(p);
struct filedesc *fdp = p->p_fd;
struct file *fp;
struct file *delfp;
int oldflags;
int holdleaders;
int dtsize;
int error, newfd;
retry:
if (limit->pl_rlimit[RLIMIT_NOFILE].rlim_cur > INT_MAX)
dtsize = INT_MAX;
else
dtsize = (int)limit->pl_rlimit[RLIMIT_NOFILE].rlim_cur;
if (dtsize > maxfilesperproc)
dtsize = maxfilesperproc;
if (dtsize < minfilesperproc)
dtsize = minfilesperproc;
if (new < 0 || new >= dtsize)
return (flags & DUP_FCNTL ? EINVAL : EBADF);
spin_lock(&fdp->fd_spin);
if ((unsigned)old >= fdp->fd_nfiles || fdp->fd_files[old].fp == NULL) {
spin_unlock(&fdp->fd_spin);
return (EBADF);
}
if ((flags & DUP_FIXED) && old == new) {
*res = new;
if (flags & DUP_CLOEXEC)
fdp->fd_files[new].fileflags |= UF_EXCLOSE;
if (flags & DUP_CLOFORK)
fdp->fd_files[new].fileflags |= UF_FOCLOSE;
spin_unlock(&fdp->fd_spin);
return (0);
}
fp = fdp->fd_files[old].fp;
oldflags = fdp->fd_files[old].fileflags;
fhold(fp);
if ((flags & DUP_VARIABLE) || new >= fdp->fd_nfiles) {
error = fdalloc_locked(p, fdp, new, &newfd);
if (error) {
spin_unlock(&fdp->fd_spin);
fdrop(fp);
return (error);
}
if (old >= fdp->fd_nfiles || fdp->fd_files[old].fp != fp) {
fsetfd_locked(fdp, NULL, newfd);
spin_unlock(&fdp->fd_spin);
fdrop(fp);
goto retry;
}
if ((flags & DUP_VARIABLE) == 0 && new != newfd) {
fsetfd_locked(fdp, NULL, newfd);
spin_unlock(&fdp->fd_spin);
fdrop(fp);
goto retry;
}
if (old == newfd) {
fsetfd_locked(fdp, NULL, newfd);
spin_unlock(&fdp->fd_spin);
fdrop(fp);
goto retry;
}
new = newfd;
delfp = NULL;
} else {
if (fdp->fd_files[new].reserved) {
spin_unlock(&fdp->fd_spin);
fdrop(fp);
kprintf("Warning: dup(): target descriptor %d is "
"reserved, waiting for it to be resolved\n",
new);
tsleep(fdp, 0, "fdres", hz);
goto retry;
}
++fdp->fd_closedcounter;
fclearcache(&fdp->fd_files[new], NULL, 0);
++fdp->fd_closedcounter;
delfp = fdp->fd_files[new].fp;
fdp->fd_files[new].fp = NULL;
fdp->fd_files[new].reserved = 1;
if (delfp == NULL) {
fdreserve_locked(fdp, new, 1);
if (new > fdp->fd_lastfile)
fdp->fd_lastfile = new;
}
}
if (delfp != NULL && p->p_fdtol != NULL) {
fdp->fd_holdleaderscount++;
holdleaders = 1;
} else {
holdleaders = 0;
}
KASSERT(delfp == NULL || (flags & DUP_FIXED),
("dup() picked an open file"));
fsetfd_locked(fdp, fp, new);
fdp->fd_files[new].fileflags = oldflags & ~(UF_EXCLOSE | UF_FOCLOSE);
if ((flags & DUP_CLOEXEC) != 0)
fdp->fd_files[new].fileflags |= UF_EXCLOSE;
if ((flags & DUP_CLOFORK) != 0)
fdp->fd_files[new].fileflags |= UF_FOCLOSE;
spin_unlock(&fdp->fd_spin);
fdrop(fp);
*res = new;
if (delfp) {
if (SLIST_FIRST(&delfp->f_klist))
knote_fdclose(delfp, fdp, new);
closef(delfp, p);
if (holdleaders) {
spin_lock(&fdp->fd_spin);
fdp->fd_holdleaderscount--;
if (fdp->fd_holdleaderscount == 0 &&
fdp->fd_holdleaderswakeup != 0) {
fdp->fd_holdleaderswakeup = 0;
spin_unlock(&fdp->fd_spin);
wakeup(&fdp->fd_holdleaderscount);
} else {
spin_unlock(&fdp->fd_spin);
}
}
}
return (0);
}
void
funsetown(struct sigio **sigiop)
{
struct pgrp *pgrp;
struct proc *p;
struct sigio *sigio;
if ((sigio = *sigiop) != NULL) {
lwkt_gettoken(&sigio_token);
KKASSERT(sigiop == sigio->sio_myref);
sigio = *sigiop;
*sigiop = NULL;
lwkt_reltoken(&sigio_token);
}
if (sigio == NULL)
return;
if (sigio->sio_pgid < 0) {
pgrp = sigio->sio_pgrp;
sigio->sio_pgrp = NULL;
lwkt_gettoken(&pgrp->pg_token);
SLIST_REMOVE(&pgrp->pg_sigiolst, sigio, sigio, sio_pgsigio);
lwkt_reltoken(&pgrp->pg_token);
pgrel(pgrp);
} else {
p = sigio->sio_proc;
sigio->sio_proc = NULL;
PHOLD(p);
lwkt_gettoken(&p->p_token);
SLIST_REMOVE(&p->p_sigiolst, sigio, sigio, sio_pgsigio);
lwkt_reltoken(&p->p_token);
PRELE(p);
}
crfree(sigio->sio_ucred);
sigio->sio_ucred = NULL;
kfree(sigio, M_SIGIO);
}
void
funsetownlst(struct sigiolst *sigiolst)
{
struct sigio *sigio;
while ((sigio = SLIST_FIRST(sigiolst)) != NULL)
funsetown(sigio->sio_myref);
}
int
fsetown(pid_t pgid, struct sigio **sigiop)
{
struct proc *proc = NULL;
struct pgrp *pgrp = NULL;
struct sigio *sigio;
int error;
if (pgid == 0) {
funsetown(sigiop);
return (0);
}
if (pgid > 0) {
proc = pfind(pgid);
if (proc == NULL) {
error = ESRCH;
goto done;
}
if (proc->p_session != curproc->p_session) {
error = EPERM;
goto done;
}
} else {
pgrp = pgfind(-pgid);
if (pgrp == NULL) {
error = ESRCH;
goto done;
}
if (pgrp->pg_session != curproc->p_session) {
error = EPERM;
goto done;
}
}
sigio = kmalloc(sizeof(struct sigio), M_SIGIO, M_WAITOK | M_ZERO);
if (pgid > 0) {
KKASSERT(pgrp == NULL);
lwkt_gettoken(&proc->p_token);
SLIST_INSERT_HEAD(&proc->p_sigiolst, sigio, sio_pgsigio);
sigio->sio_proc = proc;
lwkt_reltoken(&proc->p_token);
} else {
KKASSERT(proc == NULL);
lwkt_gettoken(&pgrp->pg_token);
SLIST_INSERT_HEAD(&pgrp->pg_sigiolst, sigio, sio_pgsigio);
sigio->sio_pgrp = pgrp;
lwkt_reltoken(&pgrp->pg_token);
pgrp = NULL;
}
sigio->sio_pgid = pgid;
sigio->sio_ucred = crhold(curthread->td_ucred);
sigio->sio_ruid = sigio->sio_ucred->cr_ruid;
sigio->sio_myref = sigiop;
lwkt_gettoken(&sigio_token);
while (*sigiop)
funsetown(sigiop);
*sigiop = sigio;
lwkt_reltoken(&sigio_token);
error = 0;
done:
if (pgrp)
pgrel(pgrp);
if (proc)
PRELE(proc);
return (error);
}
pid_t
fgetown(struct sigio **sigiop)
{
struct sigio *sigio;
pid_t own;
lwkt_gettoken_shared(&sigio_token);
sigio = *sigiop;
own = (sigio != NULL ? sigio->sio_pgid : 0);
lwkt_reltoken(&sigio_token);
return (own);
}
int
sys_closefrom(struct sysmsg *sysmsg, const struct closefrom_args *uap)
{
return(kern_closefrom(uap->fd));
}
int
kern_closefrom(int fd)
{
struct thread *td = curthread;
struct proc *p = td->td_proc;
struct filedesc *fdp;
int error;
int e2;
KKASSERT(p);
fdp = p->p_fd;
if (fd < 0)
return (EINVAL);
error = 0;
spin_lock(&fdp->fd_spin);
while (fd <= fdp->fd_lastfile) {
if (fdp->fd_files[fd].fp != NULL) {
spin_unlock(&fdp->fd_spin);
e2 = kern_close(fd);
if (e2 == EINTR)
error = EINTR;
spin_lock(&fdp->fd_spin);
}
++fd;
}
spin_unlock(&fdp->fd_spin);
return error;
}
int
sys_close(struct sysmsg *sysmsg, const struct close_args *uap)
{
return(kern_close(uap->fd));
}
int
kern_close(int fd)
{
struct thread *td = curthread;
struct proc *p = td->td_proc;
struct filedesc *fdp;
struct file *fp;
int error;
int holdleaders;
KKASSERT(p);
fdp = p->p_fd;
spin_lock(&fdp->fd_spin);
if ((fp = funsetfd_locked(fdp, fd)) == NULL) {
spin_unlock(&fdp->fd_spin);
return (EBADF);
}
holdleaders = 0;
if (p->p_fdtol != NULL) {
fdp->fd_holdleaderscount++;
holdleaders = 1;
}
spin_unlock(&fdp->fd_spin);
if (SLIST_FIRST(&fp->f_klist))
knote_fdclose(fp, fdp, fd);
error = closef(fp, p);
if (holdleaders) {
spin_lock(&fdp->fd_spin);
fdp->fd_holdleaderscount--;
if (fdp->fd_holdleaderscount == 0 &&
fdp->fd_holdleaderswakeup != 0) {
fdp->fd_holdleaderswakeup = 0;
spin_unlock(&fdp->fd_spin);
wakeup(&fdp->fd_holdleaderscount);
} else {
spin_unlock(&fdp->fd_spin);
}
}
return (error);
}
int
kern_shutdown(int fd, int how)
{
struct thread *td = curthread;
struct file *fp;
int error;
if ((fp = holdfp(td, fd, -1)) == NULL)
return (EBADF);
error = fo_shutdown(fp, how);
fdrop(fp);
return (error);
}
int
sys_shutdown(struct sysmsg *sysmsg, const struct shutdown_args *uap)
{
int error;
error = kern_shutdown(uap->s, uap->how);
return (error);
}
int
kern_fstat(int fd, struct stat *ub)
{
struct thread *td = curthread;
struct file *fp;
int error;
if ((fp = holdfp(td, fd, -1)) == NULL)
return (EBADF);
error = fo_stat(fp, ub, td->td_ucred);
fdrop(fp);
return (error);
}
int
sys_fstat(struct sysmsg *sysmsg, const struct fstat_args *uap)
{
struct stat st;
int error;
error = kern_fstat(uap->fd, &st);
if (error == 0)
error = copyout(&st, uap->sb, sizeof(st));
return (error);
}
int
sys_fpathconf(struct sysmsg *sysmsg, const struct fpathconf_args *uap)
{
struct thread *td = curthread;
struct file *fp;
struct vnode *vp;
int error = 0;
if ((fp = holdfp(td, uap->fd, -1)) == NULL)
return (EBADF);
switch (fp->f_type) {
case DTYPE_PIPE:
case DTYPE_SOCKET:
if (uap->name != _PC_PIPE_BUF) {
error = EINVAL;
} else {
sysmsg->sysmsg_result = PIPE_BUF;
error = 0;
}
break;
case DTYPE_FIFO:
case DTYPE_VNODE:
vp = (struct vnode *)fp->f_data;
error = VOP_PATHCONF(vp, uap->name, &sysmsg->sysmsg_reg);
break;
default:
error = EOPNOTSUPP;
break;
}
fdrop(fp);
return(error);
}
static void
fdgrow_locked(struct filedesc *fdp, int want)
{
struct fdnode *newfiles;
struct fdnode *oldfiles;
int nf, extra;
nf = fdp->fd_nfiles;
do {
nf = 2 * nf + 1;
} while (nf <= want);
spin_unlock(&fdp->fd_spin);
newfiles = kmalloc(nf * sizeof(struct fdnode), M_FILEDESC, M_WAITOK);
spin_lock(&fdp->fd_spin);
if (fdp->fd_nfiles >= nf) {
spin_unlock(&fdp->fd_spin);
kfree(newfiles, M_FILEDESC);
spin_lock(&fdp->fd_spin);
return;
}
extra = nf - fdp->fd_nfiles;
bcopy(fdp->fd_files, newfiles, fdp->fd_nfiles * sizeof(struct fdnode));
bzero(&newfiles[fdp->fd_nfiles], extra * sizeof(struct fdnode));
oldfiles = fdp->fd_files;
fdp->fd_files = newfiles;
fdp->fd_nfiles = nf;
if (oldfiles != fdp->fd_builtin_files) {
spin_unlock(&fdp->fd_spin);
kfree(oldfiles, M_FILEDESC);
spin_lock(&fdp->fd_spin);
}
}
static __inline int
right_subtree_size(int n)
{
return (n ^ (n | (n + 1)));
}
static __inline int
right_ancestor(int n)
{
return (n | (n + 1));
}
static __inline int
left_ancestor(int n)
{
return ((n & (n + 1)) - 1);
}
static
void
fdreserve_locked(struct filedesc *fdp, int fd, int incr)
{
while (fd >= 0) {
fdp->fd_files[fd].allocated += incr;
KKASSERT(fdp->fd_files[fd].allocated >= 0);
fd = left_ancestor(fd);
}
}
static
int
fdalloc_locked(struct proc *p, struct filedesc *fdp, int want, int *result)
{
struct plimit *limit = readplimits(p);
struct uidinfo *uip;
int fd, rsize, rsum, node, lim;
*result = -1;
if (limit->pl_rlimit[RLIMIT_NOFILE].rlim_cur > INT_MAX)
lim = INT_MAX;
else
lim = (int)limit->pl_rlimit[RLIMIT_NOFILE].rlim_cur;
if (lim > maxfilesperproc)
lim = maxfilesperproc;
if (lim < minfilesperproc)
lim = minfilesperproc;
if (want >= lim)
return (EINVAL);
if (p->p_ucred->cr_uid && fdp->fd_nfiles >= minfilesperproc) {
uip = p->p_ucred->cr_uidinfo;
if (uip->ui_openfiles > maxfilesperuser) {
int n;
int count;
count = 0;
for (n = 0; n < ncpus; ++n) {
count += atomic_swap_int(
&uip->ui_pcpu[n].pu_openfiles, 0);
}
atomic_add_int(&uip->ui_openfiles, count);
if (uip->ui_openfiles > maxfilesperuser) {
krateprintf(&krate_uidinfo,
"Warning: user %d pid %d (%s) "
"ran out of file descriptors "
"(%d/%d)\n",
p->p_ucred->cr_uid, (int)p->p_pid,
p->p_comm,
uip->ui_openfiles, maxfilesperuser);
return(ENFILE);
}
}
}
if (want >= fdp->fd_nfiles)
fdgrow_locked(fdp, want);
retry:
for (fd = max(want, fdp->fd_freefile); fd < min(fdp->fd_nfiles, lim);
fd = right_ancestor(fd)) {
if (fdp->fd_files[fd].allocated == 0)
goto found;
rsize = right_subtree_size(fd);
if (fdp->fd_files[fd].allocated == rsize)
continue;
for (rsum = 0, rsize /= 2; rsize > 0; rsize /= 2) {
node = fd + rsize;
rsum += fdp->fd_files[node].allocated;
if (fdp->fd_files[fd].allocated == rsum + rsize) {
fd = node;
if (fdp->fd_files[node].allocated == 0)
goto found;
rsum = 0;
}
}
goto found;
}
if (fdp->fd_nfiles >= lim) {
return (EMFILE);
}
fdgrow_locked(fdp, want);
goto retry;
found:
KKASSERT(fd < fdp->fd_nfiles);
if (fd > fdp->fd_lastfile)
fdp->fd_lastfile = fd;
if (want <= fdp->fd_freefile)
fdp->fd_freefile = fd;
*result = fd;
KKASSERT(fdp->fd_files[fd].fp == NULL);
KKASSERT(fdp->fd_files[fd].reserved == 0);
fdp->fd_files[fd].fileflags = 0;
fdp->fd_files[fd].reserved = 1;
fdreserve_locked(fdp, fd, 1);
return (0);
}
int
fdalloc(struct proc *p, int want, int *result)
{
struct filedesc *fdp = p->p_fd;
int error;
spin_lock(&fdp->fd_spin);
error = fdalloc_locked(p, fdp, want, result);
spin_unlock(&fdp->fd_spin);
return error;
}
int
fdavail(struct proc *p, int n)
{
struct plimit *limit = readplimits(p);
struct filedesc *fdp = p->p_fd;
struct fdnode *fdnode;
int i, lim, last;
if (limit->pl_rlimit[RLIMIT_NOFILE].rlim_cur > INT_MAX)
lim = INT_MAX;
else
lim = (int)limit->pl_rlimit[RLIMIT_NOFILE].rlim_cur;
if (lim > maxfilesperproc)
lim = maxfilesperproc;
if (lim < minfilesperproc)
lim = minfilesperproc;
spin_lock(&fdp->fd_spin);
if ((i = lim - fdp->fd_nfiles) > 0 && (n -= i) <= 0) {
spin_unlock(&fdp->fd_spin);
return (1);
}
last = min(fdp->fd_nfiles, lim);
fdnode = &fdp->fd_files[fdp->fd_freefile];
for (i = last - fdp->fd_freefile; --i >= 0; ++fdnode) {
if (fdnode->fp == NULL && --n <= 0) {
spin_unlock(&fdp->fd_spin);
return (1);
}
}
spin_unlock(&fdp->fd_spin);
return (0);
}
struct fdrevoke_info {
void *data;
short type;
short unused;
int found;
struct ucred *cred;
struct file *nfp;
};
static int fdrevoke_check_callback(struct file *fp, void *vinfo);
static int fdrevoke_proc_callback(struct proc *p, void *vinfo);
int
fdrevoke(void *f_data, short f_type, struct ucred *cred)
{
struct fdrevoke_info info;
int error;
bzero(&info, sizeof(info));
info.data = f_data;
info.type = f_type;
info.cred = cred;
error = falloc(NULL, &info.nfp, NULL);
if (error)
return (error);
lwkt_gettoken(&revoke_token);
allfiles_scan_exclusive(fdrevoke_check_callback, &info);
lwkt_reltoken(&revoke_token);
if (info.found)
allproc_scan(fdrevoke_proc_callback, &info, 0);
fdrop(info.nfp);
return(0);
}
static int
fdrevoke_check_callback(struct file *fp, void *vinfo)
{
struct fdrevoke_info *info = vinfo;
if (fp->f_flag & FREVOKED)
return(0);
if (info->cred->cr_prison &&
(fp->f_cred == NULL ||
info->cred->cr_prison != fp->f_cred->cr_prison)) {
return(0);
}
if (info->data == fp->f_data && info->type == fp->f_type) {
atomic_set_int(&fp->f_flag, FREVOKED);
info->found = 1;
}
return(0);
}
static int
fdrevoke_proc_callback(struct proc *p, void *vinfo)
{
struct fdrevoke_info *info = vinfo;
struct filedesc *fdp;
struct file *fp;
int n;
if (p->p_stat == SIDL || p->p_stat == SZOMB)
return(0);
if (info->cred->cr_prison &&
info->cred->cr_prison != p->p_ucred->cr_prison) {
return(0);
}
if (p->p_session && info->type == DTYPE_VNODE &&
info->data == p->p_session->s_ttyvp) {
p->p_session->s_ttyvp = NULL;
vrele(info->data);
}
spin_lock(&p->p_spin);
if ((fdp = p->p_fd) == NULL) {
spin_unlock(&p->p_spin);
return(0);
}
atomic_add_int(&fdp->fd_softrefs, 1);
spin_unlock(&p->p_spin);
spin_lock(&fdp->fd_spin);
for (n = 0; n < fdp->fd_nfiles; ++n) {
if ((fp = fdp->fd_files[n].fp) == NULL)
continue;
if (fp->f_flag & FREVOKED) {
++fdp->fd_closedcounter;
fclearcache(&fdp->fd_files[n], NULL, 0);
++fdp->fd_closedcounter;
fhold(info->nfp);
fdp->fd_files[n].fp = info->nfp;
spin_unlock(&fdp->fd_spin);
knote_fdclose(fp, fdp, n);
closef(fp, p);
spin_lock(&fdp->fd_spin);
}
}
spin_unlock(&fdp->fd_spin);
atomic_subtract_int(&fdp->fd_softrefs, 1);
return(0);
}
int
falloc(struct lwp *lp, struct file **resultfp, int *resultfd)
{
static struct timeval lastfail;
static int curfail;
struct filelist_head *head;
struct file *fp;
struct ucred *cred = lp ? lp->lwp_thread->td_ucred : proc0.p_ucred;
int error;
fp = NULL;
if (nfiles >= maxfiles - maxfilesrootres &&
(cred->cr_ruid != 0 || nfiles >= maxfiles)) {
if (ppsratecheck(&lastfail, &curfail, 1)) {
kprintf("kern.maxfiles limit exceeded by uid %d, "
"please see tuning(7).\n",
cred->cr_ruid);
}
error = ENFILE;
goto done;
}
fp = kmalloc_obj(sizeof(*fp), M_FILE, M_WAITOK|M_ZERO);
spin_init(&fp->f_spin, "falloc");
SLIST_INIT(&fp->f_klist);
fp->f_count = 1;
fp->f_ops = &badfileops;
fp->f_seqcount = 1;
fsetcred(fp, cred);
atomic_add_int(&nfiles, 1);
head = fp2filelist(fp);
spin_lock(&head->spin);
LIST_INSERT_HEAD(&head->list, fp, f_list);
spin_unlock(&head->spin);
if (resultfd) {
if ((error = fdalloc(lp->lwp_proc, 0, resultfd)) != 0) {
fdrop(fp);
fp = NULL;
}
} else {
error = 0;
}
done:
*resultfp = fp;
return (error);
}
int
checkfdclosed(thread_t td, struct filedesc *fdp, int fd, struct file *fp,
int closedcounter)
{
struct fdcache *fdc;
int error;
cpu_lfence();
if (fdp->fd_closedcounter == closedcounter)
return 0;
if (td->td_proc && td->td_proc->p_fd == fdp) {
for (fdc = &td->td_fdcache[0];
fdc < &td->td_fdcache[NFDCACHE]; ++fdc) {
if (fdc->fd == fd && fdc->fp == fp)
return 0;
}
}
spin_lock_shared(&fdp->fd_spin);
if ((unsigned)fd >= fdp->fd_nfiles || fp != fdp->fd_files[fd].fp)
error = EBADF;
else
error = 0;
spin_unlock_shared(&fdp->fd_spin);
return (error);
}
static void
fsetfd_locked(struct filedesc *fdp, struct file *fp, int fd)
{
KKASSERT((unsigned)fd < fdp->fd_nfiles);
KKASSERT(fdp->fd_files[fd].reserved != 0);
if (fp) {
fhold(fp);
fdp->fd_files[fd].fp = fp;
fdp->fd_files[fd].reserved = 0;
} else {
fdp->fd_files[fd].reserved = 0;
fdreserve_locked(fdp, fd, -1);
fdfixup_locked(fdp, fd);
}
}
void
fsetfd(struct filedesc *fdp, struct file *fp, int fd)
{
spin_lock(&fdp->fd_spin);
fsetfd_locked(fdp, fp, fd);
spin_unlock(&fdp->fd_spin);
}
static
struct file *
funsetfd_locked(struct filedesc *fdp, int fd)
{
struct file *fp;
if ((unsigned)fd >= fdp->fd_nfiles)
return (NULL);
if ((fp = fdp->fd_files[fd].fp) == NULL)
return (NULL);
++fdp->fd_closedcounter;
fclearcache(&fdp->fd_files[fd], NULL, 0);
fdp->fd_files[fd].fp = NULL;
fdp->fd_files[fd].fileflags = 0;
++fdp->fd_closedcounter;
fdreserve_locked(fdp, fd, -1);
fdfixup_locked(fdp, fd);
return(fp);
}
int
fgetfdflags(struct filedesc *fdp, int fd, int *flagsp)
{
int error;
spin_lock_shared(&fdp->fd_spin);
if (((u_int)fd) >= fdp->fd_nfiles) {
error = EBADF;
} else if (fdp->fd_files[fd].fp == NULL) {
error = EBADF;
} else {
*flagsp = fdp->fd_files[fd].fileflags;
error = 0;
}
spin_unlock_shared(&fdp->fd_spin);
return (error);
}
int
faddfdflags(struct filedesc *fdp, int fd, int add_flags)
{
int error;
spin_lock(&fdp->fd_spin);
if (((u_int)fd) >= fdp->fd_nfiles) {
error = EBADF;
} else if (fdp->fd_files[fd].fp == NULL) {
error = EBADF;
} else {
fdp->fd_files[fd].fileflags |= add_flags;
error = 0;
}
spin_unlock(&fdp->fd_spin);
return (error);
}
int
fsetfdflags(struct filedesc *fdp, int fd, int set_flags)
{
int error;
spin_lock(&fdp->fd_spin);
if (((u_int)fd) >= fdp->fd_nfiles) {
error = EBADF;
} else if (fdp->fd_files[fd].fp == NULL) {
error = EBADF;
} else {
fdp->fd_files[fd].fileflags = (fileflags_t)set_flags;
error = 0;
}
spin_unlock(&fdp->fd_spin);
return (error);
}
int
fclrfdflags(struct filedesc *fdp, int fd, int rem_flags)
{
int error;
spin_lock(&fdp->fd_spin);
if (((u_int)fd) >= fdp->fd_nfiles) {
error = EBADF;
} else if (fdp->fd_files[fd].fp == NULL) {
error = EBADF;
} else {
fdp->fd_files[fd].fileflags &= ~rem_flags;
error = 0;
}
spin_unlock(&fdp->fd_spin);
return (error);
}
void
fsetcred(struct file *fp, struct ucred *ncr)
{
struct ucred *ocr;
struct uidinfo *uip;
struct uidcount *pup;
int cpu = mycpuid;
int count;
ocr = fp->f_cred;
if (ocr == NULL || ncr == NULL || ocr->cr_uidinfo != ncr->cr_uidinfo) {
if (ocr) {
uip = ocr->cr_uidinfo;
pup = &uip->ui_pcpu[cpu];
atomic_add_int(&pup->pu_openfiles, -1);
if (pup->pu_openfiles < -PUP_LIMIT ||
pup->pu_openfiles > PUP_LIMIT) {
count = atomic_swap_int(&pup->pu_openfiles, 0);
atomic_add_int(&uip->ui_openfiles, count);
}
}
if (ncr) {
uip = ncr->cr_uidinfo;
pup = &uip->ui_pcpu[cpu];
atomic_add_int(&pup->pu_openfiles, 1);
if (pup->pu_openfiles < -PUP_LIMIT ||
pup->pu_openfiles > PUP_LIMIT) {
count = atomic_swap_int(&pup->pu_openfiles, 0);
atomic_add_int(&uip->ui_openfiles, count);
}
}
}
if (ncr)
crhold(ncr);
fp->f_cred = ncr;
if (ocr)
crfree(ocr);
}
static
void
ffree(struct file *fp)
{
KASSERT((fp->f_count == 0), ("ffree: fp_fcount not 0!"));
fsetcred(fp, NULL);
if (fp->f_nchandle.ncp)
cache_drop(&fp->f_nchandle);
kfree_obj(fp, M_FILE);
}
void
fdinit_bootstrap(struct proc *p0, struct filedesc *fdp0, int cmask)
{
p0->p_fd = fdp0;
p0->p_fdtol = NULL;
fdp0->fd_refcnt = 1;
fdp0->fd_cmask = cmask;
fdp0->fd_files = fdp0->fd_builtin_files;
fdp0->fd_nfiles = NDFILE;
fdp0->fd_lastfile = -1;
spin_init(&fdp0->fd_spin, "fdinitbootstrap");
}
struct filedesc *
fdinit(struct proc *p)
{
struct filedesc *newfdp;
struct filedesc *fdp = p->p_fd;
newfdp = kmalloc(sizeof(struct filedesc), M_FILEDESC, M_WAITOK|M_ZERO);
spin_lock(&fdp->fd_spin);
if (fdp->fd_cdir) {
newfdp->fd_cdir = fdp->fd_cdir;
vref(newfdp->fd_cdir);
cache_copy(&fdp->fd_ncdir, &newfdp->fd_ncdir);
}
if (fdp->fd_rdir) {
newfdp->fd_rdir = fdp->fd_rdir;
vref(newfdp->fd_rdir);
cache_copy(&fdp->fd_nrdir, &newfdp->fd_nrdir);
}
if (fdp->fd_jdir) {
newfdp->fd_jdir = fdp->fd_jdir;
vref(newfdp->fd_jdir);
cache_copy(&fdp->fd_njdir, &newfdp->fd_njdir);
}
spin_unlock(&fdp->fd_spin);
newfdp->fd_refcnt = 1;
newfdp->fd_cmask = cmask;
newfdp->fd_files = newfdp->fd_builtin_files;
newfdp->fd_nfiles = NDFILE;
newfdp->fd_lastfile = -1;
spin_init(&newfdp->fd_spin, "fdinit");
return (newfdp);
}
struct filedesc *
fdshare(struct proc *p)
{
struct filedesc *fdp;
fdp = p->p_fd;
spin_lock(&fdp->fd_spin);
fdp->fd_refcnt++;
spin_unlock(&fdp->fd_spin);
return (fdp);
}
int
fdcopy(struct proc *p, struct filedesc **fpp)
{
struct filedesc *fdp = p->p_fd;
struct filedesc *newfdp;
struct fdnode *fdnode;
int i;
int ni;
if (fdp == NULL)
return (0);
newfdp = kmalloc(sizeof(struct filedesc),
M_FILEDESC, M_WAITOK | M_ZERO | M_NULLOK);
if (newfdp == NULL) {
*fpp = NULL;
return (-1);
}
again:
spin_lock(&fdp->fd_spin);
if (fdp->fd_lastfile < NDFILE) {
newfdp->fd_files = newfdp->fd_builtin_files;
i = NDFILE;
} else {
i = fdp->fd_nfiles;
ni = (i - 1) / 2;
while (ni > fdp->fd_lastfile && ni > NDFILE) {
i = ni;
ni = (i - 1) / 2;
}
spin_unlock(&fdp->fd_spin);
newfdp->fd_files = kmalloc(i * sizeof(struct fdnode),
M_FILEDESC, M_WAITOK | M_ZERO);
spin_lock(&fdp->fd_spin);
if (i <= fdp->fd_lastfile) {
spin_unlock(&fdp->fd_spin);
kfree(newfdp->fd_files, M_FILEDESC);
goto again;
}
}
if ((newfdp->fd_cdir = fdp->fd_cdir) != NULL) {
vref(newfdp->fd_cdir);
cache_copy(&fdp->fd_ncdir, &newfdp->fd_ncdir);
}
if ((newfdp->fd_rdir = fdp->fd_rdir) != NULL) {
vref(newfdp->fd_rdir);
cache_copy(&fdp->fd_nrdir, &newfdp->fd_nrdir);
}
if ((newfdp->fd_jdir = fdp->fd_jdir) != NULL) {
vref(newfdp->fd_jdir);
cache_copy(&fdp->fd_njdir, &newfdp->fd_njdir);
}
newfdp->fd_refcnt = 1;
newfdp->fd_nfiles = i;
newfdp->fd_lastfile = fdp->fd_lastfile;
newfdp->fd_freefile = fdp->fd_freefile;
newfdp->fd_cmask = fdp->fd_cmask;
spin_init(&newfdp->fd_spin, "fdcopy");
bcopy(fdp->fd_files, newfdp->fd_files, i * sizeof(struct fdnode));
for (i = 0 ; i < newfdp->fd_nfiles; ++i) {
fdnode = &newfdp->fd_files[i];
if (fdnode->reserved) {
fdreserve_locked(newfdp, i, -1);
fdnode->reserved = 0;
fdfixup_locked(newfdp, i);
} else if (fdnode->fp) {
bzero(&fdnode->tdcache, sizeof(fdnode->tdcache));
if (fdnode->fp->f_type == DTYPE_KQUEUE ||
(fdnode->fileflags & UF_FOCLOSE) != 0) {
(void)funsetfd_locked(newfdp, i);
} else {
fhold(fdnode->fp);
}
}
}
spin_unlock(&fdp->fd_spin);
*fpp = newfdp;
return (0);
}
void
fdfree(struct proc *p, struct filedesc *repl)
{
struct filedesc *fdp;
struct fdnode *fdnode;
int i;
struct filedesc_to_leader *fdtol;
struct file *fp;
struct vnode *vp;
struct flock lf;
fexitcache(curthread);
fdp = p->p_fd;
if (fdp == NULL) {
p->p_fd = repl;
return;
}
spin_lock(&fdp->fd_spin);
fdtol = p->p_fdtol;
if (fdtol != NULL) {
KASSERT(fdtol->fdl_refcount > 0,
("filedesc_to_refcount botch: fdl_refcount=%d",
fdtol->fdl_refcount));
if (fdtol->fdl_refcount == 1 && p->p_leader->p_advlock_flag) {
for (i = 0; i <= fdp->fd_lastfile; ++i) {
fdnode = &fdp->fd_files[i];
if (fdnode->fp == NULL ||
fdnode->fp->f_type != DTYPE_VNODE) {
continue;
}
fp = fdnode->fp;
fhold(fp);
spin_unlock(&fdp->fd_spin);
lf.l_whence = SEEK_SET;
lf.l_start = 0;
lf.l_len = 0;
lf.l_type = F_UNLCK;
vp = (struct vnode *)fp->f_data;
VOP_ADVLOCK(vp, (caddr_t)p->p_leader,
F_UNLCK, &lf, F_POSIX);
fdrop(fp);
spin_lock(&fdp->fd_spin);
}
}
retry:
if (fdtol->fdl_refcount == 1) {
if (fdp->fd_holdleaderscount > 0 &&
p->p_leader->p_advlock_flag) {
fdp->fd_holdleaderswakeup = 1;
ssleep(&fdp->fd_holdleaderscount,
&fdp->fd_spin, 0, "fdlhold", 0);
goto retry;
}
if (fdtol->fdl_holdcount > 0) {
fdtol->fdl_wakeup = 1;
ssleep(fdtol, &fdp->fd_spin, 0, "fdlhold", 0);
goto retry;
}
}
fdtol->fdl_refcount--;
if (fdtol->fdl_refcount == 0 &&
fdtol->fdl_holdcount == 0) {
fdtol->fdl_next->fdl_prev = fdtol->fdl_prev;
fdtol->fdl_prev->fdl_next = fdtol->fdl_next;
} else {
fdtol = NULL;
}
p->p_fdtol = NULL;
if (fdtol != NULL) {
spin_unlock(&fdp->fd_spin);
kfree(fdtol, M_FILEDESC_TO_LEADER);
spin_lock(&fdp->fd_spin);
}
}
if (--fdp->fd_refcnt > 0) {
spin_unlock(&fdp->fd_spin);
spin_lock(&p->p_spin);
p->p_fd = repl;
spin_unlock(&p->p_spin);
return;
}
for (i = 0; i <= fdp->fd_lastfile; ++i) {
if (fdp->fd_files[i].fp) {
fp = funsetfd_locked(fdp, i);
if (fp) {
spin_unlock(&fdp->fd_spin);
if (SLIST_FIRST(&fp->f_klist))
knote_fdclose(fp, fdp, i);
closef(fp, p);
spin_lock(&fdp->fd_spin);
}
}
}
spin_unlock(&fdp->fd_spin);
spin_lock(&p->p_spin);
p->p_fd = repl;
spin_unlock(&p->p_spin);
if (fdp->fd_softrefs) {
kprintf("pid %d: Warning, fdp race avoided\n", p->p_pid);
while (fdp->fd_softrefs)
tsleep(&fdp->fd_softrefs, 0, "fdsoft", 1);
}
if (fdp->fd_files != fdp->fd_builtin_files)
kfree(fdp->fd_files, M_FILEDESC);
if (fdp->fd_cdir) {
cache_drop(&fdp->fd_ncdir);
vrele(fdp->fd_cdir);
}
if (fdp->fd_rdir) {
cache_drop(&fdp->fd_nrdir);
vrele(fdp->fd_rdir);
}
if (fdp->fd_jdir) {
cache_drop(&fdp->fd_njdir);
vrele(fdp->fd_jdir);
}
kfree(fdp, M_FILEDESC);
}
struct file *
holdfp(thread_t td, int fd, int flag)
{
struct file *fp;
fp = _holdfp_cache(td, fd);
if (fp) {
if ((fp->f_flag & flag) == 0 && flag != -1) {
fdrop(fp);
fp = NULL;
}
}
return fp;
}
int
holdsock(thread_t td, int fd, struct file **fpp)
{
struct file *fp;
int error;
fp = _holdfp_cache(td, fd);
if (fp) {
if (fp->f_type != DTYPE_SOCKET) {
fdrop(fp);
fp = NULL;
error = ENOTSOCK;
} else {
error = 0;
}
} else {
error = EBADF;
}
*fpp = fp;
return (error);
}
int
holdvnode(thread_t td, int fd, struct file **fpp)
{
struct file *fp;
int error;
fp = _holdfp_cache(td, fd);
if (fp) {
if (fp->f_type != DTYPE_VNODE && fp->f_type != DTYPE_FIFO) {
fdrop(fp);
fp = NULL;
error = EINVAL;
} else {
error = 0;
}
} else {
error = EBADF;
}
*fpp = fp;
return (error);
}
int
holdvnode2(thread_t td, int fd, struct file **fpp, char *fflagsp)
{
struct file *fp;
int error;
fp = _holdfp2(td, fd, fflagsp);
if (fp) {
if (fp->f_type != DTYPE_VNODE && fp->f_type != DTYPE_FIFO) {
fdrop(fp);
fp = NULL;
error = EINVAL;
} else {
error = 0;
}
} else {
error = EBADF;
}
*fpp = fp;
return (error);
}
static int
is_unsafe(struct file *fp)
{
if (fp->f_type == DTYPE_VNODE &&
((struct vnode *)(fp->f_data))->v_tag == VT_PROCFS)
return (1);
return (0);
}
void
setugidsafety(struct proc *p)
{
struct filedesc *fdp = p->p_fd;
int i;
if (fdp == NULL)
return;
for (i = 0; i <= fdp->fd_lastfile; i++) {
if (i > 2)
break;
if (fdp->fd_files[i].fp && is_unsafe(fdp->fd_files[i].fp)) {
struct file *fp;
if ((fp = funsetfd_locked(fdp, i)) != NULL) {
knote_fdclose(fp, fdp, i);
closef(fp, p);
}
}
}
}
void
fdcloseexec(struct proc *p)
{
struct filedesc *fdp = p->p_fd;
int i;
if (fdp == NULL)
return;
for (i = 0; i <= fdp->fd_lastfile; i++) {
if (fdp->fd_files[i].fileflags & UF_FOCLOSE)
fdp->fd_files[i].fileflags &= ~UF_FOCLOSE;
if (fdp->fd_files[i].fp != NULL &&
(fdp->fd_files[i].fileflags & UF_EXCLOSE)) {
struct file *fp;
if ((fp = funsetfd_locked(fdp, i)) != NULL) {
knote_fdclose(fp, fdp, i);
closef(fp, p);
}
}
}
}
int
fdcheckstd(struct lwp *lp)
{
struct nlookupdata nd;
struct filedesc *fdp;
struct file *fp;
int retval;
int i, error, flags, devnull;
fdp = lp->lwp_proc->p_fd;
if (fdp == NULL)
return (0);
devnull = -1;
error = 0;
for (i = 0; i < 3; i++) {
if (fdp->fd_files[i].fp != NULL)
continue;
if (devnull < 0) {
if ((error = falloc(lp, &fp, &devnull)) != 0)
break;
error = nlookup_init(&nd, "/dev/null", UIO_SYSSPACE,
NLC_FOLLOW|NLC_LOCKVP);
flags = FREAD | FWRITE;
if (error == 0)
error = vn_open(&nd, &fp, flags, 0);
if (error == 0)
fsetfd(fdp, fp, devnull);
else
fsetfd(fdp, NULL, devnull);
fdrop(fp);
nlookup_done(&nd);
if (error)
break;
KKASSERT(i == devnull);
} else {
error = kern_dup(DUP_FIXED, devnull, i, &retval);
if (error != 0)
break;
}
}
return (error);
}
int
closef(struct file *fp, struct proc *p)
{
struct vnode *vp;
struct flock lf;
struct filedesc_to_leader *fdtol;
if (fp == NULL)
return (0);
if (p != NULL && fp->f_type == DTYPE_VNODE &&
(((struct vnode *)fp->f_data)->v_flag & VMAYHAVELOCKS)
) {
if (p->p_leader->p_advlock_flag) {
lf.l_whence = SEEK_SET;
lf.l_start = 0;
lf.l_len = 0;
lf.l_type = F_UNLCK;
vp = (struct vnode *)fp->f_data;
VOP_ADVLOCK(vp, (caddr_t)p->p_leader, F_UNLCK,
&lf, F_POSIX);
}
fdtol = p->p_fdtol;
if (fdtol != NULL) {
lwkt_gettoken(&p->p_token);
for (fdtol = fdtol->fdl_next;
fdtol != p->p_fdtol;
fdtol = fdtol->fdl_next) {
if (fdtol->fdl_leader->p_advlock_flag == 0)
continue;
fdtol->fdl_holdcount++;
lf.l_whence = SEEK_SET;
lf.l_start = 0;
lf.l_len = 0;
lf.l_type = F_UNLCK;
vp = (struct vnode *)fp->f_data;
VOP_ADVLOCK(vp, (caddr_t)fdtol->fdl_leader,
F_UNLCK, &lf, F_POSIX);
fdtol->fdl_holdcount--;
if (fdtol->fdl_holdcount == 0 &&
fdtol->fdl_wakeup != 0) {
fdtol->fdl_wakeup = 0;
wakeup(fdtol);
}
}
lwkt_reltoken(&p->p_token);
}
}
return (fdrop(fp));
}
void
fhold(struct file *fp)
{
KASSERT(fp->f_count > 0, ("fhold: invalid f_count %d", fp->f_count));
atomic_add_int(&fp->f_count, 1);
}
int
fdrop(struct file *fp)
{
struct flock lf;
struct vnode *vp;
int error, do_free = 0;
for (;;) {
int count = fp->f_count;
cpu_ccfence();
KASSERT(count > 0, ("fdrop: invalid f_count %d", count));
if (count == 1) {
struct filelist_head *head = fp2filelist(fp);
spin_lock(&head->spin);
if (atomic_cmpset_int(&fp->f_count, count, 0)) {
LIST_REMOVE(fp, f_list);
spin_unlock(&head->spin);
atomic_subtract_int(&nfiles, 1);
do_free = 1;
break;
}
spin_unlock(&head->spin);
} else if (atomic_cmpset_int(&fp->f_count, count, count - 1)) {
break;
}
}
if (!do_free)
return (0);
KKASSERT(SLIST_FIRST(&fp->f_klist) == NULL);
if (fp->f_count < 0)
panic("fdrop: count < 0");
if ((fp->f_flag & FHASLOCK) && fp->f_type == DTYPE_VNODE &&
(((struct vnode *)fp->f_data)->v_flag & VMAYHAVELOCKS)
) {
lf.l_whence = SEEK_SET;
lf.l_start = 0;
lf.l_len = 0;
lf.l_type = F_UNLCK;
vp = (struct vnode *)fp->f_data;
VOP_ADVLOCK(vp, (caddr_t)fp, F_UNLCK, &lf, 0);
}
if (fp->f_ops != &badfileops)
error = fo_close(fp);
else
error = 0;
ffree(fp);
return (error);
}
int
sys_flock(struct sysmsg *sysmsg, const struct flock_args *uap)
{
thread_t td = curthread;
struct file *fp;
struct vnode *vp;
struct flock lf;
int error;
if ((fp = holdfp(td, uap->fd, -1)) == NULL)
return (EBADF);
if (fp->f_type != DTYPE_VNODE) {
error = EOPNOTSUPP;
goto done;
}
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;
atomic_clear_int(&fp->f_flag, FHASLOCK);
error = VOP_ADVLOCK(vp, (caddr_t)fp, F_UNLCK, &lf, 0);
goto done;
}
if (uap->how & LOCK_EX)
lf.l_type = F_WRLCK;
else if (uap->how & LOCK_SH)
lf.l_type = F_RDLCK;
else {
error = EBADF;
goto done;
}
if (uap->how & LOCK_NB)
error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, 0);
else
error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, F_WAIT);
atomic_set_int(&fp->f_flag, FHASLOCK);
done:
fdrop(fp);
return (error);
}
static int
fdopen(struct dev_open_args *ap)
{
struct file *wfp;
thread_t td;
int error;
int sfd;
td = curthread;
KKASSERT(td->td_lwp != NULL);
sfd = minor(ap->a_head.a_dev);
if ((wfp = holdfp(td, sfd, -1)) == NULL)
return (EBADF);
if (wfp->f_flag & FREVOKED) {
kprintf("Warning: attempt to dup() a revoked descriptor\n");
fdrop(wfp);
wfp = NULL;
error = falloc(NULL, &wfp, NULL);
if (error)
return (error);
}
if (ap->a_fpp == NULL) {
fdrop(wfp);
return EINVAL;
}
if (((ap->a_oflags & (FREAD|FWRITE)) | wfp->f_flag) != wfp->f_flag) {
fdrop(wfp);
return EACCES;
}
if (wfp->f_type == DTYPE_VNODE && wfp->f_data) {
struct vnode *vp;
struct file *fp;
vp = wfp->f_data;
fp = *ap->a_fpp;
if ((ap->a_oflags & (FWRITE|O_TRUNC)) && vp->v_type == VDIR) {
fdrop(wfp);
return EISDIR;
}
fp->f_type = DTYPE_VNODE;
fp->f_flag = (fp->f_flag & ~FMASK) | (ap->a_oflags & FMASK);
fp->f_ops = &vnode_fileops;
fp->f_data = vp;
vref(vp);
if (ap->a_oflags & FWRITE)
atomic_add_int(&vp->v_writecount, 1);
KKASSERT(vp->v_opencount >= 0 && vp->v_opencount != INT_MAX);
atomic_add_int(&vp->v_opencount, 1);
fdrop(wfp);
} else {
fdrop(*ap->a_fpp);
*ap->a_fpp = wfp;
}
return EALREADY;
}
struct filedesc_to_leader *
filedesc_to_leader_alloc(struct filedesc_to_leader *old,
struct proc *leader)
{
struct filedesc_to_leader *fdtol;
fdtol = kmalloc(sizeof(struct filedesc_to_leader),
M_FILEDESC_TO_LEADER, M_WAITOK | M_ZERO);
fdtol->fdl_refcount = 1;
fdtol->fdl_holdcount = 0;
fdtol->fdl_wakeup = 0;
fdtol->fdl_leader = leader;
if (old != NULL) {
fdtol->fdl_next = old->fdl_next;
fdtol->fdl_prev = old;
old->fdl_next = fdtol;
fdtol->fdl_next->fdl_prev = fdtol;
} else {
fdtol->fdl_next = fdtol;
fdtol->fdl_prev = fdtol;
}
return fdtol;
}
void
allfiles_scan_exclusive(int (*callback)(struct file *, void *), void *data)
{
int i;
for (i = 0; i < NFILELIST_HEADS; ++i) {
struct filelist_head *head = &filelist_heads[i];
struct file *fp;
spin_lock(&head->spin);
LIST_FOREACH(fp, &head->list, f_list) {
int res;
res = callback(fp, data);
if (res < 0)
break;
}
spin_unlock(&head->spin);
}
}
struct sysctl_kern_file_info {
int count;
int error;
struct sysctl_req *req;
};
static int sysctl_kern_file_callback(struct proc *p, void *data);
static int
sysctl_kern_file(SYSCTL_HANDLER_ARGS)
{
struct sysctl_kern_file_info info;
info.count = 0;
info.error = 0;
info.req = req;
allproc_scan(sysctl_kern_file_callback, &info, 0);
if (req->oldptr == NULL) {
info.count = (info.count + 16) + (info.count / 10);
info.error = SYSCTL_OUT(req, NULL,
info.count * sizeof(struct kinfo_file));
}
return (info.error);
}
static int
sysctl_kern_file_callback(struct proc *p, void *data)
{
struct sysctl_kern_file_info *info = data;
struct kinfo_file kf;
struct filedesc *fdp;
struct file *fp;
uid_t uid;
int n;
if (p->p_stat == SIDL || p->p_stat == SZOMB)
return(0);
if (!(PRISON_CHECK(info->req->td->td_ucred, p->p_ucred) != 0))
return(0);
spin_lock(&p->p_spin);
if ((fdp = p->p_fd) == NULL) {
spin_unlock(&p->p_spin);
return(0);
}
atomic_add_int(&fdp->fd_softrefs, 1);
spin_unlock(&p->p_spin);
spin_lock_shared(&fdp->fd_spin);
for (n = 0; n < fdp->fd_nfiles; ++n) {
if ((fp = fdp->fd_files[n].fp) == NULL)
continue;
if (info->req->oldptr == NULL) {
++info->count;
} else {
uid = p->p_ucred ? p->p_ucred->cr_uid : -1;
kcore_make_file(&kf, fp, p->p_pid, uid, n);
spin_unlock_shared(&fdp->fd_spin);
info->error = SYSCTL_OUT(info->req, &kf, sizeof(kf));
spin_lock_shared(&fdp->fd_spin);
if (info->error)
break;
}
}
spin_unlock_shared(&fdp->fd_spin);
atomic_subtract_int(&fdp->fd_softrefs, 1);
if (info->error)
return(-1);
return(0);
}
SYSCTL_PROC(_kern, KERN_FILE, file, CTLTYPE_OPAQUE|CTLFLAG_RD,
0, 0, sysctl_kern_file, "S,file", "Entire file table");
SYSCTL_INT(_kern, OID_AUTO, minfilesperproc, CTLFLAG_RW,
&minfilesperproc, 0, "Minimum files allowed open per process");
SYSCTL_INT(_kern, KERN_MAXFILESPERPROC, maxfilesperproc, CTLFLAG_RW,
&maxfilesperproc, 0, "Maximum files allowed open per process");
SYSCTL_INT(_kern, OID_AUTO, maxfilesperuser, CTLFLAG_RW,
&maxfilesperuser, 0, "Maximum files allowed open per user");
SYSCTL_INT(_kern, KERN_MAXFILES, maxfiles, CTLFLAG_RW,
&maxfiles, 0, "Maximum number of files");
SYSCTL_INT(_kern, OID_AUTO, maxfilesrootres, CTLFLAG_RW,
&maxfilesrootres, 0, "Descriptors reserved for root use");
SYSCTL_INT(_kern, OID_AUTO, openfiles, CTLFLAG_RD,
&nfiles, 0, "System-wide number of open files");
static void
fildesc_drvinit(void *unused)
{
int fd;
for (fd = 0; fd < NUMFDESC; fd++) {
make_dev(&fildesc_ops, fd,
UID_BIN, GID_BIN, 0666, "fd/%d", fd);
}
make_dev(&fildesc_ops, 0, UID_ROOT, GID_WHEEL, 0666, "stdin");
make_dev(&fildesc_ops, 1, UID_ROOT, GID_WHEEL, 0666, "stdout");
make_dev(&fildesc_ops, 2, UID_ROOT, GID_WHEEL, 0666, "stderr");
}
struct fileops badfileops = {
.fo_read = badfo_readwrite,
.fo_write = badfo_readwrite,
.fo_ioctl = badfo_ioctl,
.fo_kqfilter = badfo_kqfilter,
.fo_stat = badfo_stat,
.fo_close = badfo_close,
.fo_shutdown = badfo_shutdown,
.fo_seek = badfo_seek
};
int
badfo_readwrite(
struct file *fp,
struct uio *uio,
struct ucred *cred,
int flags
) {
return (EBADF);
}
int
badfo_ioctl(struct file *fp, u_long com, caddr_t data,
struct ucred *cred, struct sysmsg *msgv)
{
return (EBADF);
}
int
badfo_kqfilter(struct file *fp, struct knote *kn)
{
return (EOPNOTSUPP);
}
int
badfo_stat(struct file *fp, struct stat *sb, struct ucred *cred)
{
return (EBADF);
}
int
badfo_close(struct file *fp)
{
return (EBADF);
}
int
badfo_shutdown(struct file *fp, int how)
{
return (EBADF);
}
int
nofo_shutdown(struct file *fp, int how)
{
return (EOPNOTSUPP);
}
int
badfo_seek(struct file *fp, off_t offset, int whence, off_t *res)
{
return (ESPIPE);
}
SYSINIT(fildescdev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE + CDEV_MAJOR,
fildesc_drvinit,NULL);
static void
filelist_heads_init(void *arg __unused)
{
int i;
for (i = 0; i < NFILELIST_HEADS; ++i) {
struct filelist_head *head = &filelist_heads[i];
spin_init(&head->spin, "filehead_spin");
LIST_INIT(&head->list);
}
}
SYSINIT(filelistheads, SI_BOOT1_LOCK, SI_ORDER_ANY, filelist_heads_init, NULL);