first draft
authorJohn Heidemann <johnh@ucbvax.Berkeley.EDU>
Wed, 8 Jul 1992 08:46:39 +0000 (00:46 -0800)
committerJohn Heidemann <johnh@ucbvax.Berkeley.EDU>
Wed, 8 Jul 1992 08:46:39 +0000 (00:46 -0800)
SCCS-vsn: sys/miscfs/nullfs/null.h 1.3
SCCS-vsn: sys/miscfs/nullfs/null_subr.c 1.3
SCCS-vsn: sys/miscfs/nullfs/null_vfsops.c 1.3
SCCS-vsn: sys/miscfs/nullfs/null_vnops.c 1.3

usr/src/sys/miscfs/nullfs/null_subr.c
usr/src/sys/miscfs/nullfs/null_vfsops.c
usr/src/sys/miscfs/nullfs/null_vnops.c

index 1b474ff..d31ff93 100644 (file)
@@ -101,6 +101,12 @@ null_node_alloc(vp, targetvp)
 }
 
 #ifdef NULLFS_DIAGNOSTIC
 }
 
 #ifdef NULLFS_DIAGNOSTIC
+/*
+ * NEEDSWORK:  The ability to set lowervp to null here
+ * implies that one can never count on lowervp staying null
+ * (even if vp is locked).  This seems quite bad.  Think
+ * about these things.
+ */
 void
 null_node_flushmp (mp)
        struct mount *mp;
 void
 null_node_flushmp (mp)
        struct mount *mp;
@@ -173,8 +179,13 @@ null_node_find(mp, targetvp)
        return (0);
 }
 
        return (0);
 }
 
-static int
-null_node_alias(mp, targetvp, newvpp)
+/*
+ * Try to find an existing null_node vnode refering
+ * to it, otherwise make a new null_node vnode which
+ * contains a reference to the target vnode.
+ */
+int
+null_node_create(mp, targetvp, newvpp)
        struct mount *mp;
        struct vnode *targetvp;
        struct vnode **newvpp;
        struct mount *mp;
        struct vnode *targetvp;
        struct vnode **newvpp;
@@ -235,40 +246,6 @@ null_node_alias(mp, targetvp, newvpp)
        *newvpp = aliasvp;
        return (0);
 }
        *newvpp = aliasvp;
        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 target vnode.
- */
-make_null_node(mp, vpp)
-       struct mount *mp;
-       struct vnode **vpp;
-{
-       int error;
-       struct vnode *aliasvp;
-       struct vnode *targetvp;
-
-#ifdef NULLFS_DIAGNOSTIC
-       printf("make_null_node(mp = %x, vp = %x\n", mp, *vpp);
-#endif
-
-       /*
-        * (targetvp) is locked at this point.
-        */
-       targetvp = *vpp;
-
-#ifdef NULLFS_DIAGNOSTIC
-       if (targetvp == 0)
-               panic("make_null_node: null vp");
-#endif
-
-       /*
-        * Try to find an existing reference to the target vnodes.
-        */
-       return (null_node_alias(mp, targetvp, vpp));
-}
-
 #ifdef NULLFS_DIAGNOSTIC
 struct vnode *
 null_checkvp(vp, fil, lno)
 #ifdef NULLFS_DIAGNOSTIC
 struct vnode *
 null_checkvp(vp, fil, lno)
@@ -278,19 +255,18 @@ null_checkvp(vp, fil, lno)
 {
        struct null_node *a = VTONULLNODE(vp);
        if (a->null_lowervp == 0) {
 {
        struct null_node *a = VTONULLNODE(vp);
        if (a->null_lowervp == 0) {
+               /* Should never happen */
                int i; u_long *p;
                printf("vp = %x, ZERO ptr\n", vp);
                int i; u_long *p;
                printf("vp = %x, ZERO ptr\n", vp);
-#ifdef notdef
                for (p = (u_long *) a, i = 0; i < 8; i++)
                        printf(" %x", p[i]);
                printf("\n");
                DELAY(2000000);
                panic("null_checkvp");
                for (p = (u_long *) a, i = 0; i < 8; i++)
                        printf(" %x", p[i]);
                printf("\n");
                DELAY(2000000);
                panic("null_checkvp");
-#endif
        }
        }
-       printf("aliasvp %x/%d -> %x/%d [%s, %d]\n",
+       printf("nullvp %x/%d -> %x/%d [%s, %d]\n",
                a->null_vnode, a->null_vnode->v_usecount,
                a->null_vnode, a->null_vnode->v_usecount,
-               a->null_lowervp, a->null_lowervp ? a->null_lowervp->v_usecount : -42,
+               a->null_lowervp, a->null_lowervp->v_usecount,
                fil, lno);
        return a->null_lowervp;
 }
                fil, lno);
        return a->null_lowervp;
 }
index 08cd0b4..587bb5b 100644 (file)
@@ -8,13 +8,15 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)null_vfsops.c       1.2 (Berkeley) 6/18/92
+ *     @(#)null_vfsops.c       1.3 (Berkeley) %G%
  *
  *
- * $Id: null_vfsops.c,v 1.9 1992/05/30 10:26:24 jsp Exp jsp $
+ * @(#)lofs_vfsops.c   1.2 (Berkeley) 6/18/92
+ * $Id: lofs_vfsops.c,v 1.9 1992/05/30 10:26:24 jsp Exp jsp $
  */
 
 /*
  */
 
 /*
- * Null layer Filesystem
+ * Null Layer
+ * (See null_vnops.c for a description of what this does.)
  */
 
 #include <sys/param.h>
  */
 
 #include <sys/param.h>
@@ -28,7 +30,7 @@
 #include <lofs/lofs.h>
 
 /*
 #include <lofs/lofs.h>
 
 /*
- * Mount loopback copy of existing name space
+ * Mount null layer
  */
 nullfs_mount(mp, path, data, ndp, p)
        struct mount *mp;
  */
 nullfs_mount(mp, path, data, ndp, p)
        struct mount *mp;
@@ -40,7 +42,7 @@ nullfs_mount(mp, path, data, ndp, p)
        USES_VOP_UNLOCK;
        int error = 0;
        struct null_args args;
        USES_VOP_UNLOCK;
        int error = 0;
        struct null_args args;
-       struct vnode *vp;
+       struct vnode *lowerrootvp, *vp;
        struct vnode *nullm_rootvp;
        struct null_mount *amp;
        u_int size;
        struct vnode *nullm_rootvp;
        struct null_mount *amp;
        u_int size;
@@ -74,15 +76,19 @@ nullfs_mount(mp, path, data, ndp, p)
        /*
         * Sanity check on target vnode
         */
        /*
         * Sanity check on target vnode
         */
-       vp = ndp->ni_vp;
+       lowerrootvp = ndp->ni_vp;
 #ifdef NULLFS_DIAGNOSTIC
 #ifdef NULLFS_DIAGNOSTIC
-       printf("vp = %x, check for VDIR...\n", vp);
+       printf("vp = %x, check for VDIR...\n", lowerrootvp);
 #endif
        vrele(ndp->ni_dvp);
        ndp->ni_dvp = 0;
 
 #endif
        vrele(ndp->ni_dvp);
        ndp->ni_dvp = 0;
 
-       if (vp->v_type != VDIR) {
-               vput(vp);
+       /*
+        * NEEDSWORK: Is this really bad, or just not
+        * the way it's been?
+        */
+       if (lowerrootvp->v_type != VDIR) {
+               vput(lowerrootvp);
                return (EINVAL);
        }
 
                return (EINVAL);
        }
 
@@ -96,13 +102,13 @@ nullfs_mount(mp, path, data, ndp, p)
        /*
         * Save reference to underlying target FS
         */
        /*
         * Save reference to underlying target FS
         */
-       amp->nullm_vfs = vp->v_mount;
+       amp->nullm_vfs = lowerrootvp->v_mount;
 
        /*
         * Save reference.  Each mount also holds
         * a reference on the root vnode.
         */
 
        /*
         * Save reference.  Each mount also holds
         * a reference on the root vnode.
         */
-       error = make_null_node(mp, &vp);
+       error = make_null_node(mp, lowerrootvp, &vp);
        /*
         * Unlock the node (either the target or the alias)
         */
        /*
         * Unlock the node (either the target or the alias)
         */
@@ -111,7 +117,7 @@ nullfs_mount(mp, path, data, ndp, p)
         * Make sure the node alias worked
         */
        if (error) {
         * Make sure the node alias worked
         */
        if (error) {
-               vrele(vp);
+               vrele(lowerrootvp);
                free(amp, M_UFSMNT);    /* XXX */
                return (error);
        }
                free(amp, M_UFSMNT);    /* XXX */
                return (error);
        }
@@ -155,7 +161,7 @@ nullfs_start(mp, flags, p)
 }
 
 /*
 }
 
 /*
- * Free reference to looped FS
+ * Free reference to null layer
  */
 nullfs_unmount(mp, mntflags, p)
        struct mount *mp;
  */
 nullfs_unmount(mp, mntflags, p)
        struct mount *mp;
@@ -294,6 +300,10 @@ nullfs_sync(mp, waitfor)
 struct mount *mp;
 int waitfor;
 {
 struct mount *mp;
 int waitfor;
 {
+       /*
+        * NEEDSWORK: Assumes no data cached at null layer.
+        * Is this true?
+        */
        return (0);
 }
 
        return (0);
 }
 
index 82c30a2..33400a3 100644 (file)
@@ -1,20 +1,44 @@
 /*
  * Copyright (c) 1992 The Regents of the University of California
 /*
  * Copyright (c) 1992 The Regents of the University of California
- * Copyright (c) 1990, 1992 Jan-Simon Pendry
  * All rights reserved.
  *
  * All rights reserved.
  *
- * This code is derived from software donated to Berkeley by
- * Jan-Simon Pendry.
+ * This code is derived from the null layer of
+ * John Heidemann of the UCLA Ficus project and
+ * the Jan-Simon Pendry's loopback file system.
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)lofs_vnops.c        1.2 (Berkeley) 6/18/92
+ *     @(#)null_vnops.c        1.3 (Berkeley) %G%
  *
  *
- * $Id: lofs_vnops.c,v 1.11 1992/05/30 10:05:43 jsp Exp jsp $
+ * Ancestors:
+ *     @(#)lofs_vnops.c        1.2 (Berkeley) 6/18/92
+ *     $Id: lofs_vnops.c,v 1.11 1992/05/30 10:05:43 jsp Exp jsp $
+ *     ...and...
+ *     @(#)null_vnodeops.c 1.20 92/07/07 UCLA Ficus project
  */
 
 /*
  */
 
 /*
- * Null layer Filesystem
+ * Null Layer
+ *
+ * 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 bypass operation, and it's "null-nodes" stack above
+ * all lower-layer vnodes, not just over directory vnodes.
+ *
+ * The null layer is the minimum file system layer,
+ * simply bypassing all possible operations to the lower layer
+ * for processing there.  All but vop_getattr, _inactive, _reclaim,
+ * and _print are bypassed.
+ *
+ * Vop_getattr is not bypassed so that we can change the fsid being
+ * returned.  Vop_{inactive,reclaim} are bypassed so that
+ * they can handle freeing null-layer specific data.
+ * Vop_print is not bypassed for debugging.
+ *
+ * NEEDSWORK: Describe methods to invoke operations on the lower layer
+ * (bypass vs. VOP).
  */
 
 #include <sys/param.h>
  */
 
 #include <sys/param.h>
 #include <sys/buf.h>
 #include <lofs/lofs.h>
 
 #include <sys/buf.h>
 #include <lofs/lofs.h>
 
-/*
- * Basic strategy: as usual, do as little work as possible.
- * Nothing is ever locked in the lofs'ed filesystem, all
- * locks are held in the underlying filesystems.
- */
-
-/*
- * Save a vnode and replace with
- * the lofs'ed one
- */
-#define PUSHREF(v, nd) \
-{ \
-       struct { struct vnode *vnp; } v; \
-       v.vnp = (nd); \
-       (nd) = NULLTOLOWERVP(v.vnp)
-
-/*
- * Undo the PUSHREF
- */
-#define POP(v, nd) \
-       \
-       (nd) = v.vnp; \
-}
 
 
+int null_bug_bypass = 0;   /* for debugging: enables bypass printf'ing */
 
 /*
 
 /*
- * vp is the current namei directory
- * ndp is the name to locate in that directory...
- */
-null_lookup (ap)
-       struct vop_lookup_args *ap;
-{
-       USES_VOP_LOOKUP;
-       struct vnode *dvp = ap->a_dvp;
-       struct vnode *newvp;
-       struct vnode *targetdvp;
+ * 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 nvop_generic_args *ap;
+{
+       register int this_vp_p;
        int error;
        int error;
-       int flag = ap->a_cnp->cn_nameiop /*& OPMASK*/;
+       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 maps, reles, i;
 
 
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_lookup(ap->a_dvp = %x->%x, \"%s\", op = %d)\n",
-               dvp, NULLTOLOWERVP(dvp), ap->a_cnp->cn_nameptr, flag);
-#endif
+       if (null_bug_bypass)
+               printf ("null_bypass: %s\n", descp->vdesc_name);
 
 
+#ifdef SAFETY
        /*
        /*
-        * (ap->a_dvp) was locked when passed in, and it will be replaced
-        * with the target vnode, BUT that will already have been
-        * locked when (ap->a_dvp) was locked [see null_lock].  all that
-        * must be done here is to keep track of reference counts.
+        * We require at least one vp.
         */
         */
-       targetdvp = NULLTOLOWERVP(dvp);
-       /*VREF(targetdvp);*/
-#ifdef NULLFS_DIAGNOSTIC
-       vprint("lofs VOP_LOOKUP", targetdvp);
+       if (descp->vdesc_vp_offsets==NULL ||
+           descp->vdesc_vp_offsets[0]==VDESC_NO_OFFSET)
+               panic ("null_bypass: no vp's in map.\n");
 #endif
 
        /*
 #endif
 
        /*
-        * Call lookup on the looped vnode
+        * Map the vnodes going in.
+        * Later, we'll invoke the operation based on
+        * the first mapped vnode's operation vector.
         */
         */
-       error = VOP_LOOKUP(targetdvp, &newvp, ap->a_cnp);
-       /*vrele(targetdvp);*/
-
-       if (error) {
-               *ap->a_vpp = NULLVP;
-#ifdef NULLFS_DIAGNOSTIC
-               printf("null_lookup(%x->%x) = %d\n", dvp, NULLTOLOWERVP(dvp), error);
-#endif
-               return (error);
-       }
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_lookup(%x->%x) = OK\n", dvp, NULLTOLOWERVP(dvp));
-#endif
-
-       *ap->a_vpp = newvp;
+       maps = descp->vdesc_flags;
+       reles = descp->vdesc_rele_flags;
+       for (i=0; i<VDESC_MAX_VPS; maps>>=1, reles>>=1, i++) {
+               if (descp->vdesc_vp_offsets[i]==VDESC_NO_OFFSET)
+                       break;   /* bail out at end of list */
+               if (maps & 1)   /* skip vps that aren't to be mapped */
+                       continue;
+               vps_p[i] = this_vp_p = 
+                       VOPARG_OFFSETTO(struct vnode**,descp->vdesc_vp_offsets[i],ap);
+               old_vps[i] = *this_vp_p;
+               *(vps_p[i]) = NULLTOLOWERVP(VTONULLNODE(*this_vp_p));
+               if (reles & 1)
+                       VREF(*this_vp_p);
+                       
+       };
 
        /*
 
        /*
-        * If we just found a directory then make
-        * a loopback node for it and return the loopback
-        * instead of the real vnode.  Otherwise simply
-        * return the aliased directory and vnode.
+        * Call the operation on the lower layer
+        * with the modified argument structure.
         */
         */
-       if (newvp && newvp->v_type == VDIR && flag == LOOKUP) {
-#ifdef NULLFS_DIAGNOSTIC
-               printf("null_lookup: found VDIR\n");
-#endif
-               /*
-                * At this point, newvp is the vnode to be looped.
-                * Activate a loopback and return the looped vnode.
-                */
-               return (make_null_node(dvp->v_mount, ap->a_vpp));
-       }
-
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_lookup: not VDIR\n");
-#endif
-
-       return (0);
-}
-
-/*
- * this = ni_dvp
- * ni_dvp references the locked directory.
- * ni_vp is NULL.
- */
-null_mknod (ap)
-       struct vop_mknod_args *ap;
-{
-       USES_VOP_MKNOD;
-       int error;
-
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_mknod(vp = %x->%x)\n", ap->a_dvp, NULLTOLOWERVP(ap->a_dvp));
-#endif
-
-       PUSHREF(xdvp, ap->a_dvp);
-       VREF(ap->a_dvp);
-
-       error = VOP_MKNOD(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap);
-
-       POP(xdvp, ap->a_dvp);
-       vrele(ap->a_dvp);
-
-       return (error);
-}
-
-/*
- * this = ni_dvp;
- * ni_dvp references the locked directory
- * ni_vp is NULL.
- */
-null_create (ap)
-       struct vop_create_args *ap;
-{
-       USES_VOP_CREATE;
-       int error;
-
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_create(ap->a_dvp = %x->%x)\n", ap->a_dvp, NULLTOLOWERVP(ap->a_dvp));
-#endif
-
-       PUSHREF(xdvp, ap->a_dvp);
-       VREF(ap->a_dvp);
-
-       error = VOP_CREATE(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap);
-
-       POP(xdvp, ap->a_dvp);
-       vrele(ap->a_dvp);
-
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_create(ap->a_dvp = %x->%x)\n", ap->a_dvp, NULLTOLOWERVP(ap->a_dvp));
-#endif
-
-       return (error);
-}
-
-null_open (ap)
-       struct vop_open_args *ap;
-{
-       USES_VOP_OPEN;
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_open(ap->a_vp = %x->%x)\n", ap->a_vp, NULLTOLOWERVP(ap->a_vp));
-#endif
-
-       return VOP_OPEN(NULLTOLOWERVP(ap->a_vp), ap->a_mode, ap->a_cred, ap->a_p);
-}
-
-null_close (ap)
-       struct vop_close_args *ap;
-{
-       USES_VOP_CLOSE;
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_close(ap->a_vp = %x->%x)\n", ap->a_vp, NULLTOLOWERVP(ap->a_vp));
-#endif
-
-       return VOP_CLOSE(NULLTOLOWERVP(ap->a_vp), ap->a_fflag, ap->a_cred, ap->a_p);
-}
-
-null_access (ap)
-       struct vop_access_args *ap;
-{
-       USES_VOP_ACCESS;
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_access(ap->a_vp = %x->%x)\n", ap->a_vp, NULLTOLOWERVP(ap->a_vp));
-#endif
-
-       return VOP_ACCESS(NULLTOLOWERVP(ap->a_vp), ap->a_mode, ap->a_cred, ap->a_p);
-}
-
-null_getattr (ap)
-       struct vop_getattr_args *ap;
-{
-       USES_VOP_GETATTR;
-       int error;
-
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_getattr(ap->a_vp = %x->%x)\n", ap->a_vp, NULLTOLOWERVP(ap->a_vp));
-#endif
+       error = VCALL(*(vps_p[0]), descp->vdesc_offset, ap);
 
        /*
 
        /*
-        * Get the stats from the underlying filesystem
+        * Maintain the illusion of call-by-value
+        * by restoring vnodes in the argument structure
+        * to their original value.
         */
         */
-       error = VOP_GETATTR(NULLTOLOWERVP(ap->a_vp), ap->a_vap, ap->a_cred, ap->a_p);
-       if (error)
-               return (error);
+       maps = descp->vdesc_flags;
+       reles = descp->vdesc_rele_flags;
+       for (i=0; i<VDESC_MAX_VPS; maps>>=1, i++) {
+               if (descp->vdesc_vp_offsets[i]==VDESC_NO_OFFSET)
+                       break;   /* bail out at end of list */
+               if (maps & 1)   /* skip vps that aren't to be mapped */
+                       continue;
+               *(vps_p[i]) = old_vps[i];
+               if (reles & 1)
+                       vrele(*(vps_p[i]));
+       };
+
        /*
        /*
-        * and replace the fsid field with the loopback number
-        * to preserve the namespace.
+        * Map the possible out-going vpp.
         */
         */
-       ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
-       return (0);
-}
-
-null_setattr (ap)
-       struct vop_setattr_args *ap;
-{
-       USES_VOP_SETATTR;
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_setattr(ap->a_vp = %x->%x)\n", ap->a_vp, NULLTOLOWERVP(ap->a_vp));
-#endif
-
-       return VOP_SETATTR(NULLTOLOWERVP(ap->a_vp), ap->a_vap, ap->a_cred, ap->a_p);
-}
-
-null_read (ap)
-       struct vop_read_args *ap;
-{
-       USES_VOP_READ;
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_read(ap->a_vp = %x->%x)\n", ap->a_vp, NULLTOLOWERVP(ap->a_vp));
-#endif
-
-       return VOP_READ(NULLTOLOWERVP(ap->a_vp), ap->a_uio, ap->a_ioflag, ap->a_cred);
-}
-
-null_write (ap)
-       struct vop_write_args *ap;
-{
-       USES_VOP_WRITE;
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_write(ap->a_vp = %x->%x)\n", ap->a_vp, NULLTOLOWERVP(ap->a_vp));
-#endif
-
-       return VOP_WRITE(NULLTOLOWERVP(ap->a_vp), ap->a_uio, ap->a_ioflag, ap->a_cred);
-}
-
-null_ioctl (ap)
-       struct vop_ioctl_args *ap;
-{
-       USES_VOP_IOCTL;
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_ioctl(ap->a_vp = %x->%x)\n", ap->a_vp, NULLTOLOWERVP(ap->a_vp));
-#endif
-
-       return VOP_IOCTL(NULLTOLOWERVP(ap->a_vp), ap->a_command, ap->a_data, ap->a_fflag, ap->a_cred, ap->a_p);
-}
-
-null_select (ap)
-       struct vop_select_args *ap;
-{
-       USES_VOP_SELECT;
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_select(ap->a_vp = %x->%x)\n", ap->a_vp, NULLTOLOWERVP(ap->a_vp));
-#endif
-
-       return VOP_SELECT(NULLTOLOWERVP(ap->a_vp), ap->a_which, ap->a_fflags, ap->a_cred, ap->a_p);
-}
-
-null_mmap (ap)
-       struct vop_mmap_args *ap;
-{
-       USES_VOP_MMAP;
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_mmap(ap->a_vp = %x->%x)\n", ap->a_vp, NULLTOLOWERVP(ap->a_vp));
-#endif
-
-       return VOP_MMAP(NULLTOLOWERVP(ap->a_vp), ap->a_fflags, ap->a_cred, ap->a_p);
-}
-
-null_fsync (ap)
-       struct vop_fsync_args *ap;
-{
-       USES_VOP_FSYNC;
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_fsync(ap->a_vp = %x->%x)\n", ap->a_vp, NULLTOLOWERVP(ap->a_vp));
-#endif
-
-       return VOP_FSYNC(NULLTOLOWERVP(ap->a_vp), ap->a_fflags, ap->a_cred, ap->a_waitfor, ap->a_p);
-}
-
-null_seek (ap)
-       struct vop_seek_args *ap;
-{
-       USES_VOP_SEEK;
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_seek(ap->a_vp = %x->%x)\n", ap->a_vp, NULLTOLOWERVP(ap->a_vp));
-#endif
-
-       return VOP_SEEK(NULLTOLOWERVP(ap->a_vp), ap->a_oldoff, ap->a_newoff, ap->a_cred);
-}
-
-null_remove (ap)
-       struct vop_remove_args *ap;
-{
-       USES_VOP_REMOVE;
-       int error;
-
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_remove(ap->a_vp = %x->%x)\n", ap->a_dvp, NULLTOLOWERVP(ap->a_dvp));
-#endif
-
-       PUSHREF(xdvp, ap->a_dvp);
-       VREF(ap->a_dvp);
-       PUSHREF(xvp, ap->a_vp);
-       VREF(ap->a_vp);
-
-       error = VOP_REMOVE(ap->a_dvp, ap->a_vp, ap->a_cnp);
-
-       POP(xvp, ap->a_vp);
-       vrele(ap->a_vp);
-       POP(xdvp, ap->a_dvp);
-       vrele(ap->a_dvp);
+       if (descp->vdesc_vpp_offset != VDESC_NO_OFFSET &&
+           !(descp->vdesc_flags & VDESC_NOMAP_VPP) &&
+           !error) {
+               vppp=VOPARG_OFFSETTO(struct vnode***,
+                                descp->vdesc_vpp_offset,ap);
+               error = make_null_node(old_vps[0]->v_mount, **vppp, *vppp);
+       };
 
        return (error);
 }
 
 
        return (error);
 }
 
+
 /*
 /*
- * vp is this.
- * ni_dvp is the locked parent of the target.
- * ni_vp is NULL.
+ *  We handle getattr to change the fsid.
  */
  */
-null_link (ap)
-       struct vop_link_args *ap;
+int
+null_getattr(ap)
+       struct nvop_getattr_args *ap;
 {
 {
-       USES_VOP_LINK;
        int error;
        int error;
-
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_link(ap->a_tdvp = %x->%x)\n", ap->a_vp, NULLTOLOWERVP(ap->a_vp));
-#endif
-
-       PUSHREF(xdvp, ap->a_vp);
-       VREF(ap->a_vp);
-
-       error = VOP_LINK(ap->a_vp, NULLTOLOWERVP(ap->a_tdvp), ap->a_cnp);
-
-       POP(xdvp, ap->a_vp);
-       vrele(ap->a_vp);
-
-       return (error);
+       if (error=null_bypass(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;
 }
 
 }
 
+
+#if 0
 null_rename (ap)
        struct vop_rename_args *ap;
 {
 null_rename (ap)
        struct vop_rename_args *ap;
 {
@@ -556,164 +357,28 @@ null_rename (ap)
 
        return (error);
 }
 
        return (error);
 }
-
-/*
- * ni_dvp is the locked (alias) parent.
- * ni_vp is NULL.
- */
-null_mkdir (ap)
-       struct vop_mkdir_args *ap;
-{
-       USES_VOP_MKDIR;
-       int error;
-       struct vnode *dvp = ap->a_dvp;
-       struct vnode *xdvp;
-       struct vnode *newvp;
-
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_mkdir(vp = %x->%x)\n", dvp, NULLTOLOWERVP(dvp));
-#endif
-
-       xdvp = dvp;
-       dvp = NULLTOLOWERVP(xdvp);
-       /*VREF(dvp);*/
-
-       error = VOP_MKDIR(dvp, &newvp, ap->a_cnp, ap->a_vap);
-
-       if (error) {
-               *ap->a_vpp = NULLVP;
-               /*vrele(xdvp);*/
-               return (error);
-       }
-
-       /*
-        * Make a new lofs node
-        */
-       /*VREF(dvp);*/
-
-       error = make_null_node(dvp->v_mount, &newvp);
-
-       *ap->a_vpp = newvp;
-
-       return (error);
-}
-
-/*
- * ni_dvp is the locked parent.
- * ni_vp is the entry to be removed.
- */
-null_rmdir (ap)
-       struct vop_rmdir_args *ap;
-{
-       USES_VOP_RMDIR;
-       struct vnode *vp = ap->a_vp;
-       struct vnode *dvp = ap->a_dvp;
-       int error;
-
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_rmdir(dvp = %x->%x)\n", dvp, NULLTOLOWERVP(dvp));
-#endif
-
-       PUSHREF(xdvp, dvp);
-       VREF(dvp);
-       PUSHREF(xvp, vp);
-       VREF(vp);
-
-       error = VOP_RMDIR(dvp, vp, ap->a_cnp);
-
-       POP(xvp, vp);
-       vrele(vp);
-       POP(xdvp, dvp);
-       vrele(dvp);
-
-       return (error);
-}
-
-/*
- * ni_dvp is the locked parent.
- * ni_vp is NULL.
- */
-null_symlink (ap)
-       struct vop_symlink_args *ap;
-{
-       USES_VOP_SYMLINK;
-       int error;
-
-#ifdef NULLFS_DIAGNOSTIC
-       printf("VOP_SYMLINK(vp = %x->%x)\n", ap->a_dvp, NULLTOLOWERVP(ap->a_dvp));
 #endif
 
 #endif
 
-       PUSHREF(xdvp, ap->a_dvp);
-       VREF(ap->a_dvp);
-
-       error = VOP_SYMLINK(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap, ap->a_target);
-
-       POP(xdvp, ap->a_dvp);
-       vrele(ap->a_dvp);
-
-       return (error);
-}
-
-null_readdir (ap)
-       struct vop_readdir_args *ap;
-{
-       USES_VOP_READDIR;
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_readdir(ap->a_vp = %x->%x)\n", ap->a_vp, NULLTOLOWERVP(ap->a_vp));
-#endif
-
-       return VOP_READDIR(NULLTOLOWERVP(ap->a_vp), ap->a_uio, ap->a_cred, ap->a_eofflagp);
-}
-
-null_readlink (ap)
-       struct vop_readlink_args *ap;
-{
-       USES_VOP_READLINK;
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_readlink(ap->a_vp = %x->%x)\n", ap->a_vp, NULLTOLOWERVP(ap->a_vp));
-#endif
-
-       return VOP_READLINK(NULLTOLOWERVP(ap->a_vp), ap->a_uio, ap->a_cred);
-}
-
-/*
- * Anyone's guess...
- */
-null_abortop (ap)
-       struct vop_abortop_args *ap;
-{
-       USES_VOP_ABORTOP;
-       int error;
-
-       PUSHREF(xdvp, ap->a_dvp);
-
-       error = VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
-
-       POP(xdvp, ap->a_dvp);
-
-       return (error);
-}
 
 
+int
 null_inactive (ap)
        struct vop_inactive_args *ap;
 {
 null_inactive (ap)
        struct vop_inactive_args *ap;
 {
-       USES_VOP_INACTIVE;
-       struct vnode *targetvp = NULLTOLOWERVP(ap->a_vp);
 #ifdef NULLFS_DIAGNOSTIC
 #ifdef NULLFS_DIAGNOSTIC
-       printf("null_inactive(ap->a_vp = %x->%x)\n", ap->a_vp, targetvp);
+       printf("null_inactive(ap->a_vp = %x->%x)\n", ap->a_vp, NULLTOLOWERVP(ap->a_vp));
 #endif
 #endif
-
-#ifdef DIAGNOSTIC
-       { extern int prtactive;
-       if (prtactive && ap->a_vp->v_usecount != 0)
-               vprint("null_inactive: pushing active", ap->a_vp);
-       }
-#endif
-
-       if (targetvp) {
-               vrele(targetvp);
-               VTONULLNODE(ap->a_vp)->null_lowervp = 0;
-       }
+       /*
+        * 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
+        * like they do in the name lookup cache code.
+        * That's too much work for now.
+        */
+       return 0;
 }
 
 null_reclaim (ap)
 }
 
 null_reclaim (ap)
@@ -724,57 +389,13 @@ null_reclaim (ap)
 #ifdef NULLFS_DIAGNOSTIC
        printf("null_reclaim(ap->a_vp = %x->%x)\n", ap->a_vp, NULLTOLOWERVP(ap->a_vp));
 #endif
 #ifdef NULLFS_DIAGNOSTIC
        printf("null_reclaim(ap->a_vp = %x->%x)\n", ap->a_vp, NULLTOLOWERVP(ap->a_vp));
 #endif
-       remque(VTONULLNODE(ap->a_vp));
-       targetvp = NULLTOLOWERVP(ap->a_vp);
-       if (targetvp) {
-               printf("lofs: delayed vrele of %x\n", targetvp);
-               vrele(targetvp);        /* XXX should never happen */
-       }
+       remque(VTONULLNODE(ap->a_vp));   /* NEEDSWORK: What? */
+       vrele (NULLTOLOWERVP(ap->a_vp));   /* release lower layer */
        FREE(ap->a_vp->v_data, M_TEMP);
        ap->a_vp->v_data = 0;
        return (0);
 }
 
        FREE(ap->a_vp->v_data, M_TEMP);
        ap->a_vp->v_data = 0;
        return (0);
 }
 
-null_lock (ap)
-       struct vop_lock_args *ap;
-{
-       USES_VOP_LOCK;
-       int error;
-       struct vnode *targetvp = NULLTOLOWERVP(ap->a_vp);
-
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_lock(ap->a_vp = %x->%x)\n", ap->a_vp, targetvp);
-       /*vprint("null_lock ap->a_vp", ap->a_vp);
-       if (targetvp)
-               vprint("null_lock ->ap->a_vp", targetvp);
-       else
-               printf("null_lock ->ap->a_vp = NIL\n");*/
-#endif
-
-       if (targetvp) {
-               error = VOP_LOCK(targetvp);
-               if (error)
-                       return (error);
-       }
-
-       return (0);
-}
-
-null_unlock (ap)
-       struct vop_unlock_args *ap;
-{
-       USES_VOP_UNLOCK;
-       struct vnode *targetvp = NULLTOLOWERVP(ap->a_vp);
-
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_unlock(ap->a_vp = %x->%x)\n", ap->a_vp, targetvp);
-#endif
-
-       if (targetvp)
-               return (VOP_UNLOCK(targetvp));
-       return (0);
-}
-
 null_bmap (ap)
        struct vop_bmap_args *ap;
 {
 null_bmap (ap)
        struct vop_bmap_args *ap;
 {
@@ -791,173 +412,52 @@ null_strategy (ap)
 {
        USES_VOP_STRATEGY;
        int error;
 {
        USES_VOP_STRATEGY;
        int error;
+       struct vnode *savedvp;
 
 #ifdef NULLFS_DIAGNOSTIC
        printf("null_strategy(vp = %x->%x)\n", ap->a_bp->b_vp, NULLTOLOWERVP(ap->a_bp->b_vp));
 #endif
 
 
 #ifdef NULLFS_DIAGNOSTIC
        printf("null_strategy(vp = %x->%x)\n", ap->a_bp->b_vp, NULLTOLOWERVP(ap->a_bp->b_vp));
 #endif
 
-       PUSHREF(vp, ap->a_bp->b_vp);
+       savedvp = ap->a_bp->b_vp;
 
        error = VOP_STRATEGY(ap->a_bp);
 
 
        error = VOP_STRATEGY(ap->a_bp);
 
-       POP(vp, ap->a_bp->b_vp);
+       ap->a_bp->b_vp = savedvp;
 
 
-       return (error);
+       return error;
 }
 
 }
 
+
+int
 null_print (ap)
        struct vop_print_args *ap;
 {
 null_print (ap)
        struct vop_print_args *ap;
 {
-       USES_VOP_PRINT;
-       struct vnode *targetvp = NULLTOLOWERVP(ap->a_vp);
-       printf("tag VT_LOFS ref ");
-       if (targetvp)
-               return (VOP_PRINT(targetvp));
-       printf("NULLVP\n");
-       return (0);
-}
-
-null_islocked (ap)
-       struct vop_islocked_args *ap;
-{
-       USES_VOP_ISLOCKED;
-       struct vnode *targetvp = NULLTOLOWERVP(ap->a_vp);
-       if (targetvp)
-               return (VOP_ISLOCKED(targetvp));
-       return (0);
-}
-
-null_advlock (ap)
-       struct vop_advlock_args *ap;
-{
-       USES_VOP_ADVLOCK;
-       return VOP_ADVLOCK(NULLTOLOWERVP(ap->a_vp), ap->a_id, ap->a_op, ap->a_fl, ap->a_flags);
-}
-
-/*
- * LOFS directory offset lookup.
- * Currently unsupported.
- */
-null_blkatoff (ap)
-       struct vop_blkatoff_args *ap;
-{
-
-       return (EOPNOTSUPP);
-}
-
-/*
- * LOFS flat namespace lookup.
- * Currently unsupported.
- */
-null_vget (ap)
-       struct vop_vget_args *ap;
-{
-
-       return (EOPNOTSUPP);
+       register struct vnode *vp = ap->a_vp;
+       printf ("tag VT_NULLFS, vp=%x, lowervp=%x\n", vp, NULLTOLOWERVP(vp));
+       return 0;
 }
 
 }
 
-/*
- * LOFS flat namespace allocation.
- * Currently unsupported.
- */
-null_valloc (ap)
-       struct vop_valloc_args *ap;
-{
-
-       return (EOPNOTSUPP);
-}
 
 /*
 
 /*
- * LOFS flat namespace free.
- * Currently unsupported.
+ * Global vfs data structures
  */
  */
-/*void*/
-null_vfree (ap)
-       struct vop_vfree_args *ap;
-{
-
-       return;
-}
-
 /*
 /*
- * LOFS file truncation.
+ * NEEDSWORK: strategy,bmap are hand coded currently.  They should
+ * go away with a merged buffer/block cache.
+ *
  */
  */
-null_truncate (ap)
-       struct vop_truncate_args *ap;
-{
-
-       /* Use null_setattr */
-       printf("null_truncate: need to implement!!");
-       return (EOPNOTSUPP);
-}
+int (**null_vnodeop_p)();
+struct vnodeopv_entry_desc lofs_vnodeop_entries[] = {
+       { &vop_default_desc, null_bypass },
 
 
-/*
- * LOFS update.
- */
-null_update (ap)
-       struct vop_update_args *ap;
-{
+       { &vop_getattr_desc, null_getattr },
+       { &vop_inactive_desc, null_inactive },
+       { &vop_reclaim_desc, null_reclaim },
+       { &vop_print_desc, null_print },
 
 
-       /* Use null_setattr */
-       printf("null_update: need to implement!!");
-       return (EOPNOTSUPP);
-}
+       { &vop_bmap_desc, null_bmap },
+       { &vop_strategy_desc, null_strategy },
 
 
-/*
- * LOFS bwrite
- */
-null_bwrite (ap)
-       struct vop_bwrite_args *ap;
-{
-       return (EOPNOTSUPP);
-}
-
-/*
- * Global vfs data structures for ufs
- */
-int (**null_vnodeop_p)();
-struct vnodeopv_entry_desc lofs_vnodeop_entries[] = {
-       { &vop_default_desc, vn_default_error },
-       { &vop_lookup_desc, null_lookup },              /* lookup */
-       { &vop_create_desc, null_create },              /* create */
-       { &vop_mknod_desc, null_mknod },                /* mknod */
-       { &vop_open_desc, null_open },          /* open */
-       { &vop_close_desc, null_close },                /* close */
-       { &vop_access_desc, null_access },              /* access */
-       { &vop_getattr_desc, null_getattr },            /* getattr */
-       { &vop_setattr_desc, null_setattr },            /* setattr */
-       { &vop_read_desc, null_read },          /* read */
-       { &vop_write_desc, null_write },                /* write */
-       { &vop_ioctl_desc, null_ioctl },                /* ioctl */
-       { &vop_select_desc, null_select },              /* select */
-       { &vop_mmap_desc, null_mmap },          /* mmap */
-       { &vop_fsync_desc, null_fsync },                /* fsync */
-       { &vop_seek_desc, null_seek },          /* seek */
-       { &vop_remove_desc, null_remove },              /* remove */
-       { &vop_link_desc, null_link },          /* link */
-       { &vop_rename_desc, null_rename },              /* rename */
-       { &vop_mkdir_desc, null_mkdir },                /* mkdir */
-       { &vop_rmdir_desc, null_rmdir },                /* rmdir */
-       { &vop_symlink_desc, null_symlink },            /* symlink */
-       { &vop_readdir_desc, null_readdir },            /* readdir */
-       { &vop_readlink_desc, null_readlink },          /* readlink */
-       { &vop_abortop_desc, null_abortop },            /* abortop */
-       { &vop_inactive_desc, null_inactive },          /* inactive */
-       { &vop_reclaim_desc, null_reclaim },            /* reclaim */
-       { &vop_lock_desc, null_lock },          /* lock */
-       { &vop_unlock_desc, null_unlock },              /* unlock */
-       { &vop_bmap_desc, null_bmap },          /* bmap */
-       { &vop_strategy_desc, null_strategy },          /* strategy */
-       { &vop_print_desc, null_print },                /* print */
-       { &vop_islocked_desc, null_islocked },          /* islocked */
-       { &vop_advlock_desc, null_advlock },            /* advlock */
-       { &vop_blkatoff_desc, null_blkatoff },          /* blkatoff */
-       { &vop_vget_desc, null_vget },          /* vget */
-       { &vop_valloc_desc, null_valloc },              /* valloc */
-       { &vop_vfree_desc, null_vfree },                /* vfree */
-       { &vop_truncate_desc, null_truncate },          /* truncate */
-       { &vop_update_desc, null_update },              /* update */
-       { &vop_bwrite_desc, null_bwrite },              /* bwrite */
        { (struct vnodeop_desc*)NULL, (int(*)())NULL }
 };
 struct vnodeopv_desc lofs_vnodeop_opv_desc =
        { (struct vnodeop_desc*)NULL, (int(*)())NULL }
 };
 struct vnodeopv_desc lofs_vnodeop_opv_desc =