X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/12561664c1c9bff8b92347f86d3a60cb82a0d8b9..13f48dee70ec0b79753383e9734854a57464d8fa:/usr/src/sys/kern/vfs_syscalls.c diff --git a/usr/src/sys/kern/vfs_syscalls.c b/usr/src/sys/kern/vfs_syscalls.c index f0f0b8adb4..4063304af6 100644 --- a/usr/src/sys/kern/vfs_syscalls.c +++ b/usr/src/sys/kern/vfs_syscalls.c @@ -9,7 +9,7 @@ * * %sccs.include.redist.c% * - * @(#)vfs_syscalls.c 8.12 (Berkeley) %G% + * @(#)vfs_syscalls.c 8.28 (Berkeley) %G% */ #include @@ -63,13 +63,9 @@ mount(p, uap, retval) register struct vnode *vp; register struct mount *mp; int error, flag; + struct vattr va; struct nameidata nd; - /* - * Must be super user - */ - if (error = suser(p->p_ucred, &p->p_acflag)) - return (error); /* * Get vnode to be covered */ @@ -95,9 +91,50 @@ mount(p, uap, retval) } 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; } + /* + * 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) { @@ -108,6 +145,10 @@ mount(p, uap, retval) vput(vp); return (ENODEV); } + if (vp->v_mountedhere != NULL) { + vput(vp); + return (EBUSY); + } /* * Allocate and initialize the file system. @@ -121,14 +162,9 @@ mount(p, uap, retval) vput(vp); return (error); } - if (vp->v_mountedhere != NULL) { - vfs_unlock(mp); - free((caddr_t)mp, M_MOUNT); - vput(vp); - return (EBUSY); - } vp->v_mountedhere = mp; mp->mnt_vnodecovered = vp; + mp->mnt_stat.f_owner = p->p_ucred->cr_uid; update: /* * Set the mount level flags. @@ -161,6 +197,7 @@ update: cache_purge(vp); if (!error) { TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); + checkdirs(vp); VOP_UNLOCK(vp); vfs_unlock(mp); error = VFS_START(mp, 0, p); @@ -173,6 +210,43 @@ update: return (error); } +/* + * Scan all active processes to see if any of them have a current + * or root directory onto which the new filesystem has just been + * mounted. If so, replace them with the new mount point. + */ +checkdirs(olddp) + struct vnode *olddp; +{ + struct filedesc *fdp; + struct vnode *newdp; + struct proc *p; + + if (olddp->v_usecount == 1) + return; + if (VFS_ROOT(olddp->v_mountedhere, &newdp)) + panic("mount: lost mount"); + for (p = allproc.lh_first; p != 0; p = p->p_list.le_next) { + fdp = p->p_fd; + if (fdp->fd_cdir == olddp) { + vrele(fdp->fd_cdir); + VREF(newdp); + fdp->fd_cdir = newdp; + } + if (fdp->fd_rdir == olddp) { + vrele(fdp->fd_rdir); + VREF(newdp); + fdp->fd_rdir = newdp; + } + } + if (rootvnode == olddp) { + vrele(rootvnode); + VREF(newdp); + rootvnode = newdp; + } + vput(newdp); +} + /* * Unmount a file system. * @@ -198,12 +272,13 @@ unmount(p, uap, retval) 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); @@ -216,7 +291,6 @@ unmount(p, uap, retval) vput(vp); return (EINVAL); } - mp = vp->v_mount; vput(vp); return (dounmount(mp, uap->flags, p)); } @@ -264,7 +338,7 @@ dounmount(mp, flags, p) /* * Sync each mounted filesystem. */ -#ifdef DIAGNOSTIC +#ifdef DEBUG int syncprt = 0; struct ctldebug debug0 = { "syncprt", &syncprt }; #endif @@ -282,6 +356,10 @@ sync(p, uap, retval) 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 @@ -294,6 +372,11 @@ sync(p, uap, retval) 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); } } @@ -449,22 +532,36 @@ fchdir(p, uap, retval) int *retval; { register struct filedesc *fdp = p->p_fd; - register struct vnode *vp; + struct vnode *vp, *tdp; + struct mount *mp; struct file *fp; int error; if (error = getvnode(fdp, uap->fd, &fp)) return (error); vp = (struct vnode *)fp->f_data; + VREF(vp); VOP_LOCK(vp); if (vp->v_type != VDIR) error = ENOTDIR; else error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); + while (!error && (mp = vp->v_mountedhere) != NULL) { + if (mp->mnt_flag & MNT_MLOCK) { + mp->mnt_flag |= MNT_MWAIT; + sleep((caddr_t)mp, PVFS); + continue; + } + if (error = VFS_ROOT(mp, &tdp)) + break; + vput(vp); + vp = tdp; + } VOP_UNLOCK(vp); - if (error) + if (error) { + vrele(vp); return (error); - VREF(vp); + } vrele(fdp->fd_cdir); fdp->fd_cdir = vp; return (0); @@ -661,6 +758,7 @@ mknod(p, uap, retval) register struct vnode *vp; struct vattr vattr; int error; + int whiteout; struct nameidata nd; CHECKPOINTREF; @@ -676,6 +774,7 @@ mknod(p, uap, retval) 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 */ @@ -687,14 +786,25 @@ mknod(p, uap, retval) case S_IFBLK: vattr.va_type = VBLK; break; + case S_IFWHT: + whiteout = 1; + break; default: error = EINVAL; break; } } if (!error) { - LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); - error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + if (whiteout) { + error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, CREATE); + if (error) + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + vput(nd.ni_dvp); + } else { + error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, + &nd.ni_cnd, &vattr); + } } else { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == vp) @@ -743,7 +853,7 @@ mkfifo(p, uap, retval) 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 */ } @@ -779,10 +889,9 @@ link(p, uap, retval) 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); @@ -837,7 +946,7 @@ symlink(p, uap, retval) } 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); @@ -845,6 +954,44 @@ out: return (error); } +/* + * Delete a whiteout from the filesystem. + */ +struct undelete_args { + char *path; +}; +/* ARGSUSED */ +undelete(p, uap, retval) + struct proc *p; + struct undelete_args *uap; + int *retval; +{ + int error; + struct nameidata nd; + + NDINIT(&nd, DELETE, LOCKPARENT|DOWHITEOUT, UIO_USERSPACE, uap->path, p); + error = namei(&nd); + if (error) + return (error); + + 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); + else + vput(nd.ni_dvp); + if (nd.ni_vp) + vrele(nd.ni_vp); + return (EEXIST); + } + + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + if (error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, DELETE)) + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + vput(nd.ni_dvp); + return (error); +} + /* * Delete a name from the filesystem. */ @@ -866,7 +1013,7 @@ unlink(p, uap, retval) 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 || @@ -881,7 +1028,7 @@ unlink(p, uap, retval) } 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); @@ -889,7 +1036,8 @@ unlink(p, uap, retval) vrele(nd.ni_dvp); else vput(nd.ni_dvp); - vput(vp); + if (vp != NULLVP) + vput(vp); } CHECKREFS("unlink"); return (error); @@ -1056,18 +1204,48 @@ olstat(p, uap, retval) register struct olstat_args *uap; int *retval; { - struct stat sb; + struct vnode *vp, *dvp; + struct stat sb, sb1; struct ostat osb; int error; struct nameidata nd; - NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); + NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | LOCKPARENT, UIO_USERSPACE, + uap->path, p); if (error = namei(&nd)) return (error); - error = vn_stat(nd.ni_vp, &sb, p); - vput(nd.ni_vp); - if (error) - return (error); + /* + * For symbolic links, always return the attributes of its + * containing directory, except for mode, size, and links. + */ + vp = nd.ni_vp; + dvp = nd.ni_dvp; + if (vp->v_type != VLNK) { + if (dvp == vp) + vrele(dvp); + else + vput(dvp); + error = vn_stat(vp, &sb, p); + vput(vp); + if (error) + return (error); + } else { + error = vn_stat(dvp, &sb, p); + vput(dvp); + if (error) { + vput(vp); + return (error); + } + error = vn_stat(vp, &sb1, p); + vput(vp); + if (error) + return (error); + sb.st_mode &= ~S_IFDIR; + sb.st_mode |= S_IFLNK; + sb.st_nlink = sb1.st_nlink; + sb.st_size = sb1.st_size; + sb.st_blocks = sb1.st_blocks; + } cvtstat(&sb, &osb); error = copyout((caddr_t)&osb, (caddr_t)uap->ub, sizeof (osb)); return (error); @@ -1279,7 +1457,7 @@ chflags(p, uap, retval) 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; @@ -1313,7 +1491,7 @@ fchflags(p, uap, retval) 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; @@ -1348,7 +1526,7 @@ chmod(p, uap, retval) 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; @@ -1382,7 +1560,7 @@ fchmod(p, uap, retval) 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; @@ -1418,7 +1596,7 @@ chown(p, uap, retval) 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; @@ -1454,7 +1632,7 @@ fchown(p, uap, retval) 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; @@ -1498,7 +1676,7 @@ utimes(p, uap, retval) 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; @@ -1536,7 +1714,7 @@ truncate(p, uap, retval) 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; @@ -1574,7 +1752,7 @@ ftruncate(p, uap, retval) 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; @@ -1711,11 +1889,11 @@ rename(p, uap, retval) 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) - LEASE_CHECK(fromnd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + VOP_LEASE(fromnd.ni_dvp, p, p->p_ucred, LEASE_WRITE); 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 { @@ -1735,7 +1913,8 @@ out: FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI); out1: p->p_spare[1]--; - vrele(fromnd.ni_startdir); + if (fromnd.ni_startdir) + vrele(fromnd.ni_startdir); FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI); CHECKREFS("rename"); if (error == -1) @@ -1779,7 +1958,7 @@ mkdir(p, uap, retval) 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); @@ -1826,8 +2005,8 @@ rmdir(p, uap, retval) 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); @@ -1862,7 +2041,7 @@ ogetdirentries(p, uap, retval) struct iovec aiov, kiov; struct dirent *dp, *edp; caddr_t dirbuf; - int error, readcnt; + int error, eofflag, readcnt; long loff; if (error = getvnode(p->p_fd, uap->fd, &fp)) @@ -1870,6 +2049,7 @@ ogetdirentries(p, uap, retval) if ((fp->f_flag & FREAD) == 0) return (EBADF); vp = (struct vnode *)fp->f_data; +unionread: if (vp->v_type != VDIR) return (EINVAL); aiov.iov_base = uap->buf; @@ -1884,7 +2064,8 @@ ogetdirentries(p, uap, retval) loff = auio.uio_offset = fp->f_offset; # if (BYTE_ORDER != LITTLE_ENDIAN) if (vp->v_mount->mnt_maxsymlinklen <= 0) { - error = VOP_READDIR(vp, &auio, fp->f_cred); + error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, + (u_long *)0, 0); fp->f_offset = auio.uio_offset; } else # endif @@ -1895,7 +2076,8 @@ ogetdirentries(p, uap, retval) kiov.iov_len = uap->count; MALLOC(dirbuf, caddr_t, uap->count, M_TEMP, M_WAITOK); kiov.iov_base = dirbuf; - error = VOP_READDIR(vp, &kuio, fp->f_cred); + error = VOP_READDIR(vp, &kuio, fp->f_cred, &eofflag, + (u_long *)0, 0); fp->f_offset = kuio.uio_offset; if (error == 0) { readcnt = uap->count - kuio.uio_resid; @@ -1934,11 +2116,67 @@ ogetdirentries(p, uap, retval) VOP_UNLOCK(vp); if (error) return (error); + +#ifdef UNION +{ + extern int (**union_vnodeop_p)(); + extern struct vnode *union_dircache __P((struct vnode *)); + + if ((uap->count == auio.uio_resid) && + (vp->v_op == union_vnodeop_p)) { + struct vnode *lvp; + + lvp = union_dircache(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) { + vput(lvp); + lvp = NULL; + } + } + + if (lvp != NULLVP) { + error = VOP_OPEN(lvp, FREAD, fp->f_cred, p); + VOP_UNLOCK(lvp); + + if (error) { + vrele(lvp); + return (error); + } + fp->f_data = (caddr_t) lvp; + fp->f_offset = 0; + error = vn_close(vp, FREAD, fp->f_cred, p); + if (error) + return (error); + vp = lvp; + goto unionread; + } + } +} +#endif /* UNION */ + + if ((uap->count == auio.uio_resid) && + (vp->v_flag & VROOT) && + (vp->v_mount->mnt_flag & MNT_UNION)) { + struct vnode *tvp = vp; + vp = vp->v_mount->mnt_vnodecovered; + VREF(vp); + fp->f_data = (caddr_t) vp; + fp->f_offset = 0; + vrele(tvp); + goto unionread; + } error = copyout((caddr_t)&loff, (caddr_t)uap->basep, sizeof(long)); *retval = uap->count - auio.uio_resid; return (error); } -#endif +#endif /* COMPAT_43 */ /* * Read a block of directory entries in a file system independent format. @@ -1959,7 +2197,7 @@ getdirentries(p, uap, retval) struct uio auio; struct iovec aiov; long loff; - int error; + int error, eofflag; if (error = getvnode(p->p_fd, uap->fd, &fp)) return (error); @@ -1979,7 +2217,7 @@ unionread: auio.uio_resid = uap->count; VOP_LOCK(vp); loff = auio.uio_offset = fp->f_offset; - error = VOP_READDIR(vp, &auio, fp->f_cred); + error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, (u_long *)0, 0); fp->f_offset = auio.uio_offset; VOP_UNLOCK(vp); if (error) @@ -1988,27 +2226,41 @@ unionread: #ifdef UNION { extern int (**union_vnodeop_p)(); - extern struct vnode *union_lowervp __P((struct vnode *)); + extern struct vnode *union_dircache __P((struct vnode *)); if ((uap->count == auio.uio_resid) && (vp->v_op == union_vnodeop_p)) { - struct vnode *tvp = vp; + struct vnode *lvp; - vp = union_lowervp(vp); - if (vp != NULLVP) { - VOP_LOCK(vp); - error = VOP_OPEN(vp, FREAD); - VOP_UNLOCK(vp); + lvp = union_dircache(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) { + vput(lvp); + lvp = NULL; + } + } + + if (lvp != NULLVP) { + error = VOP_OPEN(lvp, FREAD, fp->f_cred, p); + VOP_UNLOCK(lvp); if (error) { - vrele(vp); + vrele(lvp); return (error); } - fp->f_data = (caddr_t) vp; + fp->f_data = (caddr_t) lvp; fp->f_offset = 0; - error = vn_close(tvp, FREAD, fp->f_cred, p); + error = vn_close(vp, FREAD, fp->f_cred, p); if (error) return (error); + vp = lvp; goto unionread; } }