diff options
Diffstat (limited to 'sys/miscfs/nullfs')
-rw-r--r-- | sys/miscfs/nullfs/null.h | 77 | ||||
-rw-r--r-- | sys/miscfs/nullfs/null_subr.c | 292 | ||||
-rw-r--r-- | sys/miscfs/nullfs/null_vfsops.c | 421 | ||||
-rw-r--r-- | sys/miscfs/nullfs/null_vnops.c | 661 |
4 files changed, 1451 insertions, 0 deletions
diff --git a/sys/miscfs/nullfs/null.h b/sys/miscfs/nullfs/null.h new file mode 100644 index 0000000..0b438fe --- /dev/null +++ b/sys/miscfs/nullfs/null.h @@ -0,0 +1,77 @@ +/* + * 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. + * + * @(#)null.h 8.3 (Berkeley) 8/20/94 + * + * $Id: null.h,v 1.6 1997/02/22 09:40:20 peter Exp $ + */ + +struct null_args { + char *target; /* Target of loopback */ +}; + +struct null_mount { + struct mount *nullm_vfs; + struct vnode *nullm_rootvp; /* Reference to root null_node */ +}; + +#ifdef KERNEL +/* + * A cache of vnode references + */ +struct null_node { + LIST_ENTRY(null_node) null_hash; /* Hash list */ + struct vnode *null_lowervp; /* VREFed once */ + struct vnode *null_vnode; /* Back pointer */ +}; + +extern int nullfs_init __P((struct vfsconf *vfsp)); +extern int null_node_create __P((struct mount *mp, struct vnode *target, struct vnode **vpp)); + +#define MOUNTTONULLMOUNT(mp) ((struct null_mount *)((mp)->mnt_data)) +#define VTONULL(vp) ((struct null_node *)(vp)->v_data) +#define NULLTOV(xp) ((xp)->null_vnode) +#ifdef NULLFS_DIAGNOSTIC +extern struct vnode *null_checkvp __P((struct vnode *vp, char *fil, int lno)); +#define NULLVPTOLOWERVP(vp) null_checkvp((vp), __FILE__, __LINE__) +#else +#define NULLVPTOLOWERVP(vp) (VTONULL(vp)->null_lowervp) +#endif + +extern int null_bypass __P((struct vop_generic_args *ap)); + +extern vop_t **null_vnodeop_p; +extern struct vfsops null_vfsops; +#endif /* KERNEL */ diff --git a/sys/miscfs/nullfs/null_subr.c b/sys/miscfs/nullfs/null_subr.c new file mode 100644 index 0000000..0cb47a5 --- /dev/null +++ b/sys/miscfs/nullfs/null_subr.c @@ -0,0 +1,292 @@ +/* + * 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. + * + * @(#)null_subr.c 8.7 (Berkeley) 5/14/95 + * + * $Id: null_subr.c,v 1.10 1997/04/17 11:17:28 kato Exp $ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/vnode.h> +#include <sys/mount.h> +#include <sys/malloc.h> +#include <miscfs/nullfs/null.h> + +#define LOG2_SIZEVNODE 7 /* log2(sizeof struct vnode) */ +#define NNULLNODECACHE 16 + +/* + * Null layer cache: + * Each cache entry holds a reference to the lower vnode + * along with a pointer to the alias vnode. When an + * entry is added the lower vnode is VREF'd. When the + * alias is removed the lower vnode is vrele'd. + */ + +#define NULL_NHASH(vp) \ + (&null_node_hashtbl[(((u_long)vp)>>LOG2_SIZEVNODE) & null_node_hash]) +LIST_HEAD(null_node_hashhead, null_node) *null_node_hashtbl; +u_long null_node_hash; + +static int null_node_alloc __P((struct mount *mp, struct vnode *lowervp, + struct vnode **vpp)); +static struct vnode * + null_node_find __P((struct mount *mp, struct vnode *lowervp)); + +/* + * Initialise cache headers + */ +int +nullfs_init(vfsp) + struct vfsconf *vfsp; +{ + +#ifdef NULLFS_DIAGNOSTIC + printf("nullfs_init\n"); /* printed during system boot */ +#endif + null_node_hashtbl = hashinit(NNULLNODECACHE, M_CACHE, &null_node_hash); + return (0); +} + +/* + * Return a VREF'ed alias for lower vnode if already exists, else 0. + */ +static struct vnode * +null_node_find(mp, lowervp) + struct mount *mp; + struct vnode *lowervp; +{ + struct proc *p = curproc; /* XXX */ + struct null_node_hashhead *hd; + struct null_node *a; + struct vnode *vp; + + /* + * Find hash base, and then search the (two-way) linked + * list looking for a null_node structure which is referencing + * the lower vnode. If found, the increment the null_node + * reference count (but NOT the lower vnode's VREF counter). + */ + hd = NULL_NHASH(lowervp); +loop: + for (a = hd->lh_first; a != 0; a = a->null_hash.le_next) { + if (a->null_lowervp == lowervp && NULLTOV(a)->v_mount == mp) { + vp = NULLTOV(a); + /* + * We need vget for the VXLOCK + * stuff, but we don't want to lock + * the lower node. + */ + if (vget(vp, 0, p)) { + printf ("null_node_find: vget failed.\n"); + goto loop; + }; + return (vp); + } + } + + return NULLVP; +} + + +/* + * Make a new null_node node. + * Vp is the alias vnode, lofsvp is the lower vnode. + * Maintain a reference to (lowervp). + */ +static int +null_node_alloc(mp, lowervp, vpp) + struct mount *mp; + struct vnode *lowervp; + struct vnode **vpp; +{ + struct null_node_hashhead *hd; + struct null_node *xp; + struct vnode *othervp, *vp; + int error; + + /* + * 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(xp, struct null_node *, sizeof(struct null_node), M_TEMP, M_WAITOK); + + error = getnewvnode(VT_NULL, mp, null_vnodeop_p, vpp); + if (error) { + FREE(xp, M_TEMP); + return (error); + } + vp = *vpp; + + vp->v_type = lowervp->v_type; + xp->null_vnode = vp; + vp->v_data = xp; + xp->null_lowervp = lowervp; + /* + * Before we insert our new node onto the hash chains, + * check to see if someone else has beaten us to it. + * (We could have slept in MALLOC.) + */ + othervp = null_node_find(mp, lowervp); + if (othervp) { + FREE(xp, M_TEMP); + vp->v_type = VBAD; /* node is discarded */ + vp->v_usecount = 0; /* XXX */ + *vpp = othervp; + return 0; + }; + VREF(lowervp); /* Extra VREF will be vrele'd in null_node_create */ + hd = NULL_NHASH(lowervp); + LIST_INSERT_HEAD(hd, xp, null_hash); + return 0; +} + + +/* + * Try to find an existing null_node vnode refering + * to it, otherwise make a new null_node vnode which + * contains a reference to the lower vnode. + */ +int +null_node_create(mp, lowervp, newvpp) + struct mount *mp; + struct vnode *lowervp; + struct vnode **newvpp; +{ + struct vnode *aliasvp; + + aliasvp = null_node_find(mp, lowervp); + if (aliasvp) { + /* + * null_node_find has taken another reference + * to the alias vnode. + */ +#ifdef NULLFS_DIAGNOSTIC + vprint("null_node_create: exists", NULLTOV(ap)); +#endif + /* VREF(aliasvp); --- done in null_node_find */ + } else { + int error; + + /* + * Get new vnode. + */ +#ifdef NULLFS_DIAGNOSTIC + printf("null_node_create: create new alias vnode\n"); +#endif + + /* + * Make new vnode reference the null_node. + */ + error = null_node_alloc(mp, lowervp, &aliasvp); + if (error) + return error; + + /* + * aliasvp is already VREF'd by getnewvnode() + */ + } + + vrele(lowervp); + +#ifdef DIAGNOSTIC + if (lowervp->v_usecount < 1) { + /* Should never happen... */ + vprint ("null_node_create: alias ", aliasvp); + vprint ("null_node_create: lower ", lowervp); + panic ("null_node_create: lower has 0 usecount."); + }; +#endif + +#ifdef NULLFS_DIAGNOSTIC + vprint("null_node_create: alias", aliasvp); + vprint("null_node_create: lower", lowervp); +#endif + + *newvpp = aliasvp; + return (0); +} +#ifdef NULLFS_DIAGNOSTIC +struct vnode * +null_checkvp(vp, fil, lno) + struct vnode *vp; + char *fil; + int lno; +{ + struct null_node *a = VTONULL(vp); +#ifdef notyet + /* + * Can't do this check because vop_reclaim runs + * with a funny vop vector. + */ + if (vp->v_op != null_vnodeop_p) { + printf ("null_checkvp: on non-null-node\n"); + while (null_checkvp_barrier) /*WAIT*/ ; + panic("null_checkvp"); + }; +#endif + if (a->null_lowervp == NULLVP) { + /* Should never happen */ + int i; u_long *p; + printf("vp = %x, ZERO ptr\n", vp); + for (p = (u_long *) a, i = 0; i < 8; i++) + printf(" %x", p[i]); + printf("\n"); + /* wait for debugger */ + while (null_checkvp_barrier) /*WAIT*/ ; + panic("null_checkvp"); + } + if (a->null_lowervp->v_usecount < 1) { + int i; u_long *p; + printf("vp = %x, unref'ed lowervp\n", vp); + for (p = (u_long *) a, i = 0; i < 8; i++) + printf(" %x", p[i]); + printf("\n"); + /* wait for debugger */ + while (null_checkvp_barrier) /*WAIT*/ ; + panic ("null with unref'ed lowervp"); + }; +#ifdef notyet + printf("null %x/%d -> %x/%d [%s, %d]\n", + NULLTOV(a), NULLTOV(a)->v_usecount, + a->null_lowervp, a->null_lowervp->v_usecount, + fil, lno); +#endif + return a->null_lowervp; +} +#endif diff --git a/sys/miscfs/nullfs/null_vfsops.c b/sys/miscfs/nullfs/null_vfsops.c new file mode 100644 index 0000000..40e2a19 --- /dev/null +++ b/sys/miscfs/nullfs/null_vfsops.c @@ -0,0 +1,421 @@ +/* + * Copyright (c) 1992, 1993, 1995 + * 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. + * + * @(#)null_vfsops.c 8.2 (Berkeley) 1/21/94 + * + * @(#)lofs_vfsops.c 1.2 (Berkeley) 6/18/92 + * $Id: null_vfsops.c,v 1.17 1997/04/19 06:03:30 kato Exp $ + */ + +/* + * Null Layer + * (See null_vnops.c for a description of what this does.) + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/proc.h> +#include <sys/vnode.h> +#include <sys/mount.h> +#include <sys/namei.h> +#include <sys/malloc.h> +#include <miscfs/nullfs/null.h> + +static int nullfs_fhtovp __P((struct mount *mp, struct fid *fidp, + struct mbuf *nam, struct vnode **vpp, + int *exflagsp, struct ucred **credanonp)); +static int nullfs_mount __P((struct mount *mp, char *path, caddr_t data, + struct nameidata *ndp, struct proc *p)); +static int nullfs_quotactl __P((struct mount *mp, int cmd, uid_t uid, + caddr_t arg, struct proc *p)); +static int nullfs_root __P((struct mount *mp, struct vnode **vpp)); +static int nullfs_start __P((struct mount *mp, int flags, struct proc *p)); +static int nullfs_statfs __P((struct mount *mp, struct statfs *sbp, + struct proc *p)); +static int nullfs_sync __P((struct mount *mp, int waitfor, + struct ucred *cred, struct proc *p)); +static int nullfs_unmount __P((struct mount *mp, int mntflags, + struct proc *p)); +static int nullfs_vget __P((struct mount *mp, ino_t ino, + struct vnode **vpp)); +static int nullfs_vptofh __P((struct vnode *vp, struct fid *fhp)); + +/* + * Mount null layer + */ +static int +nullfs_mount(mp, path, data, ndp, p) + struct mount *mp; + char *path; + caddr_t data; + struct nameidata *ndp; + struct proc *p; +{ + int error = 0; + struct null_args args; + struct vnode *lowerrootvp, *vp; + struct vnode *nullm_rootvp; + struct null_mount *xmp; + u_int size; + int isvnunlocked = 0; + +#ifdef NULLFS_DIAGNOSTIC + printf("nullfs_mount(mp = %x)\n", mp); +#endif + + /* + * Update is a no-op + */ + if (mp->mnt_flag & MNT_UPDATE) { + return (EOPNOTSUPP); + /* return VFS_MOUNT(MOUNTTONULLMOUNT(mp)->nullm_vfs, path, data, ndp, p);*/ + } + + /* + * Get argument + */ + error = copyin(data, (caddr_t)&args, sizeof(struct null_args)); + if (error) + return (error); + + /* + * Unlock lower node to avoid deadlock. + * (XXX) VOP_ISLOCKED is needed? + */ + if ((mp->mnt_vnodecovered->v_op == null_vnodeop_p) && + VOP_ISLOCKED(mp->mnt_vnodecovered)) { + VOP_UNLOCK(mp->mnt_vnodecovered, 0, p); + isvnunlocked = 1; + } + /* + * Find lower node + */ + NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT|LOCKLEAF, + UIO_USERSPACE, args.target, p); + error = namei(ndp); + /* + * Re-lock vnode. + */ + if (isvnunlocked && !VOP_ISLOCKED(mp->mnt_vnodecovered)) + vn_lock(mp->mnt_vnodecovered, LK_EXCLUSIVE | LK_RETRY, p); + + if (error) + return (error); + + /* + * Sanity check on lower vnode + */ + lowerrootvp = ndp->ni_vp; + + vrele(ndp->ni_dvp); + ndp->ni_dvp = NULLVP; + + /* + * Check multi null mount to avoid `lock against myself' panic. + */ + if (lowerrootvp == VTONULL(mp->mnt_vnodecovered)->null_lowervp) { +#ifdef DIAGNOSTIC + printf("nullfs_mount: multi null mount?\n"); +#endif + return (EDEADLK); + } + + xmp = (struct null_mount *) malloc(sizeof(struct null_mount), + M_UFSMNT, M_WAITOK); /* XXX */ + + /* + * Save reference to underlying FS + */ + xmp->nullm_vfs = lowerrootvp->v_mount; + + /* + * Save reference. Each mount also holds + * a reference on the root vnode. + */ + error = null_node_create(mp, lowerrootvp, &vp); + /* + * Unlock the node (either the lower or the alias) + */ + VOP_UNLOCK(vp, 0, p); + /* + * Make sure the node alias worked + */ + if (error) { + vrele(lowerrootvp); + free(xmp, M_UFSMNT); /* XXX */ + return (error); + } + + /* + * Keep a held reference to the root vnode. + * It is vrele'd in nullfs_unmount. + */ + nullm_rootvp = vp; + nullm_rootvp->v_flag |= VROOT; + xmp->nullm_rootvp = nullm_rootvp; + if (NULLVPTOLOWERVP(nullm_rootvp)->v_mount->mnt_flag & MNT_LOCAL) + mp->mnt_flag |= MNT_LOCAL; + mp->mnt_data = (qaddr_t) xmp; + vfs_getnewfsid(mp); + + (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size); + bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); + (void) copyinstr(args.target, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, + &size); + bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); +#ifdef NULLFS_DIAGNOSTIC + printf("nullfs_mount: lower %s, alias at %s\n", + mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); +#endif + return (0); +} + +/* + * VFS start. Nothing needed here - the start routine + * on the underlying filesystem will have been called + * when that filesystem was mounted. + */ +static int +nullfs_start(mp, flags, p) + struct mount *mp; + int flags; + struct proc *p; +{ + return (0); + /* return VFS_START(MOUNTTONULLMOUNT(mp)->nullm_vfs, flags, p); */ +} + +/* + * Free reference to null layer + */ +static int +nullfs_unmount(mp, mntflags, p) + struct mount *mp; + int mntflags; + struct proc *p; +{ + struct vnode *nullm_rootvp = MOUNTTONULLMOUNT(mp)->nullm_rootvp; + int error; + int flags = 0; + +#ifdef NULLFS_DIAGNOSTIC + printf("nullfs_unmount(mp = %x)\n", mp); +#endif + + if (mntflags & MNT_FORCE) + flags |= FORCECLOSE; + + /* + * Clear out buffer cache. I don't think we + * ever get anything cached at this level at the + * moment, but who knows... + */ +#if 0 + mntflushbuf(mp, 0); + if (mntinvalbuf(mp, 1)) + return (EBUSY); +#endif + if (nullm_rootvp->v_usecount > 1) + return (EBUSY); + error = vflush(mp, nullm_rootvp, flags); + if (error) + return (error); + +#ifdef NULLFS_DIAGNOSTIC + vprint("alias root of lower", nullm_rootvp); +#endif + /* + * Release reference on underlying root vnode + */ + vrele(nullm_rootvp); + /* + * And blow it away for future re-use + */ + vgone(nullm_rootvp); + /* + * Finally, throw away the null_mount structure + */ + free(mp->mnt_data, M_UFSMNT); /* XXX */ + mp->mnt_data = 0; + return 0; +} + +static int +nullfs_root(mp, vpp) + struct mount *mp; + struct vnode **vpp; +{ + struct proc *p = curproc; /* XXX */ + struct vnode *vp; + +#ifdef NULLFS_DIAGNOSTIC + printf("nullfs_root(mp = %x, vp = %x->%x)\n", mp, + MOUNTTONULLMOUNT(mp)->nullm_rootvp, + NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp) + ); +#endif + + /* + * Return locked reference to root. + */ + vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp; + VREF(vp); + if (VOP_ISLOCKED(vp)) { + /* + * XXX + * Should we check type of node? + */ +#ifdef DIAGNOSTIC + printf("nullfs_root: multi null mount?\n"); +#endif + vrele(vp); + return (EDEADLK); + } else + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + *vpp = vp; + return 0; +} + +static int +nullfs_quotactl(mp, cmd, uid, arg, p) + struct mount *mp; + int cmd; + uid_t uid; + caddr_t arg; + struct proc *p; +{ + return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg, p); +} + +static int +nullfs_statfs(mp, sbp, p) + struct mount *mp; + struct statfs *sbp; + struct proc *p; +{ + int error; + struct statfs mstat; + +#ifdef NULLFS_DIAGNOSTIC + printf("nullfs_statfs(mp = %x, vp = %x->%x)\n", mp, + MOUNTTONULLMOUNT(mp)->nullm_rootvp, + NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp) + ); +#endif + + bzero(&mstat, sizeof(mstat)); + + error = VFS_STATFS(MOUNTTONULLMOUNT(mp)->nullm_vfs, &mstat, p); + if (error) + return (error); + + /* now copy across the "interesting" information and fake the rest */ + sbp->f_type = mstat.f_type; + sbp->f_flags = mstat.f_flags; + sbp->f_bsize = mstat.f_bsize; + sbp->f_iosize = mstat.f_iosize; + sbp->f_blocks = mstat.f_blocks; + sbp->f_bfree = mstat.f_bfree; + sbp->f_bavail = mstat.f_bavail; + sbp->f_files = mstat.f_files; + sbp->f_ffree = mstat.f_ffree; + if (sbp != &mp->mnt_stat) { + bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); + bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); + bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); + } + return (0); +} + +static int +nullfs_sync(mp, waitfor, cred, p) + struct mount *mp; + int waitfor; + struct ucred *cred; + struct proc *p; +{ + /* + * XXX - Assumes no data cached at null layer. + */ + return (0); +} + +static int +nullfs_vget(mp, ino, vpp) + struct mount *mp; + ino_t ino; + struct vnode **vpp; +{ + + return VFS_VGET(MOUNTTONULLMOUNT(mp)->nullm_vfs, ino, vpp); +} + +static int +nullfs_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp) + struct mount *mp; + struct fid *fidp; + struct mbuf *nam; + struct vnode **vpp; + int *exflagsp; + struct ucred**credanonp; +{ + + return VFS_FHTOVP(MOUNTTONULLMOUNT(mp)->nullm_vfs, fidp, nam, vpp, exflagsp,credanonp); +} + +static int +nullfs_vptofh(vp, fhp) + struct vnode *vp; + struct fid *fhp; +{ + return VFS_VPTOFH(NULLVPTOLOWERVP(vp), fhp); +} + +static struct vfsops null_vfsops = { + nullfs_mount, + nullfs_start, + nullfs_unmount, + nullfs_root, + nullfs_quotactl, + nullfs_statfs, + nullfs_sync, + nullfs_vget, + nullfs_fhtovp, + nullfs_vptofh, + nullfs_init, +}; + +VFS_SET(null_vfsops, null, MOUNT_NULL, VFCF_LOOPBACK); diff --git a/sys/miscfs/nullfs/null_vnops.c b/sys/miscfs/nullfs/null_vnops.c new file mode 100644 index 0000000..04e96d0 --- /dev/null +++ b/sys/miscfs/nullfs/null_vnops.c @@ -0,0 +1,661 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * John Heidemann of the UCLA Ficus project. + * + * 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. + * + * @(#)null_vnops.c 8.6 (Berkeley) 5/27/95 + * + * Ancestors: + * @(#)lofs_vnops.c 1.2 (Berkeley) 6/18/92 + * $Id: null_vnops.c,v 1.20 1997/06/26 17:12:59 alex Exp $ + * ...and... + * @(#)null_vnodeops.c 1.20 92/07/07 UCLA Ficus project + * + * $Id: null_vnops.c,v 1.20 1997/06/26 17:12:59 alex Exp $ + */ + +/* + * Null Layer + * + * (See mount_null(8) for more information.) + * + * The null layer duplicates a portion of the file system + * name space under a new name. In this respect, it is + * similar to the loopback file system. It differs from + * the loopback fs in two respects: it is implemented using + * a stackable layers techniques, and it's "null-node"s stack above + * all lower-layer vnodes, not just over directory vnodes. + * + * The null layer has two purposes. First, it serves as a demonstration + * of layering by proving a layer which does nothing. (It actually + * does everything the loopback file system does, which is slightly + * more than nothing.) Second, the null layer can serve as a prototype + * layer. Since it provides all necessary layer framework, + * new file system layers can be created very easily be starting + * with a null layer. + * + * The remainder of this man page examines the null layer as a basis + * for constructing new layers. + * + * + * INSTANTIATING NEW NULL LAYERS + * + * New null layers are created with mount_null(8). + * Mount_null(8) takes two arguments, the pathname + * of the lower vfs (target-pn) and the pathname where the null + * layer will appear in the namespace (alias-pn). After + * the null layer is put into place, the contents + * of target-pn subtree will be aliased under alias-pn. + * + * + * OPERATION OF A NULL LAYER + * + * The null layer is the minimum file system layer, + * simply bypassing all possible operations to the lower layer + * for processing there. The majority of its activity centers + * on the bypass routine, through which nearly all vnode operations + * pass. + * + * The bypass routine accepts arbitrary vnode operations for + * handling by the lower layer. It begins by examing vnode + * operation arguments and replacing any null-nodes by their + * lower-layer equivlants. It then invokes the operation + * on the lower layer. Finally, it replaces the null-nodes + * in the arguments and, if a vnode is return by the operation, + * stacks a null-node on top of the returned vnode. + * + * Although bypass handles most operations, vop_getattr, vop_lock, + * vop_unlock, vop_inactive, vop_reclaim, and vop_print are not + * bypassed. Vop_getattr must change the fsid being returned. + * Vop_lock and vop_unlock must handle any locking for the + * current vnode as well as pass the lock request down. + * Vop_inactive and vop_reclaim are not bypassed so that + * they can handle freeing null-layer specific data. Vop_print + * is not bypassed to avoid excessive debugging information. + * Also, certain vnode operations change the locking state within + * the operation (create, mknod, remove, link, rename, mkdir, rmdir, + * and symlink). Ideally these operations should not change the + * lock state, but should be changed to let the caller of the + * function unlock them. Otherwise all intermediate vnode layers + * (such as union, umapfs, etc) must catch these functions to do + * the necessary locking at their layer. + * + * + * INSTANTIATING VNODE STACKS + * + * Mounting associates the null layer with a lower layer, + * effect stacking two VFSes. Vnode stacks are instead + * created on demand as files are accessed. + * + * The initial mount creates a single vnode stack for the + * root of the new null layer. All other vnode stacks + * are created as a result of vnode operations on + * this or other null vnode stacks. + * + * New vnode stacks come into existance as a result of + * an operation which returns a vnode. + * The bypass routine stacks a null-node above the new + * vnode before returning it to the caller. + * + * For example, imagine mounting a null layer with + * "mount_null /usr/include /dev/layer/null". + * Changing directory to /dev/layer/null will assign + * the root null-node (which was created when the null layer was mounted). + * Now consider opening "sys". A vop_lookup would be + * done on the root null-node. This operation would bypass through + * to the lower layer which would return a vnode representing + * the UFS "sys". Null_bypass then builds a null-node + * aliasing the UFS "sys" and returns this to the caller. + * Later operations on the null-node "sys" will repeat this + * process when constructing other vnode stacks. + * + * + * CREATING OTHER FILE SYSTEM LAYERS + * + * One of the easiest ways to construct new file system layers is to make + * a copy of the null layer, rename all files and variables, and + * then begin modifing the copy. Sed can be used to easily rename + * all variables. + * + * The umap layer is an example of a layer descended from the + * null layer. + * + * + * INVOKING OPERATIONS ON LOWER LAYERS + * + * There are two techniques to invoke operations on a lower layer + * when the operation cannot be completely bypassed. Each method + * is appropriate in different situations. In both cases, + * it is the responsibility of the aliasing layer to make + * the operation arguments "correct" for the lower layer + * by mapping an vnode arguments to the lower layer. + * + * The first approach is to call the aliasing layer's bypass routine. + * This method is most suitable when you wish to invoke the operation + * currently being handled on the lower layer. It has the advantage + * that the bypass routine already must do argument mapping. + * An example of this is null_getattrs in the null layer. + * + * A second approach is to directly invoke vnode operations on + * the lower layer with the VOP_OPERATIONNAME interface. + * The advantage of this method is that it is easy to invoke + * arbitrary operations on the lower layer. The disadvantage + * is that vnode arguments must be manualy mapped. + * + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/sysctl.h> +#include <sys/vnode.h> +#include <sys/mount.h> +#include <sys/namei.h> +#include <sys/malloc.h> +#include <sys/buf.h> +#include <miscfs/nullfs/null.h> + +static int null_bug_bypass = 0; /* for debugging: enables bypass printf'ing */ +SYSCTL_INT(_debug, OID_AUTO, nullfs_bug_bypass, CTLFLAG_RW, + &null_bug_bypass, 0, ""); + +static int null_access __P((struct vop_access_args *ap)); +static int null_bwrite __P((struct vop_bwrite_args *ap)); +static int null_getattr __P((struct vop_getattr_args *ap)); +static int null_inactive __P((struct vop_inactive_args *ap)); +static int null_lock __P((struct vop_lock_args *ap)); +static int null_lookup __P((struct vop_lookup_args *ap)); +static int null_print __P((struct vop_print_args *ap)); +static int null_reclaim __P((struct vop_reclaim_args *ap)); +static int null_setattr __P((struct vop_setattr_args *ap)); +static int null_strategy __P((struct vop_strategy_args *ap)); +static int null_unlock __P((struct vop_unlock_args *ap)); + +/* + * This is the 10-Apr-92 bypass routine. + * This version has been optimized for speed, throwing away some + * safety checks. It should still always work, but it's not as + * robust to programmer errors. + * Define SAFETY to include some error checking code. + * + * In general, we map all vnodes going down and unmap them on the way back. + * As an exception to this, vnodes can be marked "unmapped" by setting + * the Nth bit in operation's vdesc_flags. + * + * Also, some BSD vnode operations have the side effect of vrele'ing + * their arguments. With stacking, the reference counts are held + * by the upper node, not the lower one, so we must handle these + * side-effects here. This is not of concern in Sun-derived systems + * since there are no such side-effects. + * + * This makes the following assumptions: + * - only one returned vpp + * - no INOUT vpp's (Sun's vop_open has one of these) + * - the vnode operation vector of the first vnode should be used + * to determine what implementation of the op should be invoked + * - all mapped vnodes are of our vnode-type (NEEDSWORK: + * problems on rmdir'ing mount points and renaming?) + */ +int +null_bypass(ap) + struct vop_generic_args /* { + struct vnodeop_desc *a_desc; + <other random data follows, presumably> + } */ *ap; +{ + register struct vnode **this_vp_p; + int error; + struct vnode *old_vps[VDESC_MAX_VPS]; + struct vnode **vps_p[VDESC_MAX_VPS]; + struct vnode ***vppp; + struct vnodeop_desc *descp = ap->a_desc; + int reles, i; + + if (null_bug_bypass) + printf ("null_bypass: %s\n", descp->vdesc_name); + +#ifdef SAFETY + /* + * We require at least one vp. + */ + if (descp->vdesc_vp_offsets == NULL || + descp->vdesc_vp_offsets[0] == VDESC_NO_OFFSET) + panic ("null_bypass: no vp's in map."); +#endif + + /* + * Map the vnodes going in. + * Later, we'll invoke the operation based on + * the first mapped vnode's operation vector. + */ + reles = descp->vdesc_flags; + for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) { + if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET) + break; /* bail out at end of list */ + vps_p[i] = this_vp_p = + VOPARG_OFFSETTO(struct vnode**,descp->vdesc_vp_offsets[i],ap); + /* + * We're not guaranteed that any but the first vnode + * are of our type. Check for and don't map any + * that aren't. (We must always map first vp or vclean fails.) + */ + if (i && (*this_vp_p == NULLVP || + (*this_vp_p)->v_op != null_vnodeop_p)) { + old_vps[i] = NULLVP; + } else { + old_vps[i] = *this_vp_p; + *(vps_p[i]) = NULLVPTOLOWERVP(*this_vp_p); + /* + * XXX - Several operations have the side effect + * of vrele'ing their vp's. We must account for + * that. (This should go away in the future.) + */ + if (reles & 1) + VREF(*this_vp_p); + } + + } + + /* + * Call the operation on the lower layer + * with the modified argument structure. + */ + error = VCALL(*(vps_p[0]), descp->vdesc_offset, ap); + + /* + * Maintain the illusion of call-by-value + * by restoring vnodes in the argument structure + * to their original value. + */ + reles = descp->vdesc_flags; + for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) { + if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET) + break; /* bail out at end of list */ + if (old_vps[i]) { + *(vps_p[i]) = old_vps[i]; + if (reles & 1) + vrele(*(vps_p[i])); + } + } + + /* + * Map the possible out-going vpp + * (Assumes that the lower layer always returns + * a VREF'ed vpp unless it gets an error.) + */ + if (descp->vdesc_vpp_offset != VDESC_NO_OFFSET && + !(descp->vdesc_flags & VDESC_NOMAP_VPP) && + !error) { + /* + * XXX - even though some ops have vpp returned vp's, + * several ops actually vrele this before returning. + * We must avoid these ops. + * (This should go away when these ops are regularized.) + */ + if (descp->vdesc_flags & VDESC_VPP_WILLRELE) + goto out; + vppp = VOPARG_OFFSETTO(struct vnode***, + descp->vdesc_vpp_offset,ap); + error = null_node_create(old_vps[0]->v_mount, **vppp, *vppp); + } + + out: + return (error); +} + +/* + * We have to carry on the locking protocol on the null layer vnodes + * as we progress through the tree. We also have to enforce read-only + * if this layer is mounted read-only. + */ +static int +null_lookup(ap) + struct vop_lookup_args /* { + struct vnode * a_dvp; + struct vnode ** a_vpp; + struct componentname * a_cnp; + } */ *ap; +{ + struct componentname *cnp = ap->a_cnp; + struct proc *p = cnp->cn_proc; + int flags = cnp->cn_flags; + struct vop_lock_args lockargs; + struct vop_unlock_args unlockargs; + struct vnode *dvp, *vp; + int error; + + if ((flags & ISLASTCN) && (ap->a_dvp->v_mount->mnt_flag & MNT_RDONLY) && + (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) + return (EROFS); + error = null_bypass((struct vop_generic_args *)ap); + if (error == EJUSTRETURN && (flags & ISLASTCN) && + (ap->a_dvp->v_mount->mnt_flag & MNT_RDONLY) && + (cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME)) + error = EROFS; + /* + * We must do the same locking and unlocking at this layer as + * is done in the layers below us. We could figure this out + * based on the error return and the LASTCN, LOCKPARENT, and + * LOCKLEAF flags. However, it is more expidient to just find + * out the state of the lower level vnodes and set ours to the + * same state. + */ + dvp = ap->a_dvp; + vp = *ap->a_vpp; + if (dvp == vp) + return (error); + if (!VOP_ISLOCKED(dvp)) { + unlockargs.a_vp = dvp; + unlockargs.a_flags = 0; + unlockargs.a_p = p; + vop_nounlock(&unlockargs); + } + if (vp != NULLVP && VOP_ISLOCKED(vp)) { + lockargs.a_vp = vp; + lockargs.a_flags = LK_SHARED; + lockargs.a_p = p; + vop_nolock(&lockargs); + } + return (error); +} + +/* + * Setattr call. Disallow write attempts if the layer is mounted read-only. + */ +int +null_setattr(ap) + struct vop_setattr_args /* { + struct vnodeop_desc *a_desc; + 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; + + if ((vap->va_flags != VNOVAL || vap->va_uid != (uid_t)VNOVAL || + vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL || + vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL) && + (vp->v_mount->mnt_flag & MNT_RDONLY)) + return (EROFS); + if (vap->va_size != VNOVAL) { + switch (vp->v_type) { + case VDIR: + return (EISDIR); + case VCHR: + case VBLK: + case VSOCK: + case VFIFO: + return (0); + case VREG: + case VLNK: + default: + /* + * Disallow write attempts if the filesystem is + * mounted read-only. + */ + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); + } + } + return (null_bypass((struct vop_generic_args *)ap)); +} + +/* + * We handle getattr only to change the fsid. + */ +static int +null_getattr(ap) + struct vop_getattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + int error; + + if (error = null_bypass((struct vop_generic_args *)ap)) + return (error); + /* Requires that arguments be restored. */ + ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0]; + return (0); +} + +static int +null_access(ap) + struct vop_access_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + mode_t mode = ap->a_mode; + + /* + * Disallow write attempts on read-only layers; + * unless the file is a socket, fifo, or a block or + * character device resident on the file system. + */ + if (mode & VWRITE) { + switch (vp->v_type) { + case VDIR: + case VLNK: + case VREG: + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); + break; + } + } + return (null_bypass((struct vop_generic_args *)ap)); +} + +/* + * We need to process our own vnode lock and then clear the + * interlock flag as it applies only to our vnode, not the + * vnodes below us on the stack. + */ +static int +null_lock(ap) + struct vop_lock_args /* { + struct vnode *a_vp; + int a_flags; + struct proc *a_p; + } */ *ap; +{ + + vop_nolock(ap); + if ((ap->a_flags & LK_TYPE_MASK) == LK_DRAIN) + return (0); + ap->a_flags &= ~LK_INTERLOCK; + return (null_bypass((struct vop_generic_args *)ap)); +} + +/* + * We need to process our own vnode unlock and then clear the + * interlock flag as it applies only to our vnode, not the + * vnodes below us on the stack. + */ +static int +null_unlock(ap) + struct vop_unlock_args /* { + struct vnode *a_vp; + int a_flags; + struct proc *a_p; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + + vop_nounlock(ap); + ap->a_flags &= ~LK_INTERLOCK; + return (null_bypass((struct vop_generic_args *)ap)); +} + +static int +null_inactive(ap) + struct vop_inactive_args /* { + struct vnode *a_vp; + struct proc *a_p; + } */ *ap; +{ + /* + * Do nothing (and _don't_ bypass). + * Wait to vrele lowervp until reclaim, + * so that until then our null_node is in the + * cache and reusable. + * + * NEEDSWORK: Someday, consider inactive'ing + * the lowervp and then trying to reactivate it + * with capabilities (v_id) + * like they do in the name lookup cache code. + * That's too much work for now. + */ + VOP_UNLOCK(ap->a_vp, 0, ap->a_p); + return (0); +} + +static int +null_reclaim(ap) + struct vop_reclaim_args /* { + struct vnode *a_vp; + struct proc *a_p; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct null_node *xp = VTONULL(vp); + struct vnode *lowervp = xp->null_lowervp; + + /* + * Note: in vop_reclaim, vp->v_op == dead_vnodeop_p, + * so we can't call VOPs on ourself. + */ + /* After this assignment, this node will not be re-used. */ + xp->null_lowervp = NULLVP; + LIST_REMOVE(xp, null_hash); + FREE(vp->v_data, M_TEMP); + vp->v_data = NULL; + vrele (lowervp); + return (0); +} + +static int +null_print(ap) + struct vop_print_args /* { + struct vnode *a_vp; + } */ *ap; +{ + register struct vnode *vp = ap->a_vp; + printf ("\ttag VT_NULLFS, vp=%p, lowervp=%p\n", vp, NULLVPTOLOWERVP(vp)); + return (0); +} + +/* + * XXX - vop_strategy must be hand coded because it has no + * vnode in its arguments. + * This goes away with a merged VM/buffer cache. + */ +static int +null_strategy(ap) + struct vop_strategy_args /* { + struct buf *a_bp; + } */ *ap; +{ + struct buf *bp = ap->a_bp; + int error; + struct vnode *savedvp; + + savedvp = bp->b_vp; + bp->b_vp = NULLVPTOLOWERVP(bp->b_vp); + + error = VOP_STRATEGY(bp); + + bp->b_vp = savedvp; + + return (error); +} + +/* + * XXX - like vop_strategy, vop_bwrite must be hand coded because it has no + * vnode in its arguments. + * This goes away with a merged VM/buffer cache. + */ +static int +null_bwrite(ap) + struct vop_bwrite_args /* { + struct buf *a_bp; + } */ *ap; +{ + struct buf *bp = ap->a_bp; + int error; + struct vnode *savedvp; + + savedvp = bp->b_vp; + bp->b_vp = NULLVPTOLOWERVP(bp->b_vp); + + error = VOP_BWRITE(bp); + + bp->b_vp = savedvp; + + return (error); +} + +/* + * Global vfs data structures + */ +vop_t **null_vnodeop_p; +static struct vnodeopv_entry_desc null_vnodeop_entries[] = { + { &vop_default_desc, (vop_t *)null_bypass }, + + { &vop_lookup_desc, (vop_t *)null_lookup }, + { &vop_setattr_desc, (vop_t *)null_setattr }, + { &vop_getattr_desc, (vop_t *)null_getattr }, + { &vop_access_desc, (vop_t *)null_access }, + { &vop_lock_desc, (vop_t *)null_lock }, + { &vop_unlock_desc, (vop_t *)null_unlock }, + { &vop_inactive_desc, (vop_t *)null_inactive }, + { &vop_reclaim_desc, (vop_t *)null_reclaim }, + { &vop_print_desc, (vop_t *)null_print }, + + { &vop_strategy_desc, (vop_t *)null_strategy }, + { &vop_bwrite_desc, (vop_t *)null_bwrite }, + + { NULL, NULL } +}; +static struct vnodeopv_desc null_vnodeop_opv_desc = + { &null_vnodeop_p, null_vnodeop_entries }; + +VNODEOP_SET(null_vnodeop_opv_desc); |