Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_dir.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_dir.c (revision 195484) +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_dir.c (working copy) @@ -906,12 +906,10 @@ top: ASSERT(zp->z_phys->zp_xattr == 0); -#ifdef TODO if (!(flags & CREATE_XATTR_DIR)) { zfs_dirent_unlock(dl); - return (ENOENT); + return (ENOATTR); } -#endif if (zfsvfs->z_vfs->vfs_flag & VFS_RDONLY) { zfs_dirent_unlock(dl); Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c (revision 195484) +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c (working copy) @@ -1134,15 +1134,12 @@ zfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, } /* - * Do we have permission to get into attribute directory? + * In FreeBSD, read/lookup EA access is controlled by the + * read permission on the containing file; we don't care + * about permissions on the attribute directory + * and we don't need to check it here, like SunOS does. */ - if (error = zfs_zaccess(VTOZ(*vpp), ACE_EXECUTE, 0, - B_FALSE, cr)) { - VN_RELE(*vpp); - *vpp = NULL; - } - ZFS_EXIT(zfsvfs); return (error); } @@ -4502,6 +4499,11 @@ vop_getextattr { vnode_t *xvp = NULL, *vp; int error, flags; + error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, + ap->a_cred, ap->a_td, VREAD); + if (error != 0) + return (error); + error = zfs_create_attrname(ap->a_attrnamespace, ap->a_name, attrname, sizeof(attrname)); if (error != 0) @@ -4519,10 +4521,13 @@ vop_getextattr { flags = FREAD; NDINIT_ATVP(&nd, LOOKUP, NOFOLLOW | MPSAFE, UIO_SYSSPACE, attrname, xvp, td); - error = vn_open_cred(&nd, &flags, 0, 0, ap->a_cred, NULL); + error = vn_open_cred(&nd, &flags, 0, 0, ap->a_vp->v_mount->mnt_cred, + NULL); vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); if (error != 0) { + if (error == ENOENT) + error = ENOATTR; ZFS_EXIT(zfsvfs); return (error); } @@ -4564,6 +4569,11 @@ vop_deleteextattr { vnode_t *xvp = NULL, *vp; int error, flags; + error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, + ap->a_cred, ap->a_td, VWRITE); + if (error != 0) + return (error); + error = zfs_create_attrname(ap->a_attrnamespace, ap->a_name, attrname, sizeof(attrname)); if (error != 0) @@ -4584,10 +4594,13 @@ vop_deleteextattr { vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); if (error != 0) { + if (error == ENOENT) + error = ENOATTR; ZFS_EXIT(zfsvfs); return (error); } - error = VOP_REMOVE(nd.ni_dvp, vp, &nd.ni_cnd); + error = zfs_remove(nd.ni_dvp, nd.ni_cnd.cn_nameptr, + ap->a_vp->v_mount->mnt_cred, NULL, 0); vput(nd.ni_dvp); if (vp == nd.ni_dvp) @@ -4623,6 +4636,11 @@ vop_setextattr { vnode_t *xvp = NULL, *vp; int error, flags; + error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, + ap->a_cred, ap->a_td, VWRITE); + if (error != 0) + return (error); + error = zfs_create_attrname(ap->a_attrnamespace, ap->a_name, attrname, sizeof(attrname)); if (error != 0) @@ -4631,7 +4649,7 @@ vop_setextattr { ZFS_ENTER(zfsvfs); error = zfs_lookup(ap->a_vp, NULL, &xvp, NULL, 0, ap->a_cred, td, - LOOKUP_XATTR); + LOOKUP_XATTR | CREATE_XATTR_DIR); if (error != 0) { ZFS_EXIT(zfsvfs); return (error); @@ -4640,7 +4658,8 @@ vop_setextattr { flags = FFLAGS(O_WRONLY | O_CREAT); NDINIT_ATVP(&nd, LOOKUP, NOFOLLOW | MPSAFE, UIO_SYSSPACE, attrname, xvp, td); - error = vn_open_cred(&nd, &flags, 0600, 0, ap->a_cred, NULL); + error = vn_open_cred(&nd, &flags, 0600, 0, + ap->a_vp->v_mount->mnt_cred, NULL); vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); if (error != 0) { @@ -4650,7 +4669,7 @@ vop_setextattr { VATTR_NULL(&va); va.va_size = 0; - error = VOP_SETATTR(vp, &va, ap->a_cred); + error = VOP_SETATTR(vp, &va, ap->a_vp->v_mount->mnt_cred); if (error == 0) VOP_WRITE(vp, ap->a_uio, IO_UNIT | IO_SYNC, ap->a_cred); @@ -4690,6 +4709,11 @@ vop_listextattr { vnode_t *xvp = NULL, *vp; int done, error, eof, pos; + error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, + ap->a_cred, ap->a_td, VREAD); + if (error) + return (error); + error = zfs_create_attrname(ap->a_attrnamespace, "", attrprefix, sizeof(attrprefix)); if (error != 0) @@ -4701,6 +4725,12 @@ vop_listextattr { error = zfs_lookup(ap->a_vp, NULL, &xvp, NULL, 0, ap->a_cred, td, LOOKUP_XATTR); if (error != 0) { + /* + * ENOATTR means that the EA directory does not yet exist, + * i.e. there are no extended attributes there. + */ + if (error == ENOATTR) + error = 0; ZFS_EXIT(zfsvfs); return (error); } Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_acl.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_acl.c (revision 195484) +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_acl.c (working copy) @@ -2361,31 +2361,16 @@ zfs_zaccess(znode_t *zp, int mode, int flags, bool (ZTOV(zp)->v_type == VDIR)); /* - * If attribute then validate against base file + * In FreeBSD, we don't care about the EA directory permissions. + * Note that not checking for it is not just an optimization - without + * this shortcut, creating new EA on a file owned by someone else, + * to which we have write permission, will fail - there is no way + * to pass proper (root) cred to namei(9); it always uses ucred + * from curthread. */ - if (is_attr) { - if ((error = zfs_zget(zp->z_zfsvfs, - zp->z_phys->zp_parent, &xzp)) != 0) { - return (error); - } + if (is_attr) + return (0); - check_zp = xzp; - - /* - * fixup mode to map to xattr perms - */ - - if (mode & (ACE_WRITE_DATA|ACE_APPEND_DATA)) { - mode &= ~(ACE_WRITE_DATA|ACE_APPEND_DATA); - mode |= ACE_WRITE_NAMED_ATTRS; - } - - if (mode & (ACE_READ_DATA|ACE_EXECUTE)) { - mode &= ~(ACE_READ_DATA|ACE_EXECUTE); - mode |= ACE_READ_NAMED_ATTRS; - } - } - if ((error = zfs_zaccess_common(check_zp, mode, &working_mode, &check_privs, skipaclchk, cr)) == 0) { if (is_attr)