diff options
Diffstat (limited to 'sys/fs/fdescfs/fdesc_vnops.c')
-rw-r--r-- | sys/fs/fdescfs/fdesc_vnops.c | 874 |
1 files changed, 874 insertions, 0 deletions
diff --git a/sys/fs/fdescfs/fdesc_vnops.c b/sys/fs/fdescfs/fdesc_vnops.c new file mode 100644 index 0000000..3256349 --- /dev/null +++ b/sys/fs/fdescfs/fdesc_vnops.c @@ -0,0 +1,874 @@ +/* + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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.9 (Berkeley) 1/21/94 + * + * $FreeBSD$ + */ + +/* + * /dev/fd Filesystem + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/kernel.h> /* boottime */ +#include <sys/filedesc.h> +#include <sys/vnode.h> +#include <sys/malloc.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/mount.h> +#include <sys/namei.h> +#include <sys/dirent.h> +#include <sys/conf.h> +#include <miscfs/fdesc/fdesc.h> + +extern struct cdevsw ctty_cdevsw; + +#define cttyvp(p) ((p)->p_flag & P_CONTROLT ? (p)->p_session->s_ttyvp : NULL) + +#define FDL_WANT 0x01 +#define FDL_LOCKED 0x02 +static int fdcache_lock; + +static vop_t **fdesc_vnodeop_p; + +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]) +static LIST_HEAD(fdhashhead, fdescnode) *fdhashtbl; +static u_long fdhash; + +static int fdesc_attr __P((int fd, struct vattr *vap, struct ucred *cred, + struct proc *p)); +static int fdesc_badop __P((void)); +static int fdesc_getattr __P((struct vop_getattr_args *ap)); +static int fdesc_inactive __P((struct vop_inactive_args *ap)); +static int fdesc_ioctl __P((struct vop_ioctl_args *ap)); +static int fdesc_lookup __P((struct vop_lookup_args *ap)); +static int fdesc_open __P((struct vop_open_args *ap)); +static int fdesc_print __P((struct vop_print_args *ap)); +static int fdesc_read __P((struct vop_read_args *ap)); +static int fdesc_readdir __P((struct vop_readdir_args *ap)); +static int fdesc_readlink __P((struct vop_readlink_args *ap)); +static int fdesc_reclaim __P((struct vop_reclaim_args *ap)); +static int fdesc_poll __P((struct vop_poll_args *ap)); +static int fdesc_setattr __P((struct vop_setattr_args *ap)); +static int fdesc_write __P((struct vop_write_args *ap)); + +/* + * Initialise cache headers + */ +int +fdesc_init(vfsp) + struct vfsconf *vfsp; +{ + + devctty = NODEV; + fdhashtbl = hashinit(NFDCACHE, M_CACHE, &fdhash); + return (0); +} + +int +fdesc_allocvp(ftype, ix, mp, vpp) + fdntype ftype; + int ix; + struct mount *mp; + struct vnode **vpp; +{ + struct proc *p = curproc; /* XXX */ + 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, 0, p)) + 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((caddr_t) &fdcache_lock, PINOD, "fdalvp", 0); + goto loop; + } + fdcache_lock |= FDL_LOCKED; + + /* + * Do the MALLOC before the getnewvnode since doing so afterward + * might cause a bogus v_data pointer to get dereferenced + * elsewhere if MALLOC should block. + */ + MALLOC(fd, struct fdescnode *, sizeof(struct fdescnode), M_TEMP, M_WAITOK); + + error = getnewvnode(VT_FDESC, mp, fdesc_vnodeop_p, vpp); + if (error) { + FREE(fd, M_TEMP); + goto out; + } + (*vpp)->v_data = fd; + fd->fd_vnode = *vpp; + fd->fd_type = ftype; + fd->fd_fd = -1; + fd->fd_link = 0; + fd->fd_ix = ix; + LIST_INSERT_HEAD(fc, fd, fd_hash); + +out:; + fdcache_lock &= ~FDL_LOCKED; + + if (fdcache_lock & FDL_WANT) { + fdcache_lock &= ~FDL_WANT; + wakeup((caddr_t) &fdcache_lock); + } + + return (error); +} + +/* + * vp is the current namei directory + * ndp is the name to locate in that directory... + */ +static int +fdesc_lookup(ap) + struct vop_lookup_args /* { + struct vnode * a_dvp; + struct vnode ** a_vpp; + struct componentname * a_cnp; + } */ *ap; +{ + struct vnode **vpp = ap->a_vpp; + struct vnode *dvp = ap->a_dvp; + struct componentname *cnp = ap->a_cnp; + char *pname = cnp->cn_nameptr; + struct proc *p = cnp->cn_proc; + int nfiles = p->p_fd->fd_nfiles; + unsigned fd = -1; + int error; + struct vnode *fvp; + char *ln; + + if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) { + error = EROFS; + goto bad; + } + + VOP_UNLOCK(dvp, 0, p); + if (cnp->cn_namelen == 1 && *pname == '.') { + *vpp = dvp; + VREF(dvp); + vn_lock(dvp, LK_SHARED | LK_RETRY, p); + 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 && bcmp(pname, "fd", 2) == 0) { + error = fdesc_allocvp(Fdevfd, FD_DEVFD, dvp->v_mount, &fvp); + if (error) + goto bad; + *vpp = fvp; + fvp->v_type = VDIR; + vn_lock(fvp, LK_SHARED | LK_RETRY, p); + return (0); + } + + if (cnp->cn_namelen == 3 && bcmp(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 = VFIFO; + vn_lock(fvp, LK_SHARED | LK_RETRY, p); + return (0); + } + + ln = 0; + switch (cnp->cn_namelen) { + case 5: + if (bcmp(pname, "stdin", 5) == 0) { + ln = "fd/0"; + fd = FD_STDIN; + } + break; + case 6: + if (bcmp(pname, "stdout", 6) == 0) { + ln = "fd/1"; + fd = FD_STDOUT; + } else + if (bcmp(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; + VTOFDESC(fvp)->fd_link = ln; + *vpp = fvp; + fvp->v_type = VLNK; + vn_lock(fvp, LK_SHARED | LK_RETRY, p); + return (0); + } else { + error = ENOENT; + goto bad; + } + + /* FALL THROUGH */ + + case Fdevfd: + if (cnp->cn_namelen == 2 && bcmp(pname, "..", 2) == 0) { + if ((error = fdesc_root(dvp->v_mount, vpp)) != 0) + goto bad; + return (0); + } + + fd = 0; + while (*pname >= '0' && *pname <= '9') { + fd = 10 * fd + *pname++ - '0'; + if (fd >= nfiles) + break; + } + + if (*pname != '\0') { + error = ENOENT; + goto bad; + } + + if (fd >= nfiles || p->p_fd->fd_ofiles[fd] == 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; + vn_lock(fvp, LK_SHARED | LK_RETRY, p); + *vpp = fvp; + return (0); + } + +bad:; + vn_lock(dvp, LK_SHARED | LK_RETRY, p); + *vpp = NULL; + return (error); +} + +static int +fdesc_open(ap) + struct vop_open_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + int error = 0; + + switch (VTOFDESC(vp)->fd_type) { + case Fdesc: + /* + * XXX Kludge: set p->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. Other callers of vn_open or + * VOP_OPEN will simply report the error. + */ + ap->a_p->p_dupfd = VTOFDESC(vp)->fd_fd; /* XXX */ + error = ENODEV; + break; + + case Fctty: + error = (*ctty_cdevsw.d_open)(devctty, ap->a_mode, 0, ap->a_p); + break; + + default: /* nothing special */ + break; + } + + return (error); +} + +static int +fdesc_attr(fd, vap, cred, p) + int fd; + struct vattr *vap; + struct ucred *cred; + struct proc *p; +{ + struct filedesc *fdp = p->p_fd; + struct file *fp; + struct stat stb; + int error; + + if (fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) + return (EBADF); + + switch (fp->f_type) { + case DTYPE_FIFO: + case DTYPE_VNODE: + error = VOP_GETATTR((struct vnode *) fp->f_data, vap, cred, p); + 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 &= ~((VEXEC)|(VEXEC>>3)|(VEXEC>>6)); + } + break; + + case DTYPE_PIPE: + case DTYPE_SOCKET: + error = fo_stat(fp, &stb, p); + if (error == 0) { + vattr_null(vap); + vap->va_type = VSOCK; /* XXX pipe? */ + 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; + + default: + panic("fdesc attr"); + break; + } + + return (error); +} + +static int +fdesc_getattr(ap) + struct vop_getattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + 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: + bzero((caddr_t) vap, sizeof(*vap)); + vattr_null(vap); + vap->va_fileid = VTOFDESC(vp)->fd_ix; + + switch (VTOFDESC(vp)->fd_type) { + case Flink: + vap->va_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; + vap->va_type = VLNK; + vap->va_nlink = 1; + vap->va_size = strlen(VTOFDESC(vp)->fd_link); + break; + + case Fctty: + vap->va_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; + vap->va_type = VFIFO; + vap->va_nlink = 1; + vap->va_size = 0; + break; + + default: + vap->va_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; + vap->va_type = VDIR; + vap->va_nlink = 2; + vap->va_size = DEV_BSIZE; + break; + } + vap->va_uid = 0; + vap->va_gid = 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_rdev = 0; + vap->va_bytes = 0; + break; + + case Fdesc: + fd = VTOFDESC(vp)->fd_fd; + error = fdesc_attr(fd, vap, ap->a_cred, ap->a_p); + break; + + default: + panic("fdesc_getattr"); + break; + } + + if (error == 0) + vp->v_type = vap->va_type; + + return (error); +} + +static int +fdesc_setattr(ap) + struct vop_setattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + struct filedesc *fdp = ap->a_p->p_fd; + struct vattr *vap = ap->a_vap; + struct file *fp; + unsigned fd; + int error; + + /* + * Can't mess with the root vnode + */ + switch (VTOFDESC(ap->a_vp)->fd_type) { + case Fdesc: + break; + + case Fctty: + if (vap->va_flags != VNOVAL) + return (EOPNOTSUPP); + return (0); + + default: + return (EACCES); + } + + fd = VTOFDESC(ap->a_vp)->fd_fd; + if (fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) { + return (EBADF); + } + + /* + * Can setattr the underlying vnode, but not sockets! + */ + switch (fp->f_type) { + case DTYPE_FIFO: + case DTYPE_PIPE: + case DTYPE_VNODE: + error = VOP_SETATTR((struct vnode *) fp->f_data, ap->a_vap, ap->a_cred, ap->a_p); + break; + + case DTYPE_SOCKET: + if (vap->va_flags != VNOVAL) + error = EOPNOTSUPP; + else + error = 0; + break; + + default: + error = EBADF; + break; + } + + return (error); +} + +#define UIO_MX 16 + +static struct dirtmp { + u_long d_fileno; + u_short d_reclen; + u_short d_namlen; + char d_name[8]; +} rootent[] = { + { FD_DEVFD, UIO_MX, 2, "fd" }, + { FD_STDIN, UIO_MX, 5, "stdin" }, + { FD_STDOUT, UIO_MX, 6, "stdout" }, + { FD_STDERR, UIO_MX, 6, "stderr" }, + { FD_CTTY, UIO_MX, 3, "tty" }, +}; + +static int +fdesc_readdir(ap) + struct vop_readdir_args /* { + struct vnode *a_vp; + struct uio *a_uio; + struct ucred *a_cred; + int *a_eofflag; + u_long *a_cookies; + int a_ncookies; + } */ *ap; +{ + struct uio *uio = ap->a_uio; + struct filedesc *fdp; + int error, i, off; + + /* + * We don't allow exporting fdesc mounts, and currently local + * requests do not need cookies. + */ + if (ap->a_ncookies) + panic("fdesc_readdir: not hungry"); + + if (VTOFDESC(ap->a_vp)->fd_type != Froot && + VTOFDESC(ap->a_vp)->fd_type != Fdevfd) + panic("fdesc_readdir: not dir"); + + off = (int)uio->uio_offset; + if (off != uio->uio_offset || off < 0 || (u_int)off % UIO_MX != 0 || + uio->uio_resid < UIO_MX) + return (EINVAL); + i = (u_int)off / UIO_MX; + fdp = uio->uio_procp->p_fd; + + if (VTOFDESC(ap->a_vp)->fd_type == Froot) { + struct dirent d; + struct dirent *dp = &d; + struct dirtmp *dt; + + error = 0; + + while (i < sizeof(rootent) / sizeof(rootent[0]) && + uio->uio_resid >= UIO_MX) { + dt = &rootent[i]; + switch (dt->d_fileno) { + case FD_CTTY: + if (cttyvp(uio->uio_procp) == NULL) + continue; + break; + + case FD_STDIN: + case FD_STDOUT: + case FD_STDERR: + if ((dt->d_fileno-FD_STDIN) >= fdp->fd_nfiles) + continue; + if (fdp->fd_ofiles[dt->d_fileno-FD_STDIN] == NULL) + continue; + break; + } + bzero((caddr_t) dp, UIO_MX); + dp->d_fileno = dt->d_fileno; + dp->d_namlen = dt->d_namlen; + dp->d_type = DT_UNKNOWN; + dp->d_reclen = dt->d_reclen; + bcopy(dt->d_name, dp->d_name, dp->d_namlen+1); + error = uiomove((caddr_t) dp, UIO_MX, uio); + if (error) + break; + i++; + } + uio->uio_offset = i * UIO_MX; + return (error); + } + + error = 0; + while (i < fdp->fd_nfiles && uio->uio_resid >= UIO_MX) { + if (fdp->fd_ofiles[i] != NULL) { + struct dirent d; + struct dirent *dp = &d; + + bzero((caddr_t) dp, UIO_MX); + + dp->d_namlen = sprintf(dp->d_name, "%d", i); + dp->d_reclen = UIO_MX; + dp->d_type = DT_UNKNOWN; + dp->d_fileno = i + FD_STDIN; + /* + * And ship to userland + */ + error = uiomove((caddr_t) dp, UIO_MX, uio); + if (error) + break; + } + i++; + } + + uio->uio_offset = i * UIO_MX; + return (error); +} + +static int +fdesc_readlink(ap) + struct vop_readlink_args /* { + struct vnode *a_vp; + struct uio *a_uio; + struct ucred *a_cred; + } */ *ap; +{ + 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); +} + +static int +fdesc_read(ap) + struct vop_read_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap; +{ + int error = EOPNOTSUPP; + + switch (VTOFDESC(ap->a_vp)->fd_type) { + case Fctty: + error = (*ctty_cdevsw.d_read)(devctty, ap->a_uio, ap->a_ioflag); + break; + + default: + error = EOPNOTSUPP; + break; + } + + return (error); +} + +static int +fdesc_write(ap) + struct vop_write_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap; +{ + int error = EOPNOTSUPP; + + switch (VTOFDESC(ap->a_vp)->fd_type) { + case Fctty: + error = (*ctty_cdevsw.d_write)(devctty, ap->a_uio, ap->a_ioflag); + break; + + default: + error = EOPNOTSUPP; + break; + } + + return (error); +} + +static int +fdesc_ioctl(ap) + struct vop_ioctl_args /* { + struct vnode *a_vp; + int a_command; + caddr_t a_data; + int a_fflag; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + int error = EOPNOTSUPP; + + switch (VTOFDESC(ap->a_vp)->fd_type) { + case Fctty: + error = (*ctty_cdevsw.d_ioctl)(devctty, ap->a_command, + ap->a_data, ap->a_fflag, ap->a_p); + break; + + default: + error = EOPNOTSUPP; + break; + } + + return (error); +} + +static int +fdesc_poll(ap) + struct vop_poll_args /* { + struct vnode *a_vp; + int a_events; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + int revents; + + switch (VTOFDESC(ap->a_vp)->fd_type) { + case Fctty: + revents = (*ctty_cdevsw.d_poll)(devctty, ap->a_events, ap->a_p); + break; + + default: + revents = seltrue(0, ap->a_events, ap->a_p); + break; + } + + return (revents); +} + +static int +fdesc_inactive(ap) + struct vop_inactive_args /* { + struct vnode *a_vp; + struct proc *a_p; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + + /* + * Clear out the v_type field to avoid + * nasty things happening in vgone(). + */ + VOP_UNLOCK(vp, 0, ap->a_p); + vp->v_type = VNON; + return (0); +} + +static int +fdesc_reclaim(ap) + struct vop_reclaim_args /* { + struct vnode *a_vp; + } */ *ap; +{ + 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); +} + +/* + * Print out the contents of a /dev/fd vnode. + */ +/* ARGSUSED */ +static int +fdesc_print(ap) + struct vop_print_args /* { + struct vnode *a_vp; + } */ *ap; +{ + + printf("tag VT_NON, fdesc vnode\n"); + return (0); +} + +/* + * /dev/fd "should never get here" operation + */ +static int +fdesc_badop() +{ + + panic("fdesc: bad op"); + /* NOTREACHED */ +} + +static struct vnodeopv_entry_desc fdesc_vnodeop_entries[] = { + { &vop_default_desc, (vop_t *) vop_defaultop }, + { &vop_access_desc, (vop_t *) vop_null }, + { &vop_bmap_desc, (vop_t *) fdesc_badop }, + { &vop_getattr_desc, (vop_t *) fdesc_getattr }, + { &vop_inactive_desc, (vop_t *) fdesc_inactive }, + { &vop_ioctl_desc, (vop_t *) fdesc_ioctl }, + { &vop_lookup_desc, (vop_t *) fdesc_lookup }, + { &vop_open_desc, (vop_t *) fdesc_open }, + { &vop_pathconf_desc, (vop_t *) vop_stdpathconf }, + { &vop_poll_desc, (vop_t *) fdesc_poll }, + { &vop_print_desc, (vop_t *) fdesc_print }, + { &vop_read_desc, (vop_t *) fdesc_read }, + { &vop_readdir_desc, (vop_t *) fdesc_readdir }, + { &vop_readlink_desc, (vop_t *) fdesc_readlink }, + { &vop_reclaim_desc, (vop_t *) fdesc_reclaim }, + { &vop_setattr_desc, (vop_t *) fdesc_setattr }, + { &vop_write_desc, (vop_t *) fdesc_write }, + { NULL, NULL } +}; +static struct vnodeopv_desc fdesc_vnodeop_opv_desc = + { &fdesc_vnodeop_p, fdesc_vnodeop_entries }; + +VNODEOP_SET(fdesc_vnodeop_opv_desc); |