have to recompute the next pointer after possibly sleeping in sync
[unix-history] / usr / src / sys / kern / vfs_syscalls.c
index b876bf7..deb34b9 100644 (file)
@@ -9,7 +9,7 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)vfs_syscalls.c      8.17 (Berkeley) %G%
+ *     @(#)vfs_syscalls.c      8.21 (Berkeley) %G%
  */
 
 #include <sys/param.h>
  */
 
 #include <sys/param.h>
@@ -63,13 +63,9 @@ mount(p, uap, retval)
        register struct vnode *vp;
        register struct mount *mp;
        int error, flag;
        register struct vnode *vp;
        register struct mount *mp;
        int error, flag;
+       struct vattr va;
        struct nameidata nd;
 
        struct nameidata nd;
 
-       /*
-        * Must be super user
-        */
-       if (error = suser(p->p_ucred, &p->p_acflag))
-               return (error);
        /*
         * Get vnode to be covered
         */
        /*
         * Get vnode to be covered
         */
@@ -95,9 +91,50 @@ mount(p, uap, retval)
                }
                mp->mnt_flag |=
                    uap->flags & (MNT_RELOAD | MNT_FORCE | MNT_UPDATE);
                }
                mp->mnt_flag |=
                    uap->flags & (MNT_RELOAD | MNT_FORCE | MNT_UPDATE);
+               /*
+                * Only root, or the user that did the original mount is
+                * permitted to update it.
+                */
+               if (mp->mnt_stat.f_owner != p->p_ucred->cr_uid &&
+                   (error = suser(p->p_ucred, &p->p_acflag))) {
+                       vput(vp);
+                       return (error);
+               }
+               /*
+                * Do not allow NFS export by non-root users. Silently
+                * enforce MNT_NOSUID and MNT_NODEV for non-root users.
+                */
+               if (p->p_ucred->cr_uid != 0) {
+                       if (uap->flags & MNT_EXPORTED) {
+                               vput(vp);
+                               return (EPERM);
+                       }
+                       uap->flags |= MNT_NOSUID | MNT_NODEV;
+               }
                VOP_UNLOCK(vp);
                goto update;
        }
                VOP_UNLOCK(vp);
                goto update;
        }
+       /*
+        * If the user is not root, ensure that they own the directory
+        * onto which we are attempting to mount.
+        */
+       if ((error = VOP_GETATTR(vp, &va, p->p_ucred, p)) ||
+           (va.va_uid != p->p_ucred->cr_uid &&
+            (error = suser(p->p_ucred, &p->p_acflag)))) {
+               vput(vp);
+               return (error);
+       }
+       /*
+        * Do not allow NFS export by non-root users. Silently
+        * enforce MNT_NOSUID and MNT_NODEV for non-root users.
+        */
+       if (p->p_ucred->cr_uid != 0) {
+               if (uap->flags & MNT_EXPORTED) {
+                       vput(vp);
+                       return (EPERM);
+               }
+               uap->flags |= MNT_NOSUID | MNT_NODEV;
+       }
        if (error = vinvalbuf(vp, V_SAVE, p->p_ucred, p, 0, 0))
                return (error);
        if (vp->v_type != VDIR) {
        if (error = vinvalbuf(vp, V_SAVE, p->p_ucred, p, 0, 0))
                return (error);
        if (vp->v_type != VDIR) {
@@ -129,6 +166,7 @@ mount(p, uap, retval)
        }
        vp->v_mountedhere = mp;
        mp->mnt_vnodecovered = vp;
        }
        vp->v_mountedhere = mp;
        mp->mnt_vnodecovered = vp;
+       mp->mnt_stat.f_owner = p->p_ucred->cr_uid;
 update:
        /*
         * Set the mount level flags.
 update:
        /*
         * Set the mount level flags.
@@ -198,12 +236,13 @@ unmount(p, uap, retval)
        if (error = namei(&nd))
                return (error);
        vp = nd.ni_vp;
        if (error = namei(&nd))
                return (error);
        vp = nd.ni_vp;
+       mp = vp->v_mount;
 
        /*
 
        /*
-        * Unless this is a user mount, then must
-        * have suser privilege.
+        * Only root, or the user that did the original mount is
+        * permitted to unmount this filesystem.
         */
         */
-       if (((vp->v_mount->mnt_flag & MNT_USER) == 0) &&
+       if ((mp->mnt_stat.f_owner != p->p_ucred->cr_uid) &&
            (error = suser(p->p_ucred, &p->p_acflag))) {
                vput(vp);
                return (error);
            (error = suser(p->p_ucred, &p->p_acflag))) {
                vput(vp);
                return (error);
@@ -216,7 +255,6 @@ unmount(p, uap, retval)
                vput(vp);
                return (EINVAL);
        }
                vput(vp);
                return (EINVAL);
        }
-       mp = vp->v_mount;
        vput(vp);
        return (dounmount(mp, uap->flags, p));
 }
        vput(vp);
        return (dounmount(mp, uap->flags, p));
 }
@@ -282,6 +320,10 @@ sync(p, uap, retval)
        int asyncflag;
 
        for (mp = mountlist.tqh_first; mp != NULL; mp = nmp) {
        int asyncflag;
 
        for (mp = mountlist.tqh_first; mp != NULL; mp = nmp) {
+               /*
+                * Get the next pointer in case we hang on vfs_busy
+                * while we are being unmounted.
+                */
                nmp = mp->mnt_list.tqe_next;
                /*
                 * The lock check below is to avoid races with mount
                nmp = mp->mnt_list.tqe_next;
                /*
                 * The lock check below is to avoid races with mount
@@ -294,6 +336,11 @@ sync(p, uap, retval)
                        VFS_SYNC(mp, MNT_NOWAIT, p->p_ucred, p);
                        if (asyncflag)
                                mp->mnt_flag |= MNT_ASYNC;
                        VFS_SYNC(mp, MNT_NOWAIT, p->p_ucred, p);
                        if (asyncflag)
                                mp->mnt_flag |= MNT_ASYNC;
+                       /*
+                        * Get the next pointer again, as the next filesystem
+                        * might have been unmounted while we were sync'ing.
+                        */
+                       nmp = mp->mnt_list.tqe_next;
                        vfs_unbusy(mp);
                }
        }
                        vfs_unbusy(mp);
                }
        }
@@ -661,6 +708,7 @@ mknod(p, uap, retval)
        register struct vnode *vp;
        struct vattr vattr;
        int error;
        register struct vnode *vp;
        struct vattr vattr;
        int error;
+       int whiteout;
        struct nameidata nd;
 
        CHECKPOINTREF;
        struct nameidata nd;
 
        CHECKPOINTREF;
@@ -676,6 +724,7 @@ mknod(p, uap, retval)
                VATTR_NULL(&vattr);
                vattr.va_mode = (uap->mode & ALLPERMS) &~ p->p_fd->fd_cmask;
                vattr.va_rdev = uap->dev;
                VATTR_NULL(&vattr);
                vattr.va_mode = (uap->mode & ALLPERMS) &~ p->p_fd->fd_cmask;
                vattr.va_rdev = uap->dev;
+               whiteout = 0;
 
                switch (uap->mode & S_IFMT) {
                case S_IFMT:    /* used by badsect to flag bad sectors */
 
                switch (uap->mode & S_IFMT) {
                case S_IFMT:    /* used by badsect to flag bad sectors */
@@ -687,13 +736,19 @@ mknod(p, uap, retval)
                case S_IFBLK:
                        vattr.va_type = VBLK;
                        break;
                case S_IFBLK:
                        vattr.va_type = VBLK;
                        break;
+               case S_IFWHT:
+                       whiteout = 1;
+                       break;
                default:
                        error = EINVAL;
                        break;
                }
        }
                default:
                        error = EINVAL;
                        break;
                }
        }
-       if (!error) {
-               LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+       if (whiteout) {
+               error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, CREATE);
+               vput(nd.ni_dvp);
+       } else if (!error) {
+               VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
                error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr);
        } else {
                VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
                error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr);
        } else {
                VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
@@ -743,7 +798,7 @@ mkfifo(p, uap, retval)
        VATTR_NULL(&vattr);
        vattr.va_type = VFIFO;
        vattr.va_mode = (uap->mode & ALLPERMS) &~ p->p_fd->fd_cmask;
        VATTR_NULL(&vattr);
        vattr.va_type = VFIFO;
        vattr.va_mode = (uap->mode & ALLPERMS) &~ p->p_fd->fd_cmask;
-       LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+       VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
        return (VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr));
 #endif /* FIFO */
 }
        return (VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr));
 #endif /* FIFO */
 }
@@ -779,10 +834,9 @@ link(p, uap, retval)
                        if (nd.ni_vp != NULL)
                                error = EEXIST;
                        if (!error) {
                        if (nd.ni_vp != NULL)
                                error = EEXIST;
                        if (!error) {
-                               LEASE_CHECK(nd.ni_dvp,
-                                   p, p->p_ucred, LEASE_WRITE);
-                               LEASE_CHECK(vp,
-                                   p, p->p_ucred, LEASE_WRITE);
+                               VOP_LEASE(nd.ni_dvp, p, p->p_ucred,
+                                   LEASE_WRITE);
+                               VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE);
                                error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd);
                        } else {
                                VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
                                error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd);
                        } else {
                                VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
@@ -837,7 +891,7 @@ symlink(p, uap, retval)
        }
        VATTR_NULL(&vattr);
        vattr.va_mode = ACCESSPERMS &~ p->p_fd->fd_cmask;
        }
        VATTR_NULL(&vattr);
        vattr.va_mode = ACCESSPERMS &~ p->p_fd->fd_cmask;
-       LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+       VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
        error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr, path);
 out:
        FREE(path, M_NAMEI);
        error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr, path);
 out:
        FREE(path, M_NAMEI);
@@ -860,10 +914,12 @@ unwhiteout(p, uap, retval)
        int error;
        struct nameidata nd;
 
        int error;
        struct nameidata nd;
 
-       NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->path, p);
-       if (error = namei(&nd))
+       NDINIT(&nd, DELETE, LOCKPARENT|DOWHITEOUT, UIO_USERSPACE, uap->path, p);
+       error = namei(&nd);
+       if (error)
                return (error);
                return (error);
-       if (nd.ni_vp || !(nd.ni_cnd.cn_flags & WHITEOUT)) {
+
+       if (nd.ni_vp != NULLVP || !(nd.ni_cnd.cn_flags & ISWHITEOUT)) {
                VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
                if (nd.ni_dvp == nd.ni_vp)
                        vrele(nd.ni_dvp);
                VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
                if (nd.ni_dvp == nd.ni_vp)
                        vrele(nd.ni_dvp);
@@ -873,8 +929,11 @@ unwhiteout(p, uap, retval)
                        vrele(nd.ni_vp);
                return (EEXIST);
        }
                        vrele(nd.ni_vp);
                return (EEXIST);
        }
-       LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+
+       VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
        error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, DELETE);
        error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, DELETE);
+       if (error != 0)
+               VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
        vput(nd.ni_dvp);
        return (error);
 }
        vput(nd.ni_dvp);
        return (error);
 }
@@ -900,7 +959,7 @@ unlink(p, uap, retval)
        if (error = namei(&nd))
                return (error);
        vp = nd.ni_vp;
        if (error = namei(&nd))
                return (error);
        vp = nd.ni_vp;
-       LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+       VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE);
        VOP_LOCK(vp);
 
        if (vp->v_type != VDIR ||
        VOP_LOCK(vp);
 
        if (vp->v_type != VDIR ||
@@ -915,7 +974,7 @@ unlink(p, uap, retval)
        }
 
        if (!error) {
        }
 
        if (!error) {
-               LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+               VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
                error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
        } else {
                VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
                error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
        } else {
                VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
@@ -923,7 +982,8 @@ unlink(p, uap, retval)
                        vrele(nd.ni_dvp);
                else
                        vput(nd.ni_dvp);
                        vrele(nd.ni_dvp);
                else
                        vput(nd.ni_dvp);
-               vput(vp);
+               if (vp != NULLVP)
+                       vput(vp);
        }
        CHECKREFS("unlink");
        return (error);
        }
        CHECKREFS("unlink");
        return (error);
@@ -1313,7 +1373,7 @@ chflags(p, uap, retval)
        if (error = namei(&nd))
                return (error);
        vp = nd.ni_vp;
        if (error = namei(&nd))
                return (error);
        vp = nd.ni_vp;
-       LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+       VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE);
        VOP_LOCK(vp);
        if (vp->v_mount->mnt_flag & MNT_RDONLY)
                error = EROFS;
        VOP_LOCK(vp);
        if (vp->v_mount->mnt_flag & MNT_RDONLY)
                error = EROFS;
@@ -1347,7 +1407,7 @@ fchflags(p, uap, retval)
        if (error = getvnode(p->p_fd, uap->fd, &fp))
                return (error);
        vp = (struct vnode *)fp->f_data;
        if (error = getvnode(p->p_fd, uap->fd, &fp))
                return (error);
        vp = (struct vnode *)fp->f_data;
-       LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+       VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE);
        VOP_LOCK(vp);
        if (vp->v_mount->mnt_flag & MNT_RDONLY)
                error = EROFS;
        VOP_LOCK(vp);
        if (vp->v_mount->mnt_flag & MNT_RDONLY)
                error = EROFS;
@@ -1382,7 +1442,7 @@ chmod(p, uap, retval)
        if (error = namei(&nd))
                return (error);
        vp = nd.ni_vp;
        if (error = namei(&nd))
                return (error);
        vp = nd.ni_vp;
-       LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+       VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE);
        VOP_LOCK(vp);
        if (vp->v_mount->mnt_flag & MNT_RDONLY)
                error = EROFS;
        VOP_LOCK(vp);
        if (vp->v_mount->mnt_flag & MNT_RDONLY)
                error = EROFS;
@@ -1416,7 +1476,7 @@ fchmod(p, uap, retval)
        if (error = getvnode(p->p_fd, uap->fd, &fp))
                return (error);
        vp = (struct vnode *)fp->f_data;
        if (error = getvnode(p->p_fd, uap->fd, &fp))
                return (error);
        vp = (struct vnode *)fp->f_data;
-       LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+       VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE);
        VOP_LOCK(vp);
        if (vp->v_mount->mnt_flag & MNT_RDONLY)
                error = EROFS;
        VOP_LOCK(vp);
        if (vp->v_mount->mnt_flag & MNT_RDONLY)
                error = EROFS;
@@ -1452,7 +1512,7 @@ chown(p, uap, retval)
        if (error = namei(&nd))
                return (error);
        vp = nd.ni_vp;
        if (error = namei(&nd))
                return (error);
        vp = nd.ni_vp;
-       LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+       VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE);
        VOP_LOCK(vp);
        if (vp->v_mount->mnt_flag & MNT_RDONLY)
                error = EROFS;
        VOP_LOCK(vp);
        if (vp->v_mount->mnt_flag & MNT_RDONLY)
                error = EROFS;
@@ -1488,7 +1548,7 @@ fchown(p, uap, retval)
        if (error = getvnode(p->p_fd, uap->fd, &fp))
                return (error);
        vp = (struct vnode *)fp->f_data;
        if (error = getvnode(p->p_fd, uap->fd, &fp))
                return (error);
        vp = (struct vnode *)fp->f_data;
-       LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+       VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE);
        VOP_LOCK(vp);
        if (vp->v_mount->mnt_flag & MNT_RDONLY)
                error = EROFS;
        VOP_LOCK(vp);
        if (vp->v_mount->mnt_flag & MNT_RDONLY)
                error = EROFS;
@@ -1532,7 +1592,7 @@ utimes(p, uap, retval)
        if (error = namei(&nd))
                return (error);
        vp = nd.ni_vp;
        if (error = namei(&nd))
                return (error);
        vp = nd.ni_vp;
-       LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+       VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE);
        VOP_LOCK(vp);
        if (vp->v_mount->mnt_flag & MNT_RDONLY)
                error = EROFS;
        VOP_LOCK(vp);
        if (vp->v_mount->mnt_flag & MNT_RDONLY)
                error = EROFS;
@@ -1570,7 +1630,7 @@ truncate(p, uap, retval)
        if (error = namei(&nd))
                return (error);
        vp = nd.ni_vp;
        if (error = namei(&nd))
                return (error);
        vp = nd.ni_vp;
-       LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+       VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE);
        VOP_LOCK(vp);
        if (vp->v_type == VDIR)
                error = EISDIR;
        VOP_LOCK(vp);
        if (vp->v_type == VDIR)
                error = EISDIR;
@@ -1608,7 +1668,7 @@ ftruncate(p, uap, retval)
        if ((fp->f_flag & FWRITE) == 0)
                return (EINVAL);
        vp = (struct vnode *)fp->f_data;
        if ((fp->f_flag & FWRITE) == 0)
                return (EINVAL);
        vp = (struct vnode *)fp->f_data;
-       LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+       VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE);
        VOP_LOCK(vp);
        if (vp->v_type == VDIR)
                error = EISDIR;
        VOP_LOCK(vp);
        if (vp->v_type == VDIR)
                error = EISDIR;
@@ -1745,11 +1805,11 @@ rename(p, uap, retval)
                error = -1;
 out:
        if (!error) {
                error = -1;
 out:
        if (!error) {
-               LEASE_CHECK(tdvp, p, p->p_ucred, LEASE_WRITE);
+               VOP_LEASE(tdvp, p, p->p_ucred, LEASE_WRITE);
                if (fromnd.ni_dvp != tdvp)
                if (fromnd.ni_dvp != tdvp)
-                       LEASE_CHECK(fromnd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+                       VOP_LEASE(fromnd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
                if (tvp)
                if (tvp)
-                       LEASE_CHECK(tvp, p, p->p_ucred, LEASE_WRITE);
+                       VOP_LEASE(tvp, p, p->p_ucred, LEASE_WRITE);
                error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd,
                                   tond.ni_dvp, tond.ni_vp, &tond.ni_cnd);
        } else {
                error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd,
                                   tond.ni_dvp, tond.ni_vp, &tond.ni_cnd);
        } else {
@@ -1814,7 +1874,7 @@ mkdir(p, uap, retval)
        VATTR_NULL(&vattr);
        vattr.va_type = VDIR;
        vattr.va_mode = (uap->mode & ACCESSPERMS) &~ p->p_fd->fd_cmask;
        VATTR_NULL(&vattr);
        vattr.va_type = VDIR;
        vattr.va_mode = (uap->mode & ACCESSPERMS) &~ p->p_fd->fd_cmask;
-       LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+       VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
        error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr);
        if (!error)
                vput(nd.ni_vp);
        error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr);
        if (!error)
                vput(nd.ni_vp);
@@ -1861,8 +1921,8 @@ rmdir(p, uap, retval)
                error = EBUSY;
 out:
        if (!error) {
                error = EBUSY;
 out:
        if (!error) {
-               LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
-               LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE);
+               VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
+               VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE);
                error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
        } else {
                VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
                error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
        } else {
                VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
@@ -1983,6 +2043,20 @@ unionread:
                struct vnode *lvp;
 
                lvp = union_lowervp(vp);
                struct vnode *lvp;
 
                lvp = union_lowervp(vp);
+               if (lvp != NULLVP) {
+                       struct vattr va;
+
+                       /*
+                        * If the directory is opaque,
+                        * then don't show lower entries
+                        */
+                       error = VOP_GETATTR(vp, &va, fp->f_cred, p);
+                       if (va.va_flags & OPAQUE) {
+                               vrele(lvp);
+                               lvp = NULL;
+                       }
+               }
+               
                if (lvp != NULLVP) {
                        VOP_LOCK(lvp);
                        error = VOP_OPEN(lvp, FREAD, fp->f_cred, p);
                if (lvp != NULLVP) {
                        VOP_LOCK(lvp);
                        error = VOP_OPEN(lvp, FREAD, fp->f_cred, p);
@@ -2076,6 +2150,20 @@ unionread:
                struct vnode *lvp;
 
                lvp = union_lowervp(vp);
                struct vnode *lvp;
 
                lvp = union_lowervp(vp);
+               if (lvp != NULLVP) {
+                       struct vattr va;
+
+                       /*
+                        * If the directory is opaque,
+                        * then don't show lower entries
+                        */
+                       error = VOP_GETATTR(vp, &va, fp->f_cred, p);
+                       if (va.va_flags & OPAQUE) {
+                               vrele(lvp);
+                               lvp = NULL;
+                       }
+               }
+               
                if (lvp != NULLVP) {
                        VOP_LOCK(lvp);
                        error = VOP_OPEN(lvp, FREAD, fp->f_cred, p);
                if (lvp != NULLVP) {
                        VOP_LOCK(lvp);
                        error = VOP_OPEN(lvp, FREAD, fp->f_cred, p);