File: [cvs.NetBSD.org] / src / sys / miscfs / fdesc / fdesc_vnops.c (download)
Revision 1.109, Fri Jan 8 11:35:10 2010 UTC (14 years, 2 months ago) by pooka
Branch: MAIN
CVS Tags: yamt-nfs-mp-base9, uebayasi-xip-base1, uebayasi-xip-base Branch point for: uebayasi-xip, rmind-uvmplock
Changes since 1.108: +4 -4
lines
The VATTR_NULL/VREF/VHOLD/HOLDRELE() macros lost their will to live
years ago when the kernel was modified to not alter ABI based on
DIAGNOSTIC, and now just call the respective function interfaces
(in lowercase). Plenty of mix'n match upper/lowercase has creeped
into the tree since then. Nuke the macros and convert all callsites
to lowercase.
no functional change
|
/* $NetBSD: fdesc_vnops.c,v 1.109 2010/01/08 11:35:10 pooka Exp $ */
/*
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software donated to Berkeley by
* Jan-Simon Pendry.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)fdesc_vnops.c 8.17 (Berkeley) 5/22/95
*
* #Id: fdesc_vnops.c,v 1.12 1993/04/06 16:17:17 jsp Exp #
*/
/*
* /dev/fd Filesystem
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: fdesc_vnops.c,v 1.109 2010/01/08 11:35:10 pooka Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/proc.h>
#include <sys/kernel.h> /* boottime */
#include <sys/resourcevar.h>
#include <sys/socketvar.h>
#include <sys/filedesc.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/buf.h>
#include <sys/dirent.h>
#include <sys/tty.h>
#include <sys/kauth.h>
#include <sys/atomic.h>
#include <miscfs/fdesc/fdesc.h>
#include <miscfs/genfs/genfs.h>
#define cttyvp(p) ((p)->p_lflag & PL_CONTROLT ? (p)->p_session->s_ttyvp : NULL)
#define FDL_WANT 0x01
#define FDL_LOCKED 0x02
static int fdcache_lock;
dev_t devctty;
#if (FD_STDIN != FD_STDOUT-1) || (FD_STDOUT != FD_STDERR-1)
FD_STDIN, FD_STDOUT, FD_STDERR must be a sequence n, n+1, n+2
#endif
#define NFDCACHE 4
#define FD_NHASH(ix) \
(&fdhashtbl[(ix) & fdhash])
LIST_HEAD(fdhashhead, fdescnode) *fdhashtbl;
u_long fdhash;
int fdesc_lookup(void *);
#define fdesc_create genfs_eopnotsupp
#define fdesc_mknod genfs_eopnotsupp
int fdesc_open(void *);
#define fdesc_close genfs_nullop
#define fdesc_access genfs_nullop
int fdesc_getattr(void *);
int fdesc_setattr(void *);
int fdesc_read(void *);
int fdesc_write(void *);
int fdesc_ioctl(void *);
int fdesc_poll(void *);
int fdesc_kqfilter(void *);
#define fdesc_mmap genfs_eopnotsupp
#define fdesc_fcntl genfs_fcntl
#define fdesc_fsync genfs_nullop
#define fdesc_seek genfs_seek
#define fdesc_remove genfs_eopnotsupp
int fdesc_link(void *);
#define fdesc_rename genfs_eopnotsupp
#define fdesc_mkdir genfs_eopnotsupp
#define fdesc_rmdir genfs_eopnotsupp
int fdesc_symlink(void *);
int fdesc_readdir(void *);
int fdesc_readlink(void *);
#define fdesc_abortop genfs_abortop
int fdesc_inactive(void *);
int fdesc_reclaim(void *);
#define fdesc_lock genfs_lock
#define fdesc_unlock genfs_unlock
#define fdesc_bmap genfs_badop
#define fdesc_strategy genfs_badop
int fdesc_print(void *);
int fdesc_pathconf(void *);
#define fdesc_islocked genfs_islocked
#define fdesc_advlock genfs_einval
#define fdesc_bwrite genfs_eopnotsupp
#define fdesc_revoke genfs_revoke
#define fdesc_putpages genfs_null_putpages
static int fdesc_attr(int, struct vattr *, kauth_cred_t);
int (**fdesc_vnodeop_p)(void *);
const struct vnodeopv_entry_desc fdesc_vnodeop_entries[] = {
{ &vop_default_desc, vn_default_error },
{ &vop_lookup_desc, fdesc_lookup }, /* lookup */
{ &vop_create_desc, fdesc_create }, /* create */
{ &vop_mknod_desc, fdesc_mknod }, /* mknod */
{ &vop_open_desc, fdesc_open }, /* open */
{ &vop_close_desc, fdesc_close }, /* close */
{ &vop_access_desc, fdesc_access }, /* access */
{ &vop_getattr_desc, fdesc_getattr }, /* getattr */
{ &vop_setattr_desc, fdesc_setattr }, /* setattr */
{ &vop_read_desc, fdesc_read }, /* read */
{ &vop_write_desc, fdesc_write }, /* write */
{ &vop_ioctl_desc, fdesc_ioctl }, /* ioctl */
{ &vop_fcntl_desc, fdesc_fcntl }, /* fcntl */
{ &vop_poll_desc, fdesc_poll }, /* poll */
{ &vop_kqfilter_desc, fdesc_kqfilter }, /* kqfilter */
{ &vop_revoke_desc, fdesc_revoke }, /* revoke */
{ &vop_mmap_desc, fdesc_mmap }, /* mmap */
{ &vop_fsync_desc, fdesc_fsync }, /* fsync */
{ &vop_seek_desc, fdesc_seek }, /* seek */
{ &vop_remove_desc, fdesc_remove }, /* remove */
{ &vop_link_desc, fdesc_link }, /* link */
{ &vop_rename_desc, fdesc_rename }, /* rename */
{ &vop_mkdir_desc, fdesc_mkdir }, /* mkdir */
{ &vop_rmdir_desc, fdesc_rmdir }, /* rmdir */
{ &vop_symlink_desc, fdesc_symlink }, /* symlink */
{ &vop_readdir_desc, fdesc_readdir }, /* readdir */
{ &vop_readlink_desc, fdesc_readlink }, /* readlink */
{ &vop_abortop_desc, fdesc_abortop }, /* abortop */
{ &vop_inactive_desc, fdesc_inactive }, /* inactive */
{ &vop_reclaim_desc, fdesc_reclaim }, /* reclaim */
{ &vop_lock_desc, fdesc_lock }, /* lock */
{ &vop_unlock_desc, fdesc_unlock }, /* unlock */
{ &vop_bmap_desc, fdesc_bmap }, /* bmap */
{ &vop_strategy_desc, fdesc_strategy }, /* strategy */
{ &vop_print_desc, fdesc_print }, /* print */
{ &vop_islocked_desc, fdesc_islocked }, /* islocked */
{ &vop_pathconf_desc, fdesc_pathconf }, /* pathconf */
{ &vop_advlock_desc, fdesc_advlock }, /* advlock */
{ &vop_bwrite_desc, fdesc_bwrite }, /* bwrite */
{ &vop_putpages_desc, fdesc_putpages }, /* putpages */
{ NULL, NULL }
};
const struct vnodeopv_desc fdesc_vnodeop_opv_desc =
{ &fdesc_vnodeop_p, fdesc_vnodeop_entries };
/*
* Initialise cache headers
*/
void
fdesc_init(void)
{
int cttymajor;
/* locate the major number */
cttymajor = devsw_name2chr("ctty", NULL, 0);
devctty = makedev(cttymajor, 0);
fdhashtbl = hashinit(NFDCACHE, HASH_LIST, true, &fdhash);
}
/*
* Free hash table.
*/
void
fdesc_done(void)
{
hashdone(fdhashtbl, HASH_LIST, fdhash);
}
/*
* Return a locked vnode of the correct type.
*/
int
fdesc_allocvp(fdntype ftype, int ix, struct mount *mp, struct vnode **vpp)
{
struct fdhashhead *fc;
struct fdescnode *fd;
int error = 0;
fc = FD_NHASH(ix);
loop:
for (fd = fc->lh_first; fd != 0; fd = fd->fd_hash.le_next) {
if (fd->fd_ix == ix && fd->fd_vnode->v_mount == mp) {
if (vget(fd->fd_vnode, LK_EXCLUSIVE))
goto loop;
*vpp = fd->fd_vnode;
return (error);
}
}
/*
* otherwise lock the array while we call getnewvnode
* since that can block.
*/
if (fdcache_lock & FDL_LOCKED) {
fdcache_lock |= FDL_WANT;
(void) tsleep(&fdcache_lock, PINOD, "fdcache", 0);
goto loop;
}
fdcache_lock |= FDL_LOCKED;
error = getnewvnode(VT_FDESC, mp, fdesc_vnodeop_p, vpp);
if (error)
goto out;
fd = malloc(sizeof(struct fdescnode), M_TEMP, M_WAITOK);
(*vpp)->v_data = fd;
fd->fd_vnode = *vpp;
fd->fd_type = ftype;
fd->fd_fd = -1;
fd->fd_link = 0;
fd->fd_ix = ix;
uvm_vnp_setsize(*vpp, 0);
VOP_LOCK(*vpp, LK_EXCLUSIVE);
LIST_INSERT_HEAD(fc, fd, fd_hash);
out:;
fdcache_lock &= ~FDL_LOCKED;
if (fdcache_lock & FDL_WANT) {
fdcache_lock &= ~FDL_WANT;
wakeup(&fdcache_lock);
}
return (error);
}
/*
* vp is the current namei directory
* ndp is the name to locate in that directory...
*/
int
fdesc_lookup(void *v)
{
struct vop_lookup_args /* {
struct vnode * a_dvp;
struct vnode ** a_vpp;
struct componentname * a_cnp;
} */ *ap = v;
struct vnode **vpp = ap->a_vpp;
struct vnode *dvp = ap->a_dvp;
struct componentname *cnp = ap->a_cnp;
struct lwp *l = curlwp;
const char *pname = cnp->cn_nameptr;
struct proc *p = l->l_proc;
unsigned fd = 0;
int error;
struct vnode *fvp;
const char *ln;
fdtab_t *dt;
dt = curlwp->l_fd->fd_dt;
if (cnp->cn_namelen == 1 && *pname == '.') {
*vpp = dvp;
vref(dvp);
return (0);
}
switch (VTOFDESC(dvp)->fd_type) {
default:
case Flink:
case Fdesc:
case Fctty:
error = ENOTDIR;
goto bad;
case Froot:
if (cnp->cn_namelen == 2 && memcmp(pname, "fd", 2) == 0) {
error = fdesc_allocvp(Fdevfd, FD_DEVFD, dvp->v_mount, &fvp);
if (error)
goto bad;
*vpp = fvp;
fvp->v_type = VDIR;
goto good;
}
if (cnp->cn_namelen == 3 && memcmp(pname, "tty", 3) == 0) {
struct vnode *ttyvp = cttyvp(p);
if (ttyvp == NULL) {
error = ENXIO;
goto bad;
}
error = fdesc_allocvp(Fctty, FD_CTTY, dvp->v_mount, &fvp);
if (error)
goto bad;
*vpp = fvp;
fvp->v_type = VCHR;
goto good;
}
ln = 0;
switch (cnp->cn_namelen) {
case 5:
if (memcmp(pname, "stdin", 5) == 0) {
ln = "fd/0";
fd = FD_STDIN;
}
break;
case 6:
if (memcmp(pname, "stdout", 6) == 0) {
ln = "fd/1";
fd = FD_STDOUT;
} else
if (memcmp(pname, "stderr", 6) == 0) {
ln = "fd/2";
fd = FD_STDERR;
}
break;
}
if (ln) {
error = fdesc_allocvp(Flink, fd, dvp->v_mount, &fvp);
if (error)
goto bad;
/* XXXUNCONST */
VTOFDESC(fvp)->fd_link = __UNCONST(ln);
*vpp = fvp;
fvp->v_type = VLNK;
goto good;
} else {
error = ENOENT;
goto bad;
}
/* FALL THROUGH */
case Fdevfd:
if (cnp->cn_namelen == 2 && memcmp(pname, "..", 2) == 0) {
VOP_UNLOCK(dvp, 0);
error = fdesc_root(dvp->v_mount, vpp);
vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
if (error)
goto bad;
return (error);
}
fd = 0;
while (*pname >= '0' && *pname <= '9') {
fd = 10 * fd + *pname++ - '0';
if (fd >= dt->dt_nfiles)
break;
}
if (*pname != '\0') {
error = ENOENT;
goto bad;
}
if (fd >= dt->dt_nfiles || dt->dt_ff[fd] == NULL ||
dt->dt_ff[fd]->ff_file == NULL) {
error = EBADF;
goto bad;
}
error = fdesc_allocvp(Fdesc, FD_DESC+fd, dvp->v_mount, &fvp);
if (error)
goto bad;
VTOFDESC(fvp)->fd_fd = fd;
*vpp = fvp;
goto good;
}
bad:
*vpp = NULL;
return (error);
good:
return (0);
}
int
fdesc_open(void *v)
{
struct vop_open_args /* {
struct vnode *a_vp;
int a_mode;
kauth_cred_t a_cred;
} */ *ap = v;
struct vnode *vp = ap->a_vp;
switch (VTOFDESC(vp)->fd_type) {
case Fdesc:
/*
* XXX Kludge: set 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. Other callers of vn_open or
* VOP_OPEN will simply report the error.
*/
curlwp->l_dupfd = VTOFDESC(vp)->fd_fd; /* XXX */
return EDUPFD;
case Fctty:
return cdev_open(devctty, ap->a_mode, 0, curlwp);
case Froot:
case Fdevfd:
case Flink:
break;
}
return (0);
}
static int
fdesc_attr(int fd, struct vattr *vap, kauth_cred_t cred)
{
file_t *fp;
struct stat stb;
int error;
if ((fp = fd_getfile(fd)) == NULL)
return (EBADF);
switch (fp->f_type) {
case DTYPE_VNODE:
error = VOP_GETATTR((struct vnode *) fp->f_data, vap, cred);
if (error == 0 && vap->va_type == VDIR) {
/*
* directories can cause loops in the namespace,
* so turn off the 'x' bits to avoid trouble.
*/
vap->va_mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
}
break;
default:
memset(&stb, 0, sizeof(stb));
error = (*fp->f_ops->fo_stat)(fp, &stb);
if (error)
break;
vattr_null(vap);
switch(fp->f_type) {
case DTYPE_SOCKET:
vap->va_type = VSOCK;
break;
case DTYPE_PIPE:
vap->va_type = VFIFO;
break;
default:
/* use VNON perhaps? */
vap->va_type = VBAD;
break;
}
vap->va_mode = stb.st_mode;
vap->va_nlink = stb.st_nlink;
vap->va_uid = stb.st_uid;
vap->va_gid = stb.st_gid;
vap->va_fsid = stb.st_dev;
vap->va_fileid = stb.st_ino;
vap->va_size = stb.st_size;
vap->va_blocksize = stb.st_blksize;
vap->va_atime = stb.st_atimespec;
vap->va_mtime = stb.st_mtimespec;
vap->va_ctime = stb.st_ctimespec;
vap->va_gen = stb.st_gen;
vap->va_flags = stb.st_flags;
vap->va_rdev = stb.st_rdev;
vap->va_bytes = stb.st_blocks * stb.st_blksize;
break;
}
fd_putfile(fd);
return (error);
}
int
fdesc_getattr(void *v)
{
struct vop_getattr_args /* {
struct vnode *a_vp;
struct vattr *a_vap;
kauth_cred_t a_cred;
struct lwp *a_l;
} */ *ap = v;
struct vnode *vp = ap->a_vp;
struct vattr *vap = ap->a_vap;
unsigned fd;
int error = 0;
switch (VTOFDESC(vp)->fd_type) {
case Froot:
case Fdevfd:
case Flink:
case Fctty:
vattr_null(vap);
vap->va_fileid = VTOFDESC(vp)->fd_ix;
#define R_ALL (S_IRUSR|S_IRGRP|S_IROTH)
#define W_ALL (S_IWUSR|S_IWGRP|S_IWOTH)
#define X_ALL (S_IXUSR|S_IXGRP|S_IXOTH)
switch (VTOFDESC(vp)->fd_type) {
case Flink:
vap->va_mode = R_ALL|X_ALL;
vap->va_type = VLNK;
vap->va_rdev = 0;
vap->va_nlink = 1;
vap->va_size = strlen(VTOFDESC(vp)->fd_link);
break;
case Fctty:
vap->va_mode = R_ALL|W_ALL;
vap->va_type = VCHR;
vap->va_rdev = devctty;
vap->va_nlink = 1;
vap->va_size = 0;
break;
default:
vap->va_mode = R_ALL|X_ALL;
vap->va_type = VDIR;
vap->va_rdev = 0;
vap->va_nlink = 2;
vap->va_size = DEV_BSIZE;
break;
}
vap->va_uid = 0;
vap->va_gid = 0;
vap->va_fsid = vp->v_mount->mnt_stat.f_fsidx.__fsid_val[0];
vap->va_blocksize = DEV_BSIZE;
vap->va_atime.tv_sec = boottime.tv_sec;
vap->va_atime.tv_nsec = 0;
vap->va_mtime = vap->va_atime;
vap->va_ctime = vap->va_mtime;
vap->va_gen = 0;
vap->va_flags = 0;
vap->va_bytes = 0;
break;
case Fdesc:
fd = VTOFDESC(vp)->fd_fd;
error = fdesc_attr(fd, vap, ap->a_cred);
break;
default:
panic("fdesc_getattr");
break;
}
if (error == 0)
vp->v_type = vap->va_type;
return (error);
}
int
fdesc_setattr(void *v)
{
struct vop_setattr_args /* {
struct vnode *a_vp;
struct vattr *a_vap;
kauth_cred_t a_cred;
} */ *ap = v;
file_t *fp;
unsigned fd;
/*
* Can't mess with the root vnode
*/
switch (VTOFDESC(ap->a_vp)->fd_type) {
case Fdesc:
break;
case Fctty:
return (0);
default:
return (EACCES);
}
fd = VTOFDESC(ap->a_vp)->fd_fd;
if ((fp = fd_getfile(fd)) == NULL)
return (EBADF);
/*
* XXX: Can't reasonably set the attr's on any types currently.
* On vnode's this will cause truncation and socket/pipes make
* no sense.
*/
fd_putfile(fd);
return (0);
}
struct fdesc_target {
ino_t ft_fileno;
u_char ft_type;
u_char ft_namlen;
const char *ft_name;
} fdesc_targets[] = {
#define N(s) sizeof(s)-1, s
{ FD_DEVFD, DT_DIR, N("fd") },
{ FD_STDIN, DT_LNK, N("stdin") },
{ FD_STDOUT, DT_LNK, N("stdout") },
{ FD_STDERR, DT_LNK, N("stderr") },
{ FD_CTTY, DT_UNKNOWN, N("tty") },
#undef N
#define UIO_MX _DIRENT_RECLEN((struct dirent *)NULL, sizeof("stderr") - 1)
};
static int nfdesc_targets = sizeof(fdesc_targets) / sizeof(fdesc_targets[0]);
int
fdesc_readdir(void *v)
{
struct vop_readdir_args /* {
struct vnode *a_vp;
struct uio *a_uio;
kauth_cred_t a_cred;
int *a_eofflag;
off_t **a_cookies;
int *a_ncookies;
} */ *ap = v;
struct uio *uio = ap->a_uio;
struct dirent d;
off_t i;
int j;
int error;
off_t *cookies = NULL;
int ncookies;
fdtab_t *dt;
switch (VTOFDESC(ap->a_vp)->fd_type) {
case Fctty:
return 0;
case Fdesc:
return ENOTDIR;
default:
break;
}
dt = curlwp->l_fd->fd_dt;
if (uio->uio_resid < UIO_MX)
return EINVAL;
if (uio->uio_offset < 0)
return EINVAL;
error = 0;
i = uio->uio_offset;
(void)memset(&d, 0, UIO_MX);
d.d_reclen = UIO_MX;
if (ap->a_ncookies)
ncookies = uio->uio_resid / UIO_MX;
else
ncookies = 0;
if (VTOFDESC(ap->a_vp)->fd_type == Froot) {
struct fdesc_target *ft;
if (i >= nfdesc_targets)
return 0;
if (ap->a_ncookies) {
ncookies = min(ncookies, (nfdesc_targets - i));
cookies = malloc(ncookies * sizeof(off_t),
M_TEMP, M_WAITOK);
*ap->a_cookies = cookies;
*ap->a_ncookies = ncookies;
}
for (ft = &fdesc_targets[i]; uio->uio_resid >= UIO_MX &&
i < nfdesc_targets; ft++, i++) {
switch (ft->ft_fileno) {
case FD_CTTY:
if (cttyvp(curproc) == NULL)
continue;
break;
case FD_STDIN:
case FD_STDOUT:
case FD_STDERR:
if ((ft->ft_fileno - FD_STDIN) >=
dt->dt_nfiles)
continue;
if (dt->dt_ff[ft->ft_fileno - FD_STDIN]
== NULL || dt->dt_ff[ft->ft_fileno -
FD_STDIN]->ff_file == NULL)
continue;
break;
}
d.d_fileno = ft->ft_fileno;
d.d_namlen = ft->ft_namlen;
(void)memcpy(d.d_name, ft->ft_name, ft->ft_namlen + 1);
d.d_type = ft->ft_type;
if ((error = uiomove(&d, UIO_MX, uio)) != 0)
break;
if (cookies)
*cookies++ = i + 1;
}
} else {
membar_consumer();
if (ap->a_ncookies) {
ncookies = min(ncookies, dt->dt_nfiles + 2);
cookies = malloc(ncookies * sizeof(off_t),
M_TEMP, M_WAITOK);
*ap->a_cookies = cookies;
*ap->a_ncookies = ncookies;
}
for (; i - 2 < dt->dt_nfiles && uio->uio_resid >= UIO_MX; i++) {
switch (i) {
case 0:
case 1:
d.d_fileno = FD_ROOT; /* XXX */
d.d_namlen = i + 1;
(void)memcpy(d.d_name, "..", d.d_namlen);
d.d_name[i + 1] = '\0';
d.d_type = DT_DIR;
break;
default:
j = (int)i - 2;
if (dt->dt_ff[j] == NULL ||
dt->dt_ff[j]->ff_file == NULL)
continue;
d.d_fileno = j + FD_STDIN;
d.d_namlen = sprintf(d.d_name, "%d", j);
d.d_type = DT_UNKNOWN;
break;
}
if ((error = uiomove(&d, UIO_MX, uio)) != 0)
break;
if (cookies)
*cookies++ = i + 1;
}
}
if (ap->a_ncookies && error) {
free(*ap->a_cookies, M_TEMP);
*ap->a_ncookies = 0;
*ap->a_cookies = NULL;
}
uio->uio_offset = i;
return error;
}
int
fdesc_readlink(void *v)
{
struct vop_readlink_args /* {
struct vnode *a_vp;
struct uio *a_uio;
kauth_cred_t a_cred;
} */ *ap = v;
struct vnode *vp = ap->a_vp;
int error;
if (vp->v_type != VLNK)
return (EPERM);
if (VTOFDESC(vp)->fd_type == Flink) {
char *ln = VTOFDESC(vp)->fd_link;
error = uiomove(ln, strlen(ln), ap->a_uio);
} else {
error = EOPNOTSUPP;
}
return (error);
}
int
fdesc_read(void *v)
{
struct vop_read_args /* {
struct vnode *a_vp;
struct uio *a_uio;
int a_ioflag;
kauth_cred_t a_cred;
} */ *ap = v;
int error = EOPNOTSUPP;
struct vnode *vp = ap->a_vp;
switch (VTOFDESC(vp)->fd_type) {
case Fctty:
VOP_UNLOCK(vp, 0);
error = cdev_read(devctty, ap->a_uio, ap->a_ioflag);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
int
fdesc_write(void *v)
{
struct vop_write_args /* {
struct vnode *a_vp;
struct uio *a_uio;
int a_ioflag;
kauth_cred_t a_cred;
} */ *ap = v;
int error = EOPNOTSUPP;
struct vnode *vp = ap->a_vp;
switch (VTOFDESC(vp)->fd_type) {
case Fctty:
VOP_UNLOCK(vp, 0);
error = cdev_write(devctty, ap->a_uio, ap->a_ioflag);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
int
fdesc_ioctl(void *v)
{
struct vop_ioctl_args /* {
struct vnode *a_vp;
u_long a_command;
void *a_data;
int a_fflag;
kauth_cred_t a_cred;
} */ *ap = v;
int error = EOPNOTSUPP;
switch (VTOFDESC(ap->a_vp)->fd_type) {
case Fctty:
error = cdev_ioctl(devctty, ap->a_command, ap->a_data,
ap->a_fflag, curlwp);
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
int
fdesc_poll(void *v)
{
struct vop_poll_args /* {
struct vnode *a_vp;
int a_events;
} */ *ap = v;
int revents;
switch (VTOFDESC(ap->a_vp)->fd_type) {
case Fctty:
revents = cdev_poll(devctty, ap->a_events, curlwp);
break;
default:
revents = genfs_poll(v);
break;
}
return (revents);
}
int
fdesc_kqfilter(void *v)
{
struct vop_kqfilter_args /* {
struct vnode *a_vp;
struct knote *a_kn;
} */ *ap = v;
int error, fd;
file_t *fp;
switch (VTOFDESC(ap->a_vp)->fd_type) {
case Fctty:
error = cdev_kqfilter(devctty, ap->a_kn);
break;
case Fdesc:
/* just invoke kqfilter for the underlying descriptor */
fd = VTOFDESC(ap->a_vp)->fd_fd;
if ((fp = fd_getfile(fd)) == NULL)
return (1);
error = (*fp->f_ops->fo_kqfilter)(fp, ap->a_kn);
fd_putfile(fd);
break;
default:
return (genfs_kqfilter(v));
}
return (error);
}
int
fdesc_inactive(void *v)
{
struct vop_inactive_args /* {
struct vnode *a_vp;
} */ *ap = v;
struct vnode *vp = ap->a_vp;
/*
* Clear out the v_type field to avoid
* nasty things happening in vgone().
*/
VOP_UNLOCK(vp, 0);
vp->v_type = VNON;
return (0);
}
int
fdesc_reclaim(void *v)
{
struct vop_reclaim_args /* {
struct vnode *a_vp;
} */ *ap = v;
struct vnode *vp = ap->a_vp;
struct fdescnode *fd = VTOFDESC(vp);
LIST_REMOVE(fd, fd_hash);
free(vp->v_data, M_TEMP);
vp->v_data = 0;
return (0);
}
/*
* Return POSIX pathconf information applicable to special devices.
*/
int
fdesc_pathconf(void *v)
{
struct vop_pathconf_args /* {
struct vnode *a_vp;
int a_name;
register_t *a_retval;
} */ *ap = v;
switch (ap->a_name) {
case _PC_LINK_MAX:
*ap->a_retval = LINK_MAX;
return (0);
case _PC_MAX_CANON:
*ap->a_retval = MAX_CANON;
return (0);
case _PC_MAX_INPUT:
*ap->a_retval = MAX_INPUT;
return (0);
case _PC_PIPE_BUF:
*ap->a_retval = PIPE_BUF;
return (0);
case _PC_CHOWN_RESTRICTED:
*ap->a_retval = 1;
return (0);
case _PC_VDISABLE:
*ap->a_retval = _POSIX_VDISABLE;
return (0);
case _PC_SYNC_IO:
*ap->a_retval = 1;
return (0);
default:
return (EINVAL);
}
/* NOTREACHED */
}
/*
* Print out the contents of a /dev/fd vnode.
*/
/* ARGSUSED */
int
fdesc_print(void *v)
{
printf("tag VT_NON, fdesc vnode\n");
return (0);
}
int
fdesc_link(void *v)
{
struct vop_link_args /* {
struct vnode *a_dvp;
struct vnode *a_vp;
struct componentname *a_cnp;
} */ *ap = v;
VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
vput(ap->a_dvp);
return (EROFS);
}
int
fdesc_symlink(void *v)
{
struct vop_symlink_args /* {
struct vnode *a_dvp;
struct vnode **a_vpp;
struct componentname *a_cnp;
struct vattr *a_vap;
char *a_target;
} */ *ap = v;
VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
vput(ap->a_dvp);
return (EROFS);
}