Index: cddl/contrib/opensolaris/cmd/zlook/zlook.c
===================================================================
--- cddl/contrib/opensolaris/cmd/zlook/zlook.c	(revision 0)
+++ cddl/contrib/opensolaris/cmd/zlook/zlook.c	(revision 0)
@@ -0,0 +1,412 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This is a test program that uses ioctls to the ZFS Unit Test driver
+ * to perform readdirs or lookups using flags not normally available
+ * to user-land programs.  This allows testing of the flags'
+ * behavior outside of a complicated consumer, such as the SMB driver.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/dirent.h>
+#include <sys/attr.h>
+#include <stddef.h>
+#include <fcntl.h>
+#include <string.h>
+#include <time.h>
+
+#define	_KERNEL
+
+#include <sys/fs/zut.h>
+#include <sys/extdirent.h>
+
+#undef	_KERNEL
+
+#define	MAXBUF (64 * 1024)
+#define	BIGBUF 4096
+#define	LILBUF 64
+
+#define	DIRENT_NAMELEN(reclen)	\
+	((reclen) - (offsetof(dirent_t, d_name[0])))
+
+static void
+usage(char *pnam)
+{
+	(void) fprintf(stderr, "Usage:\n    %s -l [-is] dir-to-look-in "
+	    "file-in-dir [xfile-on-file]\n", pnam);
+	(void) fprintf(stderr, "    %s -i [-ls] dir-to-look-in "
+	    "file-in-dir [xfile-on-file]\n", pnam);
+	(void) fprintf(stderr, "    %s -s [-il] dir-to-look-in "
+	    "file-in-dir [xfile-on-file]\n", pnam);
+	(void) fprintf(stderr, "\t    Perform a lookup\n");
+	(void) fprintf(stderr, "\t    -l == lookup\n");
+	(void) fprintf(stderr, "\t    -i == request FIGNORECASE\n");
+	(void) fprintf(stderr, "\t    -s == request stat(2) and xvattr info\n");
+	(void) fprintf(stderr, "    %s -r [-ea] [-b buffer-size-in-bytes] "
+	    "dir-to-look-in [file-in-dir]\n", pnam);
+	(void) fprintf(stderr, "    %s -e [-ra] [-b buffer-size-in-bytes] "
+	    "dir-to-look-in [file-in-dir]\n", pnam);
+	(void) fprintf(stderr, "    %s -a [-re] [-b buffer-size-in-bytes] "
+	    "dir-to-look-in [file-in-dir]\n", pnam);
+	(void) fprintf(stderr, "\t    Perform a readdir\n");
+	(void) fprintf(stderr, "\t    -r == readdir\n");
+	(void) fprintf(stderr, "\t    -e == request extended entries\n");
+	(void) fprintf(stderr, "\t    -a == request access filtering\n");
+	(void) fprintf(stderr, "\t    -b == buffer size (default 4K)\n");
+	(void) fprintf(stderr, "    %s -A path\n", pnam);
+	(void) fprintf(stderr, "\t    Look up _PC_ACCESS_FILTERING "
+	    "for path with pathconf(2)\n");
+	(void) fprintf(stderr, "    %s -E path\n", pnam);
+	(void) fprintf(stderr, "\t    Look up _PC_SATTR_EXISTS "
+	    "for path with pathconf(2)\n");
+	(void) fprintf(stderr, "    %s -S path\n", pnam);
+	(void) fprintf(stderr, "\t    Look up _PC_SATTR_EXISTS "
+	    "for path with pathconf(2)\n");
+	exit(EINVAL);
+}
+
+static void
+print_extd_entries(zut_readdir_t *r)
+{
+	struct edirent *eodp;
+	char *bufstart;
+
+	eodp = (edirent_t *)(uintptr_t)r->zr_buf;
+	bufstart = (char *)eodp;
+	while ((char *)eodp < bufstart + r->zr_bytes) {
+		char *blanks = "                ";
+		int i = 0;
+		while (i < EDIRENT_NAMELEN(eodp->ed_reclen)) {
+			if (!eodp->ed_name[i])
+				break;
+			(void) printf("%c", eodp->ed_name[i++]);
+		}
+		if (i < 16)
+			(void) printf("%.*s", 16 - i, blanks);
+		(void) printf("\t%x\n", eodp->ed_eflags);
+		eodp = (edirent_t *)((intptr_t)eodp + eodp->ed_reclen);
+	}
+}
+
+static void
+print_entries(zut_readdir_t *r)
+{
+	dirent64_t *dp;
+	char *bufstart;
+
+	dp = (dirent64_t *)r->zr_buf;
+	bufstart = (char *)dp;
+	while ((char *)dp < bufstart + r->zr_bytes) {
+		int i = 0;
+		while (i < DIRENT_NAMELEN(dp->d_reclen)) {
+			if (!dp->d_name[i])
+				break;
+			(void) printf("%c", dp->d_name[i++]);
+		}
+		(void) printf("\n");
+		dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen);
+	}
+}
+
+static void
+print_stats(struct stat64 *sb)
+{
+	char timebuf[512];
+
+	(void) printf("st_mode\t\t\t%04lo\n", (unsigned long)sb->st_mode);
+	(void) printf("st_ino\t\t\t%llu\n", (unsigned long long)sb->st_ino);
+	(void) printf("st_nlink\t\t%lu\n", (unsigned long)sb->st_nlink);
+	(void) printf("st_uid\t\t\t%d\n", sb->st_uid);
+	(void) printf("st_gid\t\t\t%d\n", sb->st_gid);
+	(void) printf("st_size\t\t\t%lld\n", (long long)sb->st_size);
+	(void) printf("st_blksize\t\t%ld\n", (long)sb->st_blksize);
+	(void) printf("st_blocks\t\t%lld\n", (long long)sb->st_blocks);
+
+	timebuf[0] = 0;
+	if (ctime_r(&sb->st_atime, timebuf, 512)) {
+		(void) printf("st_atime\t\t");
+		(void) printf("%s", timebuf);
+	}
+	timebuf[0] = 0;
+	if (ctime_r(&sb->st_mtime, timebuf, 512)) {
+		(void) printf("st_mtime\t\t");
+		(void) printf("%s", timebuf);
+	}
+	timebuf[0] = 0;
+	if (ctime_r(&sb->st_ctime, timebuf, 512)) {
+		(void) printf("st_ctime\t\t");
+		(void) printf("%s", timebuf);
+	}
+}
+
+static void
+print_xvs(uint64_t xvs)
+{
+	uint_t bits;
+	int idx = 0;
+
+	if (xvs == 0)
+		return;
+
+	(void) printf("-------------------\n");
+	(void) printf("Attribute bit(s) set:\n");
+	(void) printf("-------------------\n");
+
+	bits = xvs & ((1 << F_ATTR_ALL) - 1);
+	while (bits) {
+		uint_t rest = bits >> 1;
+		if (bits & 1) {
+			(void) printf("%s", attr_to_name((f_attr_t)idx));
+			if (rest)
+				(void) printf(", ");
+		}
+		idx++;
+		bits = rest;
+	}
+	(void) printf("\n");
+}
+
+int
+main(int argc, char **argv)
+{
+	zut_lookup_t lk = {0};
+	zut_readdir_t rd = {0};
+	boolean_t checking = B_FALSE;
+	boolean_t looking = B_FALSE;
+	boolean_t reading = B_FALSE;
+	boolean_t bflag = B_FALSE;
+	long rddir_bufsize = BIGBUF;
+	int error = 0;
+	int check;
+	int fd;
+	int c;
+
+	while ((c = getopt(argc, argv, "lisaerb:ASE")) != -1) {
+		switch (c) {
+		case 'l':
+			looking = B_TRUE;
+			break;
+		case 'i':
+			lk.zl_reqflags |= ZUT_IGNORECASE;
+			looking = B_TRUE;
+			break;
+		case 's':
+			lk.zl_reqflags |= ZUT_GETSTAT;
+			looking = B_TRUE;
+			break;
+		case 'a':
+			rd.zr_reqflags |= ZUT_ACCFILTER;
+			reading = B_TRUE;
+			break;
+		case 'e':
+			rd.zr_reqflags |= ZUT_EXTRDDIR;
+			reading = B_TRUE;
+			break;
+		case 'r':
+			reading = B_TRUE;
+			break;
+		case 'b':
+			reading = B_TRUE;
+			bflag = B_TRUE;
+			rddir_bufsize = strtol(optarg, NULL, 0);
+			break;
+		case 'A':
+			checking = B_TRUE;
+			check = _PC_ACCESS_FILTERING;
+			break;
+		case 'S':
+			checking = B_TRUE;
+			check = _PC_SATTR_ENABLED;
+			break;
+		case 'E':
+			checking = B_TRUE;
+			check = _PC_SATTR_EXISTS;
+			break;
+		case '?':
+		default:
+			usage(argv[0]);		/* no return */
+		}
+	}
+
+	if ((checking && looking) || (checking && reading) ||
+	    (looking && reading) || (!reading && bflag) ||
+	    (!checking && !reading && !looking))
+		usage(argv[0]);		/* no return */
+
+	if (rddir_bufsize < LILBUF || rddir_bufsize > MAXBUF) {
+		(void) fprintf(stderr, "Sorry, buffer size "
+		    "must be >= %d and less than or equal to %d bytes.\n",
+		    LILBUF, MAXBUF);
+		exit(EINVAL);
+	}
+
+	if (checking) {
+		char pathbuf[MAXPATHLEN];
+		long result;
+
+		if (argc - optind < 1)
+			usage(argv[0]);		/* no return */
+		(void) strlcpy(pathbuf, argv[optind], MAXPATHLEN);
+		result = pathconf(pathbuf, check);
+		(void) printf("pathconf(2) check for %s\n", pathbuf);
+		switch (check) {
+		case _PC_SATTR_ENABLED:
+			(void) printf("System attributes ");
+			if (result != 0)
+				(void) printf("Enabled\n");
+			else
+				(void) printf("Not enabled\n");
+			break;
+		case _PC_SATTR_EXISTS:
+			(void) printf("System attributes ");
+			if (result != 0)
+				(void) printf("Exist\n");
+			else
+				(void) printf("Do not exist\n");
+			break;
+		case _PC_ACCESS_FILTERING:
+			(void) printf("Access filtering ");
+			if (result != 0)
+				(void) printf("Available\n");
+			else
+				(void) printf("Not available\n");
+			break;
+		}
+		return (result);
+	}
+
+	if ((fd = open(ZUT_DEV, O_RDONLY)) < 0) {
+		perror(ZUT_DEV);
+		return (ENXIO);
+	}
+
+	if (reading) {
+		char *buf;
+
+		if (argc - optind < 1)
+			usage(argv[0]);		/* no return */
+
+		(void) strlcpy(rd.zr_dir, argv[optind], MAXPATHLEN);
+		if (argc - optind > 1) {
+			(void) strlcpy(rd.zr_file, argv[optind + 1],
+			    MAXNAMELEN);
+			rd.zr_reqflags |= ZUT_XATTR;
+		}
+
+		if ((buf = malloc(rddir_bufsize)) == NULL) {
+			error = errno;
+			perror("malloc");
+			(void) close(fd);
+			return (error);
+		}
+
+		rd.zr_buf = (uint64_t)(uintptr_t)buf;
+		rd.zr_buflen = rddir_bufsize;
+
+		while (!rd.zr_eof) {
+			int ierr;
+
+			if ((ierr = ioctl(fd, ZUT_IOC_READDIR, &rd)) != 0) {
+				(void) fprintf(stderr,
+				    "IOCTL error: %s (%d)\n",
+				    strerror(ierr), ierr);
+				free(buf);
+				(void) close(fd);
+				return (ierr);
+			}
+			if (rd.zr_retcode) {
+				(void) fprintf(stderr,
+				    "readdir result: %s (%d)\n",
+				    strerror(rd.zr_retcode), rd.zr_retcode);
+				free(buf);
+				(void) close(fd);
+				return (rd.zr_retcode);
+			}
+			if (rd.zr_reqflags & ZUT_EXTRDDIR)
+				print_extd_entries(&rd);
+			else
+				print_entries(&rd);
+		}
+		free(buf);
+	} else {
+		int ierr;
+
+		if (argc - optind < 2)
+			usage(argv[0]);		/* no return */
+
+		(void) strlcpy(lk.zl_dir, argv[optind], MAXPATHLEN);
+		(void) strlcpy(lk.zl_file, argv[optind + 1], MAXNAMELEN);
+		if (argc - optind > 2) {
+			(void) strlcpy(lk.zl_xfile,
+			    argv[optind + 2], MAXNAMELEN);
+			lk.zl_reqflags |= ZUT_XATTR;
+		}
+
+		if ((ierr = ioctl(fd, ZUT_IOC_LOOKUP, &lk)) != 0) {
+			(void) fprintf(stderr,
+			    "IOCTL error: %s (%d)\n",
+			    strerror(ierr), ierr);
+			(void) close(fd);
+			return (ierr);
+		}
+
+		(void) printf("\nLookup of ");
+		if (lk.zl_reqflags & ZUT_XATTR) {
+			(void) printf("extended attribute \"%s\" of ",
+			    lk.zl_xfile);
+		}
+		(void) printf("file \"%s\" ", lk.zl_file);
+		(void) printf("in directory \"%s\" ", lk.zl_dir);
+		if (lk.zl_retcode) {
+			(void) printf("failed: %s (%d)\n",
+			    strerror(lk.zl_retcode), lk.zl_retcode);
+			(void) close(fd);
+			return (lk.zl_retcode);
+		}
+
+		(void) printf("succeeded.\n");
+		if (lk.zl_reqflags & ZUT_IGNORECASE) {
+			(void) printf("----------------------------\n");
+			(void) printf("dirent flags: 0x%0x\n", lk.zl_deflags);
+			(void) printf("real name: %s\n", lk.zl_real);
+		}
+		if (lk.zl_reqflags & ZUT_GETSTAT) {
+			(void) printf("----------------------------\n");
+			print_stats(&lk.zl_statbuf);
+			print_xvs(lk.zl_xvattrs);
+		}
+	}
+
+	(void) close(fd);
+	return (0);
+}
Index: cddl/usr.bin/Makefile
===================================================================
--- cddl/usr.bin/Makefile	(revision 210395)
+++ cddl/usr.bin/Makefile	(working copy)
@@ -8,10 +8,12 @@
 	ctfmerge \
 	sgsmsg \
 	${_zinject} \
+	${_zlook} \
 	${_ztest}
 
 .if ${MK_ZFS} != "no"
 _zinject= zinject
+#_zlook= zlook
 .if ${MK_LIBTHR} != "no"
 _ztest=	ztest
 .endif
Index: cddl/usr.bin/zlook/Makefile
===================================================================
--- cddl/usr.bin/zlook/Makefile	(revision 0)
+++ cddl/usr.bin/zlook/Makefile	(revision 0)
@@ -0,0 +1,25 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../contrib/opensolaris/cmd/zlook
+
+PROG=	zlook
+NO_MAN=
+
+WARNS?=	0
+CFLAGS+= -I${.CURDIR}/../../../sys/cddl/compat/opensolaris
+#CFLAGS+= -I${.CURDIR}/../../compat/opensolaris/include
+#CFLAGS+= -I${.CURDIR}/../../compat/opensolaris/lib/libumem
+#CFLAGS+= -I${.CURDIR}/../../contrib/opensolaris/lib/libzfs/common
+#CFLAGS+= -I${.CURDIR}/../../contrib/opensolaris/lib/libzpool/common
+#CFLAGS+= -I${.CURDIR}/../../contrib/opensolaris/lib/libnvpair
+#CFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/fs/zfs
+#CFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/sys
+CFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common
+#CFLAGS+= -I${.CURDIR}/../../contrib/opensolaris/head
+#CFLAGS+= -I${.CURDIR}/../../lib/libumem
+#
+#DPADD=	${LIBAVL} ${LIBGEOM} ${LIBM} ${LIBNVPAIR} ${LIBUMEM} ${LIBUUTIL} \
+#	${LIBZFS} ${LIBZPOOL}
+#LDADD=	-lavl -lgeom -lm -lnvpair -lumem -luutil -lzfs -lzpool
+
+.include <bsd.prog.mk>
Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c
===================================================================
--- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c	(revision 210395)
+++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c	(working copy)
@@ -2274,6 +2274,21 @@
 			}
 		}
 
+		if (flags & V_RDDIR_ACCFILTER) {
+			/*
+			 * If we have no access at all, don't include
+			 * this entry in the returned information
+			 */
+			znode_t	*ezp;
+			if (zfs_zget(zp->z_zfsvfs, objnum, &ezp) != 0)
+				goto skip_entry;
+			if (!zfs_has_access(ezp, cr)) {
+				VN_RELE(ZTOV(ezp));
+				goto skip_entry;
+			}
+			VN_RELE(ZTOV(ezp));
+		}
+
 		if (flags & V_RDDIR_ENTFLAGS)
 			reclen = EDIRENT_RECLEN(strlen(zap.za_name));
 		else
@@ -2324,6 +2339,7 @@
 		if (prefetch)
 			dmu_prefetch(os, objnum, 0, 0);
 
+	skip_entry:
 		/*
 		 * Move to the next entry, fill in the previous offset.
 		 */
Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c
===================================================================
--- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c	(revision 210395)
+++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c	(working copy)
@@ -983,6 +983,7 @@
 		vfs_set_feature(zfsvfs->z_vfs, VFSFT_SYSATTR_VIEWS);
 		vfs_set_feature(zfsvfs->z_vfs, VFSFT_ACEMASKONACCESS);
 		vfs_set_feature(zfsvfs->z_vfs, VFSFT_ACLONCREATE);
+		vfs_set_feature(zfsvfs->z_vfs, VFSFT_ACCESS_FILTER);
 	}
 }
 
Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_acl.c
===================================================================
--- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_acl.c	(revision 210395)
+++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_acl.c	(working copy)
@@ -2166,46 +2166,17 @@
 }
 
 /*
- * working_mode returns the permissions that were not granted
+ * Check accesses of interest (AoI) against attributes of the dataset
+ * such as read-only.  Returns zero if no AoI conflict with dataset
+ * attributes, otherwise an appropriate errno is returned.
  */
 static int
-zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode,
-    boolean_t *check_privs, boolean_t skipaclchk, cred_t *cr)
+zfs_zaccess_dataset_check(znode_t *zp, uint32_t v4_mode)
 {
-	zfs_acl_t	*aclp;
-	zfsvfs_t	*zfsvfs = zp->z_zfsvfs;
-	int		error;
-	uid_t		uid = crgetuid(cr);
-	uint64_t 	who;
-	uint16_t	type, iflags;
-	uint16_t	entry_type;
-	uint32_t	access_mask;
-	uint32_t	deny_mask = 0;
-	zfs_ace_hdr_t	*acep = NULL;
-	boolean_t	checkit;
-	uid_t		fowner;
-	uid_t		gowner;
-
-	/*
-	 * Short circuit empty requests
-	 */
-	if (v4_mode == 0)
-		return (0);
-
-	*check_privs = B_TRUE;
-
-	if (zfsvfs->z_replay) {
-		*working_mode = 0;
-		return (0);
-	}
-
-	*working_mode = v4_mode;
-
 	if ((v4_mode & WRITE_MASK) &&
 	    (zp->z_zfsvfs->z_vfs->vfs_flag & VFS_RDONLY) &&
 	    (!IS_DEVVP(ZTOV(zp)) ||
 	    (IS_DEVVP(ZTOV(zp)) && (v4_mode & WRITE_MASK_ATTRS)))) {
-		*check_privs = B_FALSE;
 		return (EROFS);
 	}
 
@@ -2217,45 +2188,65 @@
 	    (zp->z_phys->zp_flags & (ZFS_READONLY | ZFS_IMMUTABLE))) ||
 	    (ZTOV(zp)->v_type == VDIR &&
 	    (zp->z_phys->zp_flags & ZFS_IMMUTABLE)))) {
-		*check_privs = B_FALSE;
 		return (EPERM);
 	}
 
-#ifdef sun
 	if ((v4_mode & (ACE_DELETE | ACE_DELETE_CHILD)) &&
 	    (zp->z_phys->zp_flags & ZFS_NOUNLINK)) {
-		*check_privs = B_FALSE;
 		return (EPERM);
 	}
-#else
-	/*
-	 * In FreeBSD we allow to modify directory's content is ZFS_NOUNLINK
-	 * (sunlnk) is set. We just don't allow directory removal, which is
-	 * handled in zfs_zaccess_delete().
-	 */
-	if ((v4_mode & ACE_DELETE) &&
-	    (zp->z_phys->zp_flags & ZFS_NOUNLINK)) {
-		*check_privs = B_FALSE;
-		return (EPERM);
-	}
-#endif
 
 	if (((v4_mode & (ACE_READ_DATA|ACE_EXECUTE)) &&
 	    (zp->z_phys->zp_flags & ZFS_AV_QUARANTINED))) {
-		*check_privs = B_FALSE;
 		return (EACCES);
 	}
 
-	/*
-	 * The caller requested that the ACL check be skipped.  This
-	 * would only happen if the caller checked VOP_ACCESS() with a
-	 * 32 bit ACE mask and already had the appropriate permissions.
-	 */
-	if (skipaclchk) {
-		*working_mode = 0;
-		return (0);
-	}
+	return (0);
+}
 
+/*
+ * The primary usage of this function is to loop through all of the
+ * ACEs in the znode, determining what accesses of interest (AoI) to
+ * the caller are allowed or denied.  The AoI are expressed as bits in
+ * the working_mode parameter.  As each ACE is processed, bits covered
+ * by that ACE are removed from the working_mode.  This removal
+ * facilitates two things.  The first is that when the working mode is
+ * empty (= 0), we know we've looked at all the AoI. The second is
+ * that the ACE interpretation rules don't allow a later ACE to undo
+ * something granted or denied by an earlier ACE.  Removing the
+ * discovered access or denial enforces this rule.  At the end of
+ * processing the ACEs, all AoI that were found to be denied are
+ * placed into the working_mode, giving the caller a mask of denied
+ * accesses.  Returns:
+ *	0		if all AoI granted
+ *	EACCESS 	if the denied mask is non-zero
+ *	other error	if abnormal failure (e.g., IO error)
+ *
+ * A secondary usage of the function is to determine if any of the
+ * AoI are granted.  If an ACE grants any access in
+ * the working_mode, we immediately short circuit out of the function.
+ * This mode is chosen by setting anyaccess to B_TRUE.  The
+ * working_mode is not a denied access mask upon exit if the function
+ * is used in this manner.
+ */
+static int
+zfs_zaccess_aces_check(znode_t *zp, uint32_t *working_mode,
+    boolean_t anyaccess, cred_t *cr)
+{
+	zfsvfs_t	*zfsvfs = zp->z_zfsvfs;
+	zfs_acl_t	*aclp;
+	int		error;
+	uid_t		uid = crgetuid(cr);
+	uint64_t 	who;
+	uint16_t	type, iflags;
+	uint16_t	entry_type;
+	uint32_t	access_mask;
+	uint32_t	deny_mask = 0;
+	zfs_ace_hdr_t	*acep = NULL;
+	boolean_t	checkit;
+	uid_t		fowner;
+	uid_t		gowner;
+
 	zfs_fuid_map_ids(zp, cr, &fowner, &gowner);
 
 	mutex_enter(&zp->z_acl_lock);
@@ -2268,6 +2259,7 @@
 
 	while (acep = zfs_acl_next_ace(aclp, acep, &who, &access_mask,
 	    &iflags, &type)) {
+		uint32_t mask_matched;
 
 		if (!zfs_acl_valid_ace_type(type, iflags))
 			continue;
@@ -2275,6 +2267,11 @@
 		if (ZTOV(zp)->v_type == VDIR && (iflags & ACE_INHERIT_ONLY_ACE))
 			continue;
 
+		/* Skip ACE if it does not affect any AoI */
+		mask_matched = (access_mask & *working_mode);
+		if (!mask_matched)
+			continue;
+
 		entry_type = (iflags & ACE_TYPE_FLAGS);
 
 		checkit = B_FALSE;
@@ -2313,14 +2310,24 @@
 		}
 
 		if (checkit) {
-			uint32_t mask_matched = (access_mask & *working_mode);
-
-			if (mask_matched) {
-				if (type == DENY)
-					deny_mask |= mask_matched;
-
-				*working_mode &= ~mask_matched;
+			if (type == DENY) {
+				DTRACE_PROBE3(zfs__ace__denies,
+				    znode_t *, zp,
+				    zfs_ace_hdr_t *, acep,
+				    uint32_t, mask_matched);
+				deny_mask |= mask_matched;
+			} else {
+				DTRACE_PROBE3(zfs__ace__allows,
+				    znode_t *, zp,
+				    zfs_ace_hdr_t *, acep,
+				    uint32_t, mask_matched);
+				if (anyaccess) {
+					mutex_exit(&zp->z_acl_lock);
+					zfs_acl_free(aclp);
+					return (0);
+				}
 			}
+			*working_mode &= ~mask_matched;
 		}
 
 		/* Are we done? */
@@ -2342,7 +2349,69 @@
 	return (0);
 }
 
+/*
+ * Return true if any access whatsoever granted, we don't actually
+ * care what access is granted.
+ */
+boolean_t
+zfs_has_access(znode_t *zp, cred_t *cr)
+{
+	uint32_t have = ACE_ALL_PERMS;
+
+	if (zfs_zaccess_aces_check(zp, &have, B_TRUE, cr) != 0) {
+		uid_t		owner;
+
+		owner = zfs_fuid_map_id(zp->z_zfsvfs,
+		    zp->z_phys->zp_uid, cr, ZFS_OWNER);
+
+		return (
+		    secpolicy_vnode_access(cr, ZTOV(zp), owner, VREAD) == 0 ||
+		    secpolicy_vnode_access(cr, ZTOV(zp), owner, VWRITE) == 0 ||
+		    secpolicy_vnode_access(cr, ZTOV(zp), owner, VEXEC) == 0 ||
+		    secpolicy_vnode_chown(ZTOV(zp), cr, owner) == 0 ||
+		    secpolicy_vnode_setdac(ZTOV(zp), cr, owner) == 0 ||
+		    secpolicy_vnode_remove(ZTOV(zp), cr) == 0);
+	}
+	return (B_TRUE);
+}
+
 static int
+zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode,
+    boolean_t *check_privs, boolean_t skipaclchk, cred_t *cr)
+{
+	zfsvfs_t *zfsvfs = zp->z_zfsvfs;
+	int err;
+
+	*working_mode = v4_mode;
+	*check_privs = B_TRUE;
+
+	/*
+	 * Short circuit empty requests
+	 */
+	if (v4_mode == 0 || zfsvfs->z_replay) {
+		*working_mode = 0;
+		return (0);
+	}
+
+	if ((err = zfs_zaccess_dataset_check(zp, v4_mode)) != 0) {
+		*check_privs = B_FALSE;
+		return (err);
+	}
+
+	/*
+	 * The caller requested that the ACL check be skipped.  This
+	 * would only happen if the caller checked VOP_ACCESS() with a
+	 * 32 bit ACE mask and already had the appropriate permissions.
+	 */
+	if (skipaclchk) {
+		*working_mode = 0;
+		return (0);
+	}
+
+	return (zfs_zaccess_aces_check(zp, working_mode, B_FALSE, cr));
+}
+
+static int
 zfs_zaccess_append(znode_t *zp, uint32_t *working_mode, boolean_t *check_privs,
     cred_t *cr)
 {
@@ -2457,7 +2526,7 @@
 			    owner, checkmode);
 
 		if (error == 0 && (working_mode & ACE_WRITE_OWNER))
-			error = secpolicy_vnode_chown(ZTOV(check_zp), cr, B_TRUE);
+			error = secpolicy_vnode_chown(ZTOV(check_zp), cr, owner);
 		if (error == 0 && (working_mode & ACE_WRITE_ACL))
 			error = secpolicy_vnode_setdac(ZTOV(check_zp), cr, owner);
 
@@ -2466,7 +2535,7 @@
 			error = secpolicy_vnode_remove(ZTOV(check_zp), cr);
 
 		if (error == 0 && (working_mode & ACE_SYNCHRONIZE)) {
-			error = secpolicy_vnode_chown(ZTOV(check_zp), cr, B_FALSE);
+			error = secpolicy_vnode_chown(ZTOV(check_zp), cr, owner);
 		}
 		if (error == 0) {
 			/*
Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_acl.h
===================================================================
--- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_acl.h	(revision 210395)
+++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_acl.h	(working copy)
@@ -200,6 +200,7 @@
 void zfs_acl_rele(void *);
 void zfs_oldace_byteswap(ace_t *, int);
 void zfs_ace_byteswap(void *, size_t, boolean_t);
+extern boolean_t zfs_has_access(struct znode *zp, cred_t *cr);
 extern int zfs_zaccess(struct znode *, int, int, boolean_t, cred_t *);
 extern int zfs_zaccess_rwx(struct znode *, mode_t, int, cred_t *);
 extern int zfs_zaccess_unix(struct znode *, mode_t, cred_t *);
Index: sys/cddl/contrib/opensolaris/uts/common/sys/vnode.h
===================================================================
--- sys/cddl/contrib/opensolaris/uts/common/sys/vnode.h	(revision 210395)
+++ sys/cddl/contrib/opensolaris/uts/common/sys/vnode.h	(working copy)
@@ -378,6 +378,7 @@
  * Flags for VOP_READDIR
  */
 #define	V_RDDIR_ENTFLAGS	0x01	/* request dirent flags */
+#define	V_RDDIR_ACCFILTER	0x02	/* filter out inaccessible dirents */
 
 /*
  * Public vnode manipulation functions.
Index: sys/cddl/contrib/opensolaris/uts/common/Makefile.files
===================================================================
--- sys/cddl/contrib/opensolaris/uts/common/Makefile.files	(revision 210395)
+++ sys/cddl/contrib/opensolaris/uts/common/Makefile.files	(working copy)
@@ -19,6 +19,9 @@
 # CDDL HEADER END
 #
 
+ZUT_OBJS +=			\
+	zut.o
+
 #
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
Index: sys/cddl/compat/opensolaris/kern/opensolaris_policy.c
===================================================================
--- sys/cddl/compat/opensolaris/kern/opensolaris_policy.c	(revision 210395)
+++ sys/cddl/compat/opensolaris/kern/opensolaris_policy.c	(working copy)
@@ -332,7 +332,7 @@
 }
 
 int
-secpolicy_vnode_chown(struct vnode *vp, cred_t *cred, boolean_t check_self)
+secpolicy_vnode_chown(struct vnode *vp, cred_t *cred, uid_t owner)
 {
 
 	if (secpolicy_fs_owner(vp->v_mount, cred) == 0)
Index: sys/cddl/compat/opensolaris/sys/policy.h
===================================================================
--- sys/cddl/compat/opensolaris/sys/policy.h	(revision 210395)
+++ sys/cddl/compat/opensolaris/sys/policy.h	(working copy)
@@ -47,8 +47,7 @@
 int	secpolicy_fs_unmount(struct ucred *cred, struct mount *vfsp);
 int	secpolicy_basic_link(struct vnode *vp, struct ucred *cred);
 int	secpolicy_vnode_owner(struct vnode *vp, cred_t *cred, uid_t owner);
-int	secpolicy_vnode_chown(struct vnode *vp, cred_t *cred,
-	    boolean_t check_self);
+int	secpolicy_vnode_chown(struct vnode *vp, cred_t *cred, uid_t owner);
 int	secpolicy_vnode_stky_modify(struct ucred *cred);
 int	secpolicy_vnode_remove(struct vnode *vp, struct ucred *cred);
 int	secpolicy_vnode_access(struct ucred *cred, struct vnode *vp,
