]> granicus.if.org Git - zfs/commitdiff
Posix ACL Support
authorMassimo Maggi <me@massimo-maggi.eu>
Mon, 28 Oct 2013 16:22:15 +0000 (09:22 -0700)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Tue, 29 Oct 2013 21:54:26 +0000 (14:54 -0700)
This change adds support for Posix ACLs by storing them as an xattr
which is common practice for many Linux file systems.  Since the
Posix ACL is stored as an xattr it will not overwrite any existing
ZFS/NFSv4 ACLs which may have been set.  The Posix ACL will also
be non-functional on other platforms although it may be visible
as an xattr if that platform understands SA based xattrs.

By default Posix ACLs are disabled but they may be enabled with
the new 'aclmode=noacl|posixacl' property.  Set the property to
'posixacl' to enable them.  If ZFS/NFSv4 ACL support is ever added
an appropriate acltype will be added.

This change passes the POSIX Test Suite cleanly with the exception
of xacl/00.t test 45 which is incorrect for Linux (Ext4 fails too).

  http://www.tuxera.com/community/posix-test-suite/

Signed-off-by: Massimo Maggi <me@massimo-maggi.eu>
Signed-off-by: Richard Yao <ryao@gentoo.org>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #170

17 files changed:
config/kernel-acl.m4 [new file with mode: 0644]
config/kernel-xattr-handler.m4
config/kernel.m4
include/linux/vfs_compat.h
include/linux/xattr_compat.h
include/sys/fs/zfs.h
include/sys/zfs_ioctl.h
include/sys/zfs_vfsops.h
include/sys/zpl.h
man/man8/zfs.8
module/zcommon/zfs_prop.c
module/zfs/zfs_acl.c
module/zfs/zfs_vfsops.c
module/zfs/zfs_vnops.c
module/zfs/zpl_inode.c
module/zfs/zpl_super.c
module/zfs/zpl_xattr.c

diff --git a/config/kernel-acl.m4 b/config/kernel-acl.m4
new file mode 100644 (file)
index 0000000..e9a2547
--- /dev/null
@@ -0,0 +1,249 @@
+dnl #
+dnl # Check if posix_acl_release can be used from a CDDL module,
+dnl # The is_owner_or_cap macro was replaced by
+dnl # inode_owner_or_capable
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_POSIX_ACL_RELEASE], [
+       AC_MSG_CHECKING([whether posix_acl_release() is available])
+       ZFS_LINUX_TRY_COMPILE([
+               #include <linux/cred.h>
+               #include <linux/fs.h>
+               #include <linux/posix_acl.h>
+       ],[
+               struct posix_acl* tmp = posix_acl_alloc(1, 0);
+               posix_acl_release(tmp);
+       ],[
+               AC_MSG_RESULT(yes)
+               AC_DEFINE(HAVE_POSIX_ACL_RELEASE, 1,
+                   [posix_acl_release() is available])
+       ],[
+               AC_MSG_RESULT(no)
+       ])
+
+       AC_MSG_CHECKING([whether posix_acl_release() is GPL-only])
+       ZFS_LINUX_TRY_COMPILE([
+               #include <linux/cred.h>
+               #include <linux/fs.h>
+               #include <linux/posix_acl.h>
+
+               MODULE_LICENSE("CDDL");
+       ],[
+               struct posix_acl* tmp = posix_acl_alloc(1, 0);
+               posix_acl_release(tmp);
+       ],[
+               AC_MSG_RESULT(no)
+       ],[
+               AC_MSG_RESULT(yes)
+               AC_DEFINE(HAVE_POSIX_ACL_RELEASE_GPL_ONLY, 1,
+                   [posix_acl_release() is GPL-only])
+       ])
+])
+
+dnl #
+dnl # 3.1 API change,
+dnl # posix_acl_chmod_masq() is not exported anymore and posix_acl_chmod()
+dnl # was introduced to replace it.
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_POSIX_ACL_CHMOD], [
+       AC_MSG_CHECKING([whether posix_acl_chmod exists])
+       ZFS_LINUX_TRY_COMPILE([
+               #include <linux/fs.h>
+               #include <linux/posix_acl.h>
+       ],[
+               posix_acl_chmod(NULL, 0, 0)
+       ],[
+               AC_MSG_RESULT(yes)
+               AC_DEFINE(HAVE_POSIX_ACL_CHMOD, 1, [posix_acl_chmod() exists])
+       ],[
+               AC_MSG_RESULT(no)
+       ])
+])
+
+dnl #
+dnl # 2.6.30 API change,
+dnl # caching of ACL into the inode was added in this version.
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_POSIX_ACL_CACHING], [
+       AC_MSG_CHECKING([whether inode has i_acl and i_default_acl])
+       ZFS_LINUX_TRY_COMPILE([
+               #include <linux/fs.h>
+       ],[
+               struct inode ino;
+               ino.i_acl = NULL;
+               ino.i_default_acl = NULL;
+       ],[
+               AC_MSG_RESULT(yes)
+               AC_DEFINE(HAVE_POSIX_ACL_CACHING, 1,
+                   [inode contains i_acl and i_default_acl])
+       ],[
+               AC_MSG_RESULT(no)
+       ])
+])
+
+dnl #
+dnl # 3.1 API change,
+dnl # posix_acl_equiv_mode now wants an umode_t* instead of a mode_t*
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_POSIX_ACL_EQUIV_MODE_WANTS_UMODE_T], [
+       AC_MSG_CHECKING([whether posix_acl_equiv_mode() wants umode_t])
+       ZFS_LINUX_TRY_COMPILE([
+               #include <linux/fs.h>
+               #include <linux/posix_acl.h>
+       ],[
+               umode_t tmp;
+               posix_acl_equiv_mode(NULL,&tmp);
+       ],[
+               AC_MSG_RESULT(yes)
+               AC_DEFINE(HAVE_POSIX_ACL_EQUIV_MODE_UMODE_T, 1,
+                   [ posix_acl_equiv_mode wants umode_t*])
+       ],[
+               AC_MSG_RESULT(no)
+       ])
+])
+
+dnl #
+dnl # 2.6.27 API change,
+dnl # Check if inode_operations contains the function permission
+dnl # and expects the nameidata structure to have been removed.
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_INODE_OPERATIONS_PERMISSION], [
+       AC_MSG_CHECKING([whether iops->permission() exists])
+       ZFS_LINUX_TRY_COMPILE([
+               #include <linux/fs.h>
+
+               int permission_fn(struct inode *inode, int mask) { return 0; }
+
+               static const struct inode_operations
+                   iops __attribute__ ((unused)) = {
+                       .permission = permission_fn,
+               };
+       ],[
+       ],[
+               AC_MSG_RESULT(yes)
+               AC_DEFINE(HAVE_PERMISSION, 1, [iops->permission() exists])
+       ],[
+               AC_MSG_RESULT(no)
+       ])
+])
+
+dnl #
+dnl # 2.6.26 API change,
+dnl # Check if inode_operations contains the function permission
+dnl # and expects the nameidata structure to be passed.
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_INODE_OPERATIONS_PERMISSION_WITH_NAMEIDATA], [
+       AC_MSG_CHECKING([whether iops->permission() wants nameidata])
+       ZFS_LINUX_TRY_COMPILE([
+               #include <linux/fs.h>
+
+               int permission_fn(struct inode *inode, int mask,
+                   struct nameidata *nd) { return 0; }
+
+               static const struct inode_operations
+                   iops __attribute__ ((unused)) = {
+                       .permission = permission_fn,
+               };
+       ],[
+       ],[
+               AC_MSG_RESULT(yes)
+               AC_DEFINE(HAVE_PERMISSION, 1, [iops->permission() exists])
+               AC_DEFINE(HAVE_PERMISSION_WITH_NAMEIDATA, 1,
+                   [iops->permission() with nameidata exists])
+       ],[
+               AC_MSG_RESULT(no)
+       ])
+])
+
+dnl #
+dnl # 2.6.32 API change,
+dnl # Check if inode_operations contains the function check_acl
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_INODE_OPERATIONS_CHECK_ACL], [
+       AC_MSG_CHECKING([whether iops->check_acl() exists])
+       ZFS_LINUX_TRY_COMPILE([
+               #include <linux/fs.h>
+
+               int check_acl_fn(struct inode *inode, int mask) { return 0; }
+
+               static const struct inode_operations
+                   iops __attribute__ ((unused)) = {
+                       .check_acl = check_acl_fn,
+               };
+       ],[
+       ],[
+               AC_MSG_RESULT(yes)
+               AC_DEFINE(HAVE_CHECK_ACL, 1, [iops->check_acl() exists])
+       ],[
+               AC_MSG_RESULT(no)
+       ])
+])
+
+dnl #
+dnl # 2.6.38 API change,
+dnl # The function check_acl gained a new parameter: flags
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_INODE_OPERATIONS_CHECK_ACL_WITH_FLAGS], [
+       AC_MSG_CHECKING([whether iops->check_acl() wants flags])
+       ZFS_LINUX_TRY_COMPILE([
+               #include <linux/fs.h>
+
+               int check_acl_fn(struct inode *inode, int mask,
+                   unsigned int flags) { return 0; }
+
+               static const struct inode_operations
+                   iops __attribute__ ((unused)) = {
+                       .check_acl = check_acl_fn,
+               };
+       ],[
+       ],[
+               AC_MSG_RESULT(yes)
+               AC_DEFINE(HAVE_CHECK_ACL, 1, [iops->check_acl() exists])
+               AC_DEFINE(HAVE_CHECK_ACL_WITH_FLAGS, 1,
+                   [iops->check_acl() wants flags])
+       ],[
+               AC_MSG_RESULT(no)
+       ])
+])
+
+dnl #
+dnl # 3.1 API change,
+dnl # Check if inode_operations contains the function get_acl
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_INODE_OPERATIONS_GET_ACL], [
+       AC_MSG_CHECKING([whether iops->get_acl() exists])
+       ZFS_LINUX_TRY_COMPILE([
+               #include <linux/fs.h>
+
+               struct posix_acl *get_acl_fn(struct inode *inode, int type)
+                   { return NULL; }
+
+               static const struct inode_operations
+                   iops __attribute__ ((unused)) = {
+                       .get_acl = get_acl_fn,
+               };
+       ],[
+       ],[
+               AC_MSG_RESULT(yes)
+               AC_DEFINE(HAVE_GET_ACL, 1, [iops->get_acl() exists])
+       ],[
+               AC_MSG_RESULT(no)
+       ])
+])
+
+dnl #
+dnl # 2.6.30 API change,
+dnl # current_umask exists only since this version.
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_CURRENT_UMASK], [
+       AC_MSG_CHECKING([whether current_umask exists])
+       ZFS_LINUX_TRY_COMPILE([
+               #include <linux/fs.h>
+       ],[
+               current_umask();
+       ],[
+               AC_MSG_RESULT(yes)
+               AC_DEFINE(HAVE_CURRENT_UMASK, 1, [current_umask() exists])
+       ],[
+               AC_MSG_RESULT(no)
+       ])
+])
index 943f9033c34a4225033ae0d94a14bcb4f356881a..2ba2fcbcd2e2ab21e4911567856dccf45863b4d2 100644 (file)
@@ -84,3 +84,72 @@ AC_DEFUN([ZFS_AC_KERNEL_XATTR_HANDLER_SET], [
                AC_MSG_RESULT(no)
        ])
 ])
+
+dnl #
+dnl # 2.6.33 API change,
+dnl # The xattr_hander->list() callback was changed to take a dentry
+dnl # instead of an inode, and a handler_flags argument was added.
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_XATTR_HANDLER_LIST], [
+       AC_MSG_CHECKING([whether xattr_handler->list() wants dentry])
+       ZFS_LINUX_TRY_COMPILE([
+               #include <linux/xattr.h>
+
+               size_t list(struct dentry *dentry, char *list, size_t list_size,
+                   const char *name, size_t name_len, int handler_flags)
+                   { return 0; }
+               static const struct xattr_handler
+                   xops __attribute__ ((unused)) = {
+                       .list = list,
+               };
+       ],[
+       ],[
+               AC_MSG_RESULT(yes)
+               AC_DEFINE(HAVE_DENTRY_XATTR_LIST, 1,
+                   [xattr_handler->list() wants dentry])
+       ],[
+               AC_MSG_RESULT(no)
+       ])
+])
+
+dnl #
+dnl # 3.7 API change,
+dnl # The posix_acl_{from,to}_xattr functions gained a new
+dnl # parameter: user_ns
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_POSIX_ACL_FROM_XATTR_USERNS], [
+       AC_MSG_CHECKING([whether posix_acl_from_xattr() needs user_ns])
+       ZFS_LINUX_TRY_COMPILE([
+               #include <linux/cred.h>
+               #include <linux/fs.h>
+               #include <linux/posix_acl_xattr.h>
+       ],[
+               posix_acl_from_xattr(&init_user_ns, NULL, 0);
+       ],[
+               AC_MSG_RESULT(yes)
+               AC_DEFINE(HAVE_POSIX_ACL_FROM_XATTR_USERNS, 1,
+                   [posix_acl_from_xattr() needs user_ns])
+       ],[
+               AC_MSG_RESULT(no)
+       ])
+])
+
+dnl #
+dnl # 2.6.39 API change,
+dnl # The is_owner_or_cap() macro was replaced by inode_owner_or_capable(),
+dnl # this is used for permission checks in the xattr call paths.
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_INODE_OWNER_OR_CAPABLE], [
+       AC_MSG_CHECKING([whether inode_owner_or_capable() exists])
+       ZFS_LINUX_TRY_COMPILE([
+               #include <linux/fs.h>
+       ],[
+               inode_owner_or_capable(NULL);
+       ],[
+               AC_MSG_RESULT(yes)
+               AC_DEFINE(HAVE_INODE_OWNER_OR_CAPABLE, 1,
+                   [inode_owner_or_capable() exists])
+       ],[
+               AC_MSG_RESULT(no)
+       ])
+])
index 74ce22ce634d424ad7b813dec86ec3d53904e0e7..cbf0ca3d69d638e36c565fca69bead35cda2da1e 100644 (file)
@@ -45,6 +45,19 @@ AC_DEFUN([ZFS_AC_CONFIG_KERNEL], [
        ZFS_AC_KERNEL_CONST_XATTR_HANDLER
        ZFS_AC_KERNEL_XATTR_HANDLER_GET
        ZFS_AC_KERNEL_XATTR_HANDLER_SET
+       ZFS_AC_KERNEL_XATTR_HANDLER_LIST
+       ZFS_AC_KERNEL_INODE_OWNER_OR_CAPABLE
+       ZFS_AC_KERNEL_POSIX_ACL_FROM_XATTR_USERNS
+       ZFS_AC_KERNEL_POSIX_ACL_RELEASE
+       ZFS_AC_KERNEL_POSIX_ACL_CHMOD
+       ZFS_AC_KERNEL_POSIX_ACL_CACHING
+       ZFS_AC_KERNEL_POSIX_ACL_EQUIV_MODE_WANTS_UMODE_T
+       ZFS_AC_KERNEL_INODE_OPERATIONS_PERMISSION
+       ZFS_AC_KERNEL_INODE_OPERATIONS_PERMISSION_WITH_NAMEIDATA
+       ZFS_AC_KERNEL_INODE_OPERATIONS_CHECK_ACL
+       ZFS_AC_KERNEL_INODE_OPERATIONS_CHECK_ACL_WITH_FLAGS
+       ZFS_AC_KERNEL_INODE_OPERATIONS_GET_ACL
+       ZFS_AC_KERNEL_CURRENT_UMASK
        ZFS_AC_KERNEL_SHOW_OPTIONS
        ZFS_AC_KERNEL_FSYNC
        ZFS_AC_KERNEL_EVICT_INODE
index 17fa3ff7bd38f9f5fecdd4a98d7a4b4e73fa869d..e5371dacd1fd4219636e21cc5b78f90f0d1afc35 100644 (file)
@@ -174,4 +174,146 @@ lseek_execute(struct file *filp, struct inode *inode,
 }
 #endif /* SEEK_HOLE && SEEK_DATA && !HAVE_LSEEK_EXECUTE */
 
+/*
+ * These functions safely approximates the behavior of posix_acl_release()
+ * which cannot be used because it calls the GPL-only symbol kfree_rcu().
+ * The in-kernel version, which can access the RCU, frees the ACLs after
+ * the grace period expires.  Because we're unsure how long that grace
+ * period may be this implementation conservatively delays for 60 seconds.
+ * This is several orders of magnitude larger than expected grace period.
+ * At 60 seconds the kernel will also begin issuing RCU stall warnings.
+ */
+#include <linux/posix_acl.h>
+#ifndef HAVE_POSIX_ACL_CACHING
+#define ACL_NOT_CACHED ((void *)(-1))
+#endif /* HAVE_POSIX_ACL_CACHING */
+
+#if defined(HAVE_POSIX_ACL_RELEASE) && !defined(HAVE_POSIX_ACL_RELEASE_GPL_ONLY)
+
+#define        zpl_posix_acl_release(arg)              posix_acl_release(arg)
+#define        zpl_set_cached_acl(ip, ty, n)           set_cached_acl(ip, ty, n)
+#define        zpl_forget_cached_acl(ip, ty)           forget_cached_acl(ip, ty)
+
+#else
+
+static inline void
+zpl_posix_acl_free(void *arg) {
+       kfree(arg);
+}
+
+static inline void
+zpl_posix_acl_release(struct posix_acl *acl)
+{
+       if ((acl == NULL) || (acl == ACL_NOT_CACHED))
+               return;
+
+       if (atomic_dec_and_test(&acl->a_refcount)) {
+               taskq_dispatch_delay(system_taskq, zpl_posix_acl_free, acl,
+                   TQ_SLEEP, ddi_get_lbolt() + 60*HZ);
+       }
+}
+
+static inline void
+zpl_set_cached_acl(struct inode *ip, int type, struct posix_acl *newer) {
+#ifdef HAVE_POSIX_ACL_CACHING
+       struct posix_acl *older = NULL;
+
+       spin_lock(&ip->i_lock);
+
+       if ((newer != ACL_NOT_CACHED) && (newer != NULL))
+               posix_acl_dup(newer);
+
+       switch(type) {
+       case ACL_TYPE_ACCESS:
+               older = ip->i_acl;
+               rcu_assign_pointer(ip->i_acl,newer);
+               break;
+       case ACL_TYPE_DEFAULT:
+               older = ip->i_default_acl;
+               rcu_assign_pointer(ip->i_default_acl,newer);
+               break;
+       }
+
+       spin_unlock(&ip->i_lock);
+
+       zpl_posix_acl_release(older);
+#endif /* HAVE_POSIX_ACL_CACHING */
+}
+
+static inline void
+zpl_forget_cached_acl(struct inode *ip, int type) {
+       zpl_set_cached_acl(ip, type, (struct posix_acl *)ACL_NOT_CACHED);
+}
+#endif /* HAVE_POSIX_ACL_RELEASE */
+
+/*
+ * 2.6.38 API change,
+ * The is_owner_or_cap() function was renamed to inode_owner_or_capable().
+ */
+#ifdef HAVE_INODE_OWNER_OR_CAPABLE
+#define        zpl_inode_owner_or_capable(ip)          inode_owner_or_capable(ip)
+#else
+#define        zpl_inode_owner_or_capable(ip)          is_owner_or_cap(ip)
+#endif /* HAVE_INODE_OWNER_OR_CAPABLE */
+
+#ifndef HAVE_POSIX_ACL_CHMOD
+static inline int
+posix_acl_chmod(struct posix_acl **acl, int flags, umode_t umode) {
+       struct posix_acl *oldacl = *acl;
+       mode_t mode = umode;
+       int error;
+
+       *acl = posix_acl_clone(*acl, flags);
+       zpl_posix_acl_release(oldacl);
+
+       if (!(*acl))
+               return (-ENOMEM);
+
+       error = posix_acl_chmod_masq(*acl, mode);
+       if (error) {
+               zpl_posix_acl_release(*acl);
+               *acl = NULL;
+       }
+
+        return (error);
+}
+
+static inline int
+posix_acl_create(struct posix_acl** acl, int flags, umode_t* umodep) {
+       struct posix_acl *oldacl = *acl;
+       mode_t mode = *umodep;
+       int error;
+
+       *acl = posix_acl_clone(*acl, flags);
+       zpl_posix_acl_release(oldacl);
+
+       if (!(*acl))
+               return (-ENOMEM);
+
+       error = posix_acl_create_masq(*acl, &mode);
+       *umodep = mode;
+
+       if (error < 0) {
+               zpl_posix_acl_release(*acl);
+               *acl = NULL;
+       }
+
+       return (error);
+}
+#endif /* HAVE_POSIX_ACL_CHMOD */
+
+#ifndef HAVE_CURRENT_UMASK
+static inline int
+current_umask(void)
+{
+       return (current->fs->umask);
+}
+#endif /* HAVE_CURRENT_UMASK */
+
+#ifdef HAVE_POSIX_ACL_EQUIV_MODE_UMODE_T
+typedef umode_t zpl_equivmode_t;
+#else
+typedef mode_t zpl_equivmode_t;
+#endif /* HAVE_POSIX_ACL_EQUIV_MODE_UMODE_T */
+
 #endif /* _ZFS_VFS_H */
index 84d8fdeb89366d4002cf8083e62699581bd7e8fe..f06ba1fa05d63609fd59cdb45e2cfff93164ddc2 100644 (file)
@@ -26,6 +26,8 @@
 #ifndef _ZFS_XATTR_H
 #define _ZFS_XATTR_H
 
+#include <linux/posix_acl_xattr.h>
+
 /*
  * 2.6.35 API change,
  * The const keyword was added to the 'struct xattr_handler' in the
@@ -92,4 +94,37 @@ fn(struct inode *ip, const char *name, const void *buffer,           \
        security_inode_init_security(ip, dip, nm, val, len)
 #endif /* HAVE_6ARGS_SECURITY_INODE_INIT_SECURITY */
 
+/*
+ * Linux 3.7 API change. posix_acl_{from,to}_xattr gained the user_ns
+ * parameter.  For the HAVE_POSIX_ACL_FROM_XATTR_USERNS version the
+ * userns _may_ not be correct because it's used outside the RCU.
+ */
+#ifdef HAVE_POSIX_ACL_FROM_XATTR_USERNS
+static inline struct posix_acl *
+zpl_acl_from_xattr(const void *value, int size)
+{
+       return posix_acl_from_xattr(CRED()->user_ns, value, size);
+}
+
+static inline int
+zpl_acl_to_xattr(struct posix_acl *acl, void *value, int size)
+{
+       return posix_acl_to_xattr(CRED()->user_ns,acl, value, size);
+}
+
+#else
+
+static inline struct posix_acl *
+zpl_acl_from_xattr(const void *value,int size)
+{
+       return posix_acl_from_xattr(value, size);
+}
+
+static inline int
+zpl_acl_to_xattr(struct posix_acl *acl, void *value, int size)
+{
+       return posix_acl_to_xattr(acl, value, size);
+}
+#endif /* HAVE_POSIX_ACL_FROM_XATTR_USERNS */
+
 #endif /* _ZFS_XATTR_H */
index 164bf3591cb6d508b91254174d50d4b97e19b592..92a843b8c9bc51bb045333f509da0d482925096b 100644 (file)
@@ -139,6 +139,7 @@ typedef enum {
        ZFS_PROP_WRITTEN,
        ZFS_PROP_CLONES,
        ZFS_PROP_SNAPDEV,
+       ZFS_PROP_ACLTYPE,
        ZFS_NUM_PROPS
 } zfs_prop_t;
 
index 8838322a9a51eb04ba562b1c9d0625432a41408b..0ee6cc1cd7dbdc01d95e280256d0884c649e7395 100644 (file)
@@ -61,6 +61,11 @@ extern "C" {
  */
 #define        ZFS_SNAPDEV_HIDDEN              0
 #define        ZFS_SNAPDEV_VISIBLE             1
+/*
+ * Property values for acltype
+ */
+#define        ZFS_ACLTYPE_OFF                 0
+#define        ZFS_ACLTYPE_POSIXACL            1
 
 /*
  * Field manipulation macros for the drr_versioninfo field of the
index f685c1296401493c3b2765f5727a5ff97e9a957e..c9e9ba7f913a5c29b27634a76029442f6f88516a 100644 (file)
@@ -60,6 +60,7 @@ typedef struct zfs_sb {
        struct zfs_fuid_info    *z_fuid_replay; /* fuid info for replay */
        zilog_t         *z_log;         /* intent log pointer */
        uint_t          z_acl_inherit;  /* acl inheritance behavior */
+       uint_t          z_acl_type;     /* type of ACL usable on this FS */
        zfs_case_t      z_case;         /* case-sense */
        boolean_t       z_utf8;         /* utf8-only */
        int             z_norm;         /* normalization flags */
index 89cf8240cd299480f1d136b7ad7ed40267550772..1e338b1cd926b4df33acaa306f0c886cbd9e917c 100644 (file)
@@ -71,6 +71,22 @@ extern struct file_system_type zpl_fs_type;
 extern ssize_t zpl_xattr_list(struct dentry *dentry, char *buf, size_t size);
 extern int zpl_xattr_security_init(struct inode *ip, struct inode *dip,
     const struct qstr *qstr);
+extern int zpl_set_acl(struct inode *ip, int type, struct posix_acl *acl);
+extern struct posix_acl *zpl_get_acl(struct inode *ip, int type);
+#if !defined(HAVE_GET_ACL)
+#if defined(HAVE_CHECK_ACL_WITH_FLAGS)
+extern int zpl_check_acl(struct inode *inode, int mask,unsigned int flags);
+#elif defined(HAVE_CHECK_ACL)
+extern int zpl_check_acl(struct inode *inode, int mask);
+#elif defined(HAVE_PERMISSION_WITH_NAMEIDATA)
+extern int zpl_permission(struct inode *ip, int mask, struct nameidata *nd);
+#elif defined(HAVE_PERMISSION)
+extern int zpl_permission(struct inode *ip, int mask);
+#endif /*  HAVE_CHECK_ACL | HAVE_PERMISSION */
+#endif /* HAVE_GET_ACL */
+
+extern int zpl_init_acl(struct inode *ip, struct inode *dir);
+extern int zpl_chmod_acl(struct inode *ip);
 
 extern xattr_handler_t *zpl_xattr_handlers[];
 
index fd612afd3273c2629e508d70035f48a9b426dad4..65d98ce762224cc669de57d01315a9e293e34606 100644 (file)
@@ -676,17 +676,31 @@ The following native properties can be used to change the behavior of a \fBZFS\f
 Controls how \fBACL\fR entries are inherited when files and directories are created. A file system with an \fBaclinherit\fR property of \fBdiscard\fR does not inherit any \fBACL\fR entries. A file system with an \fBaclinherit\fR property value of \fBnoallow\fR only inherits inheritable \fBACL\fR entries that specify "deny" permissions. The property value \fBrestricted\fR (the default) removes the \fBwrite_acl\fR and \fBwrite_owner\fR permissions when the \fBACL\fR entry is inherited. A file system with an \fBaclinherit\fR property value of \fBpassthrough\fR inherits all inheritable \fBACL\fR entries without any modifications made to the \fBACL\fR entries when they are inherited. A file system with an \fBaclinherit\fR property value of \fBpassthrough-x\fR has the same meaning as \fBpassthrough\fR, except that the \fBowner@\fR, \fBgroup@\fR, and \fBeveryone@\fR \fBACE\fRs inherit the execute permission only if the file creation mode also requests the execute bit.
 .sp
 When the property value is set to \fBpassthrough\fR, files are created with a mode determined by the inheritable \fBACE\fRs. If no inheritable \fBACE\fRs exist that affect the mode, then the mode is set in accordance to the requested mode from the application.
+.sp
+The \fBaclinherit\fR property does not apply to Posix ACLs.
 .RE
 
 .sp
 .ne 2
 .mk
 .na
-\fB\fBaclmode\fR=\fBdiscard\fR | \fBgroupmask\fR | \fBpassthrough\fR\fR
+\fB\fBacltype\fR=\fBnoacl\fR | \fBposixacl\fR \fR
 .ad
 .sp .6
 .RS 4n
-Controls how an \fBACL\fR is modified during \fBchmod\fR(2). A file system with an \fBaclmode\fR property of \fBdiscard\fR deletes all \fBACL\fR entries that do not represent the mode of the file. An \fBaclmode\fR property of \fBgroupmask\fR (the default) reduces user or group permissions. The permissions are reduced, such that they are no greater than the group permission bits, unless it is a user entry that has the same \fBUID\fR as the owner of the file or directory. In this case, the \fBACL\fR permissions are reduced so that they are no greater than owner permission bits. A file system with an \fBaclmode\fR property of \fBpassthrough\fR indicates that no changes are made to the \fBACL\fR other than generating the necessary \fBACL\fR entries to represent the new mode of the file or directory.
+Controls whether ACLs are enabled and if so what type of ACL to use.  When
+a file system has the \fBacltype\fR property set to \fBnoacl\fR (the default)
+then ACLs are disabled.  Setting the \fBacltype\fR property to \fBposixacl\fR
+indicates Posix ACLs should be used.  Posix ACLs are specific to Linux and
+are not functional on other platforms.  Posix ACLs are stored as an xattr and
+therefore will not overwrite any existing ZFS/NFSv4 ACLs which may be set.
+Currently only \fBposixacls\fR are supported on Linux.
+.sp
+To obtain the best performance when setting \fBposixacl\fR users are strongly
+encouraged to set the \fBxattr=sa\fR property.  This will result in the
+Posix ACL being stored more efficiently on disk.  But as a consequence of this
+all new xattrs will only be accessable from ZFS implementations which support
+the \fBxattr=sa\fR property.  See the \fBxattr\fR property for more details.
 .RE
 
 .sp
@@ -2696,8 +2710,8 @@ userprop         other          Allows changing any user property
 userquota        other          Allows accessing any userquota@... property
 userused         other          Allows reading any userused@... property
 
+acltype          property
 aclinherit       property       
-aclmode          property       
 atime            property       
 canmount         property       
 casesensitivity  property       
@@ -3068,7 +3082,7 @@ pool/home/bob  setuid                on                     default
 pool/home/bob  readonly              off                    default
 pool/home/bob  zoned                 off                    default
 pool/home/bob  snapdir               hidden                 default
-pool/home/bob  aclmode               groupmask              default
+pool/home/bob  acltype               off                    default
 pool/home/bob  aclinherit            restricted             default
 pool/home/bob  canmount              on                     default
 pool/home/bob  shareiscsi            off                    default
index b27e4f36f391afca364a3c0521fc4cb2a64b1376..722995dc511bbf81ec3f97701dca880be17e8f83 100644 (file)
@@ -112,6 +112,14 @@ zfs_prop_init(void)
                { NULL }
        };
 
+       static zprop_index_t acltype_table[] = {
+               { "off",        ZFS_ACLTYPE_OFF },
+               { "disabled",   ZFS_ACLTYPE_OFF },
+               { "noacl",      ZFS_ACLTYPE_OFF },
+               { "posixacl",   ZFS_ACLTYPE_POSIXACL },
+               { NULL }
+       };
+
        static zprop_index_t acl_inherit_table[] = {
                { "discard",    ZFS_ACL_DISCARD },
                { "noallow",    ZFS_ACL_NOALLOW },
@@ -226,6 +234,9 @@ zfs_prop_init(void)
        zprop_register_index(ZFS_PROP_SNAPDEV, "snapdev", ZFS_SNAPDEV_HIDDEN,
            PROP_INHERIT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
            "hidden | visible", "SNAPDEV", snapdev_table);
+       zprop_register_index(ZFS_PROP_ACLTYPE, "acltype", ZFS_ACLTYPE_OFF,
+           PROP_INHERIT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT,
+           "noacl | posixacl", "ACLTYPE", acltype_table);
        zprop_register_index(ZFS_PROP_ACLINHERIT, "aclinherit",
            ZFS_ACL_RESTRICTED, PROP_INHERIT, ZFS_TYPE_FILESYSTEM,
            "discard | noallow | restricted | passthrough | passthrough-x",
index 8ab5abe81666cc361b2638ff99355f5ac14b06b7..25f236a1421e2ecac1a60e24a6bdb115b5faca7e 100644 (file)
@@ -1155,6 +1155,9 @@ zfs_acl_chown_setattr(znode_t *zp)
        int error;
        zfs_acl_t *aclp;
 
+        if (ZTOZSB(zp)->z_acl_type == ZFS_ACLTYPE_POSIXACL)
+                return 0;
+
        ASSERT(MUTEX_HELD(&zp->z_lock));
        ASSERT(MUTEX_HELD(&zp->z_acl_lock));
 
index eeac0391cb05cbfb08d919a370fb98de90ea4f00..3af174479c469514821f724434b76a4da0223876 100644 (file)
@@ -154,6 +154,25 @@ xattr_changed_cb(void *arg, uint64_t newval)
        }
 }
 
+static void
+acltype_changed_cb(void *arg, uint64_t newval)
+{
+       zfs_sb_t *zsb = arg;
+
+       switch (newval) {
+       case ZFS_ACLTYPE_OFF:
+               zsb->z_acl_type = ZFS_ACLTYPE_OFF;
+               zsb->z_sb->s_flags &= ~MS_POSIXACL;
+               break;
+       case ZFS_ACLTYPE_POSIXACL:
+               zsb->z_acl_type = ZFS_ACLTYPE_POSIXACL;
+               zsb->z_sb->s_flags |= MS_POSIXACL;
+               break;
+       default:
+               break;
+       }
+}
+
 static void
 blksz_changed_cb(void *arg, uint64_t newval)
 {
@@ -266,8 +285,9 @@ zfs_register_callbacks(zfs_sb_t *zsb)
        error = error ? error : dsl_prop_register(ds,
            zfs_prop_to_name(ZFS_PROP_SNAPDIR), snapdir_changed_cb, zsb);
        error = error ? error : dsl_prop_register(ds,
-           zfs_prop_to_name(ZFS_PROP_ACLINHERIT), acl_inherit_changed_cb,
-           zsb);
+           zfs_prop_to_name(ZFS_PROP_ACLTYPE), acltype_changed_cb, zsb);
+       error = error ? error : dsl_prop_register(ds,
+           zfs_prop_to_name(ZFS_PROP_ACLINHERIT), acl_inherit_changed_cb, zsb);
        error = error ? error : dsl_prop_register(ds,
            zfs_prop_to_name(ZFS_PROP_VSCAN), vscan_changed_cb, zsb);
        error = error ? error : dsl_prop_register(ds,
@@ -303,6 +323,8 @@ unregister:
            exec_changed_cb, zsb);
        (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_SNAPDIR),
            snapdir_changed_cb, zsb);
+       (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_ACLTYPE),
+           acltype_changed_cb, zsb);
        (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_ACLINHERIT),
            acl_inherit_changed_cb, zsb);
        (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_VSCAN),
@@ -663,6 +685,10 @@ zfs_sb_create(const char *osname, zfs_sb_t **zsbp)
                goto out;
        zsb->z_case = (uint_t)zval;
 
+       if ((error = zfs_get_zplprop(os, ZFS_PROP_ACLTYPE, &zval)) != 0)
+               goto out;
+       zsb->z_acl_type = (uint_t)zval;
+
        /*
         * Fold case on file systems that are always or sometimes case
         * insensitive.
@@ -904,6 +930,9 @@ zfs_unregister_callbacks(zfs_sb_t *zsb)
                VERIFY(dsl_prop_unregister(ds, "snapdir", snapdir_changed_cb,
                    zsb) == 0);
 
+               VERIFY(dsl_prop_unregister(ds, "acltype", acltype_changed_cb,
+                   zsb) == 0);
+
                VERIFY(dsl_prop_unregister(ds, "aclinherit",
                    acl_inherit_changed_cb, zsb) == 0);
 
@@ -1221,6 +1250,9 @@ zfs_domount(struct super_block *sb, void *data, int silent)
                if ((error = dsl_prop_get_integer(osname,"xattr",&pval,NULL)))
                        goto out;
                xattr_changed_cb(zsb, pval);
+               if ((error = dsl_prop_get_integer(osname,"acltype",&pval,NULL)))
+                       goto out;
+               acltype_changed_cb(zsb, pval);
                zsb->z_issnap = B_TRUE;
                zsb->z_os->os_sync = ZFS_SYNC_DISABLED;
 
@@ -1610,6 +1642,9 @@ zfs_get_zplprop(objset_t *os, zfs_prop_t prop, uint64_t *value)
                case ZFS_PROP_CASE:
                        *value = ZFS_CASE_SENSITIVE;
                        break;
+               case ZFS_PROP_ACLTYPE:
+                       *value = ZFS_ACLTYPE_OFF;
+                       break;
                default:
                        return (error);
                }
@@ -1632,6 +1667,7 @@ zfs_init(void)
 void
 zfs_fini(void)
 {
+       taskq_wait(system_taskq);
        unregister_filesystem(&zpl_fs_type);
        zfs_znode_fini();
        zfsctl_fini();
index 436a25eaf844ca75f203c19bd1973edbf3a4055f..423ca3800d603f59e149ec7e96792098de044521 100644 (file)
@@ -3971,7 +3971,7 @@ zfs_putpage(struct inode *ip, struct page *pp, struct writeback_control *wbc)
 
 /*
  * Update the system attributes when the inode has been dirtied.  For the
- * moment we're conservative and only update the atime, mtime, and ctime.
+ * moment we only update the mode, atime, mtime, and ctime.
  */
 int
 zfs_dirty_inode(struct inode *ip, int flags)
@@ -3979,8 +3979,8 @@ zfs_dirty_inode(struct inode *ip, int flags)
        znode_t         *zp = ITOZ(ip);
        zfs_sb_t        *zsb = ITOZSB(ip);
        dmu_tx_t        *tx;
-       uint64_t        atime[2], mtime[2], ctime[2];
-       sa_bulk_attr_t  bulk[3];
+       uint64_t        mode, atime[2], mtime[2], ctime[2];
+       sa_bulk_attr_t  bulk[4];
        int             error;
        int             cnt = 0;
 
@@ -3999,14 +3999,18 @@ zfs_dirty_inode(struct inode *ip, int flags)
        }
 
        mutex_enter(&zp->z_lock);
+       SA_ADD_BULK_ATTR(bulk, cnt, SA_ZPL_MODE(zsb), NULL, &mode, 8);
        SA_ADD_BULK_ATTR(bulk, cnt, SA_ZPL_ATIME(zsb), NULL, &atime, 16);
        SA_ADD_BULK_ATTR(bulk, cnt, SA_ZPL_MTIME(zsb), NULL, &mtime, 16);
        SA_ADD_BULK_ATTR(bulk, cnt, SA_ZPL_CTIME(zsb), NULL, &ctime, 16);
 
-       /* Preserve the mtime and ctime provided by the inode */
+       /* Preserve the mode, mtime and ctime provided by the inode */
        ZFS_TIME_ENCODE(&ip->i_atime, atime);
        ZFS_TIME_ENCODE(&ip->i_mtime, mtime);
        ZFS_TIME_ENCODE(&ip->i_ctime, ctime);
+       mode = ip->i_mode;
+
+       zp->z_mode = mode;
        zp->z_atime_dirty = 0;
 
        error = sa_bulk_update(zp->z_sa_hdl, bulk, cnt, tx);
index 720d2d9fa7bc4afde7dbf4e2380d29f4b0cfcc48..e15f0451a49367a1c368a734fe2c462f8c15d2a1 100644 (file)
@@ -102,8 +102,8 @@ zpl_create(struct inode *dir, struct dentry *dentry, zpl_umode_t mode,
 
        error = -zfs_create(dir, dname(dentry), vap, 0, mode, &ip, cr, 0, NULL);
        if (error == 0) {
-               error = zpl_xattr_security_init(ip, dir, &dentry->d_name);
-               VERIFY3S(error, ==, 0);
+               VERIFY0(zpl_xattr_security_init(ip, dir, &dentry->d_name));
+               VERIFY0(zpl_init_acl(ip, dir));
                d_instantiate(dentry, ip);
        }
 
@@ -136,8 +136,10 @@ zpl_mknod(struct inode *dir, struct dentry *dentry, zpl_umode_t mode,
        vap->va_rdev = rdev;
 
        error = -zfs_create(dir, dname(dentry), vap, 0, mode, &ip, cr, 0, NULL);
-       if (error == 0)
+       if (error == 0) {
+               VERIFY0(zpl_init_acl(ip, dir));
                d_instantiate(dentry, ip);
+       }
 
        kmem_free(vap, sizeof(vattr_t));
        crfree(cr);
@@ -173,8 +175,10 @@ zpl_mkdir(struct inode *dir, struct dentry *dentry, zpl_umode_t mode)
        zpl_vap_init(vap, dir, mode | S_IFDIR, cr);
 
        error = -zfs_mkdir(dir, dname(dentry), vap, &ip, cr, 0, NULL);
-       if (error == 0)
+       if (error == 0) {
+               VERIFY0(zpl_init_acl(ip, dir));
                d_instantiate(dentry, ip);
+       }
 
        kmem_free(vap, sizeof(vattr_t));
        crfree(cr);
@@ -223,11 +227,12 @@ zpl_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
 static int
 zpl_setattr(struct dentry *dentry, struct iattr *ia)
 {
+       struct inode *ip = dentry->d_inode;
        cred_t *cr = CRED();
        vattr_t *vap;
        int error;
 
-       error = inode_change_ok(dentry->d_inode, ia);
+       error = inode_change_ok(ip, ia);
        if (error)
                return (error);
 
@@ -242,7 +247,9 @@ zpl_setattr(struct dentry *dentry, struct iattr *ia)
        vap->va_mtime = ia->ia_mtime;
        vap->va_ctime = ia->ia_ctime;
 
-       error = -zfs_setattr(dentry->d_inode, vap, 0, cr);
+       error = -zfs_setattr(ip, vap, 0, cr);
+       if (!error && (ia->ia_valid & ATTR_MODE))
+               error = zpl_chmod_acl(ip);
 
        kmem_free(vap, sizeof(vattr_t));
        crfree(cr);
@@ -455,6 +462,13 @@ const struct inode_operations zpl_inode_operations = {
 #ifdef HAVE_INODE_FALLOCATE
        .fallocate      = zpl_fallocate,
 #endif /* HAVE_INODE_FALLOCATE */
+#if defined(HAVE_GET_ACL)
+       .get_acl        = zpl_get_acl,
+#elif defined(HAVE_CHECK_ACL)
+       .check_acl      = zpl_check_acl,
+#elif defined(HAVE_PERMISSION)
+       .permission     = zpl_permission,
+#endif /* HAVE_GET_ACL | HAVE_CHECK_ACL | HAVE_PERMISSION */
 };
 
 const struct inode_operations zpl_dir_inode_operations = {
@@ -473,6 +487,13 @@ const struct inode_operations zpl_dir_inode_operations = {
        .getxattr       = generic_getxattr,
        .removexattr    = generic_removexattr,
        .listxattr      = zpl_xattr_list,
+#if defined(HAVE_GET_ACL)
+       .get_acl        = zpl_get_acl,
+#elif defined(HAVE_CHECK_ACL)
+       .check_acl      = zpl_check_acl,
+#elif defined(HAVE_PERMISSION)
+       .permission     = zpl_permission,
+#endif /* HAVE_GET_ACL | HAVE_CHECK_ACL | HAVE_PERMISSION */
 };
 
 const struct inode_operations zpl_symlink_inode_operations = {
@@ -494,6 +515,13 @@ const struct inode_operations zpl_special_inode_operations = {
        .getxattr       = generic_getxattr,
        .removexattr    = generic_removexattr,
        .listxattr      = zpl_xattr_list,
+#if defined(HAVE_GET_ACL)
+       .get_acl        = zpl_get_acl,
+#elif defined(HAVE_CHECK_ACL)
+       .check_acl      = zpl_check_acl,
+#elif defined(HAVE_PERMISSION)
+       .permission     = zpl_permission,
+#endif /* HAVE_GET_ACL | HAVE_CHECK_ACL | HAVE_PERMISSION */
 };
 
 dentry_operations_t zpl_dentry_operations = {
index eee4a50e03a569932e22c270e7c492e4d8ec158a..72859ac5c1c4c4f4c8300d50dd42ed048ea34507 100644 (file)
@@ -179,28 +179,48 @@ zpl_umount_begin(struct super_block *sb)
 }
 
 /*
- * The Linux VFS automatically handles the following flags:
- * MNT_NOSUID, MNT_NODEV, MNT_NOEXEC, MNT_NOATIME, MNT_READONLY
+ * ZFS specific features must be explicitly handled here, the VFS will
+ * automatically handled the following generic functionality.
+ *
+ *   MNT_NOSUID,
+ *   MNT_NODEV,
+ *   MNT_NOEXEC,
+ *   MNT_NOATIME,
+ *   MNT_NODIRATIME,
+ *   MNT_READONLY,
+ *   MNT_STRICTATIME,
+ *   MS_SYNCHRONOUS,
+ *   MS_DIRSYNC,
+ *   MS_MANDLOCK.
  */
-#ifdef HAVE_SHOW_OPTIONS_WITH_DENTRY
 static int
-zpl_show_options(struct seq_file *seq, struct dentry *root)
+__zpl_show_options(struct seq_file *seq, zfs_sb_t *zsb)
 {
-       zfs_sb_t *zsb = root->d_sb->s_fs_info;
-
        seq_printf(seq, ",%s", zsb->z_flags & ZSB_XATTR ? "xattr" : "noxattr");
 
+       switch (zsb->z_acl_type) {
+       case ZFS_ACLTYPE_POSIXACL:
+               seq_puts(seq, ",posixacl");
+               break;
+       default:
+               seq_puts(seq, ",noacl");
+               break;
+       }
+
        return (0);
 }
+
+#ifdef HAVE_SHOW_OPTIONS_WITH_DENTRY
+static int
+zpl_show_options(struct seq_file *seq, struct dentry *root)
+{
+       return __zpl_show_options(seq, root->d_sb->s_fs_info);
+}
 #else
 static int
 zpl_show_options(struct seq_file *seq, struct vfsmount *vfsp)
 {
-       zfs_sb_t *zsb = vfsp->mnt_sb->s_fs_info;
-
-       seq_printf(seq, ",%s", zsb->z_flags & ZSB_XATTR ? "xattr" : "noxattr");
-
-       return (0);
+       return __zpl_show_options(seq, vfsp->mnt_sb->s_fs_info);
 }
 #endif /* HAVE_SHOW_OPTIONS_WITH_DENTRY */
 
index 0918e7404636f9021a87f85f6bf4d867b9cf2830..276ea3361c66dfff33ae702e334160e95004b6cd 100644 (file)
@@ -722,13 +722,476 @@ xattr_handler_t zpl_xattr_security_handler = {
        .set    = zpl_xattr_security_set,
 };
 
+int
+zpl_set_acl(struct inode *ip, int type, struct posix_acl *acl)
+{
+       struct super_block *sb = ITOZSB(ip)->z_sb;
+       char *name, *value = NULL;
+       int error = 0;
+       size_t size = 0;
+
+       if (S_ISLNK(ip->i_mode))
+               return (-EOPNOTSUPP);
+
+       switch(type) {
+       case ACL_TYPE_ACCESS:
+               name = POSIX_ACL_XATTR_ACCESS;
+               if (acl) {
+                       zpl_equivmode_t mode = ip->i_mode;
+                       error = posix_acl_equiv_mode(acl, &mode);
+                       if (error < 0) {
+                               return (error);
+                       } else {
+                               /*
+                                * The mode bits will have been set by
+                                * ->zfs_setattr()->zfs_acl_chmod_setattr()
+                                * using the ZFS ACL conversion.  If they
+                                * differ from the Posix ACL conversion dirty
+                                * the inode to write the Posix mode bits.
+                                */
+                               if (ip->i_mode != mode) {
+                                       ip->i_mode = mode;
+                                       ip->i_ctime = current_fs_time(sb);
+                                       mark_inode_dirty(ip);
+                               }
+
+                               if (error == 0)
+                                       acl = NULL;
+                       }
+               }
+               break;
+
+       case ACL_TYPE_DEFAULT:
+               name = POSIX_ACL_XATTR_DEFAULT;
+               if (!S_ISDIR(ip->i_mode))
+                       return (acl ? -EACCES : 0);
+               break;
+
+       default:
+               return (-EINVAL);
+       }
+
+       if (acl) {
+               size = posix_acl_xattr_size(acl->a_count);
+               value = kmem_alloc(size, KM_SLEEP);
+
+               error = zpl_acl_to_xattr(acl, value, size);
+               if (error < 0) {
+                       kmem_free(value, size);
+                       return (error);
+               }
+       }
+
+       error = zpl_xattr_set(ip, name, value, size, 0);
+       if (value)
+               kmem_free(value, size);
+
+       if (!error) {
+               if (acl)
+                       zpl_set_cached_acl(ip, type, acl);
+               else
+                       zpl_forget_cached_acl(ip, type);
+       }
+
+       return (error);
+}
+
+struct posix_acl *
+zpl_get_acl(struct inode *ip, int type)
+{
+       struct posix_acl *acl;
+       void *value = NULL;
+       char *name;
+       int size;
+
+#ifdef HAVE_POSIX_ACL_CACHING
+       acl = get_cached_acl(ip, type);
+       if (acl != ACL_NOT_CACHED)
+               return (acl);
+#endif /* HAVE_POSIX_ACL_CACHING */
+
+       switch (type) {
+       case ACL_TYPE_ACCESS:
+               name = POSIX_ACL_XATTR_ACCESS;
+               break;
+       case ACL_TYPE_DEFAULT:
+               name = POSIX_ACL_XATTR_DEFAULT;
+               break;
+       default:
+               return ERR_PTR(-EINVAL);
+       }
+
+       size = zpl_xattr_get(ip, name, NULL, 0);
+       if (size > 0) {
+               value = kmem_alloc(size, KM_PUSHPAGE);
+               size = zpl_xattr_get(ip, name, value, size);
+       }
+
+       if (size > 0) {
+               acl = zpl_acl_from_xattr(value, size);
+       } else if (size == -ENODATA || size == -ENOSYS) {
+               acl = NULL;
+       } else {
+               acl = ERR_PTR(-EIO);
+       }
+
+       if (size > 0)
+               kmem_free(value, size);
+
+       if (!IS_ERR(acl))
+               zpl_set_cached_acl(ip, type, acl);
+
+       return (acl);
+}
+
+#if !defined(HAVE_GET_ACL)
+static int
+__zpl_check_acl(struct inode *ip, int mask)
+{
+       struct posix_acl *acl;
+       int error;
+
+       acl = zpl_get_acl(ip, ACL_TYPE_ACCESS);
+       if (IS_ERR(acl))
+               return (PTR_ERR(acl));
+
+       if (acl) {
+               error = posix_acl_permission(ip, acl, mask);
+               zpl_posix_acl_release(acl);
+               return (error);
+       }
+
+       return (-EAGAIN);
+}
+
+#if defined(HAVE_CHECK_ACL_WITH_FLAGS)
+int
+zpl_check_acl(struct inode *ip, int mask, unsigned int flags)
+{
+       return __zpl_check_acl(ip, mask);
+}
+#elif defined(HAVE_CHECK_ACL)
+int
+zpl_check_acl(struct inode *ip, int mask)
+{
+       return __zpl_check_acl(ip , mask);
+}
+#elif defined(HAVE_PERMISSION_WITH_NAMEIDATA)
+int
+zpl_permission(struct inode *ip, int mask, struct nameidata *nd)
+{
+       return generic_permission(ip, mask, __zpl_check_acl);
+}
+#elif defined(HAVE_PERMISSION)
+int
+zpl_permission(struct inode *ip, int mask)
+{
+       return generic_permission(ip, mask, __zpl_check_acl);
+}
+#endif /* HAVE_CHECK_ACL | HAVE_PERMISSION */
+#endif /* !HAVE_GET_ACL */
+
+int
+zpl_init_acl(struct inode *ip, struct inode *dir)
+{
+       struct posix_acl *acl = NULL;
+       int error = 0;
+
+       if (ITOZSB(ip)->z_acl_type != ZFS_ACLTYPE_POSIXACL)
+               return (0);
+
+       if (!S_ISLNK(ip->i_mode)) {
+               if (ITOZSB(ip)->z_acl_type == ZFS_ACLTYPE_POSIXACL) {
+                       acl = zpl_get_acl(dir, ACL_TYPE_DEFAULT);
+                       if (IS_ERR(acl))
+                               return (PTR_ERR(acl));
+               }
+
+               if (!acl) {
+                       ip->i_mode &= ~current_umask();
+                       ip->i_ctime = current_fs_time(ITOZSB(ip)->z_sb);
+                       mark_inode_dirty(ip);
+                       return (0);
+               }
+       }
+
+       if ((ITOZSB(ip)->z_acl_type == ZFS_ACLTYPE_POSIXACL) && acl) {
+               umode_t mode;
+
+               if (S_ISDIR(ip->i_mode)) {
+                       error = zpl_set_acl(ip, ACL_TYPE_DEFAULT, acl);
+                       if (error)
+                               goto out;
+               }
+
+               mode = ip->i_mode;
+               error = posix_acl_create(&acl,GFP_KERNEL, &mode);
+               if (error >= 0) {
+                       ip->i_mode = mode;
+                       mark_inode_dirty(ip);
+                       if (error > 0)
+                               error = zpl_set_acl(ip, ACL_TYPE_ACCESS, acl);
+               }
+       }
+out:
+       zpl_posix_acl_release(acl);
+
+       return (error);
+}
+
+int
+zpl_chmod_acl(struct inode *ip)
+{
+       struct posix_acl *acl;
+       int error;
+
+       if (ITOZSB(ip)->z_acl_type != ZFS_ACLTYPE_POSIXACL)
+               return (0);
+
+       if (S_ISLNK(ip->i_mode))
+               return (-EOPNOTSUPP);
+
+       acl = zpl_get_acl(ip, ACL_TYPE_ACCESS);
+       if (IS_ERR(acl) || !acl)
+               return (PTR_ERR(acl));
+
+       error = posix_acl_chmod(&acl,GFP_KERNEL, ip->i_mode);
+       if (!error)
+               error = zpl_set_acl(ip,ACL_TYPE_ACCESS, acl);
+
+       zpl_posix_acl_release(acl);
+
+       return (error);
+}
+
+static size_t
+zpl_xattr_acl_list(struct inode *ip, char *list, size_t list_size,
+    const char *name, size_t name_len, int type)
+{
+       char *xattr_name;
+       size_t xattr_size;
+
+       if (ITOZSB(ip)->z_acl_type != ZFS_ACLTYPE_POSIXACL)
+               return (0);
+
+       switch (type) {
+       case ACL_TYPE_ACCESS:
+               xattr_name = POSIX_ACL_XATTR_ACCESS;
+               xattr_size = sizeof(xattr_name);
+               break;
+       case ACL_TYPE_DEFAULT:
+               xattr_name = POSIX_ACL_XATTR_DEFAULT;
+               xattr_size = sizeof(xattr_name);
+               break;
+       default:
+               return (0);
+       }
+
+       if (list && xattr_size <= list_size)
+               memcpy(list, xattr_name, xattr_size);
+
+       return (xattr_size);
+}
+
+#ifdef HAVE_DENTRY_XATTR_LIST
+static size_t
+zpl_xattr_acl_list_access(struct dentry *dentry, char *list,
+    size_t list_size, const char *name, size_t name_len, int type)
+{
+       ASSERT3S(type, ==, ACL_TYPE_ACCESS);
+       return zpl_xattr_acl_list(dentry->d_inode,
+           list, list_size, name, name_len, type);
+}
+
+static size_t
+zpl_xattr_acl_list_default(struct dentry *dentry, char *list,
+    size_t list_size, const char *name, size_t name_len, int type)
+{
+       ASSERT3S(type, ==, ACL_TYPE_DEFAULT);
+       return zpl_xattr_acl_list(dentry->d_inode,
+           list, list_size, name, name_len, type);
+}
+
+#else
+
+static size_t
+zpl_xattr_acl_list_access(struct inode *ip, char *list, size_t list_size,
+    const char *name, size_t name_len)
+{
+       return zpl_xattr_acl_list(ip,
+           list, list_size, name, name_len, ACL_TYPE_ACCESS);
+}
+
+static size_t
+zpl_xattr_acl_list_default(struct inode *ip, char *list, size_t list_size,
+    const char *name, size_t name_len)
+{
+       return zpl_xattr_acl_list(ip,
+           list, list_size, name, name_len, ACL_TYPE_DEFAULT);
+}
+#endif /* HAVE_DENTRY_XATTR_LIST */
+
+static int
+zpl_xattr_acl_get(struct inode *ip, const char *name,
+    void *buffer, size_t size, int type)
+{
+       struct posix_acl *acl;
+       int error;
+
+       if (strcmp(name, "") != 0)
+               return (-EINVAL);
+
+       if (ITOZSB(ip)->z_acl_type != ZFS_ACLTYPE_POSIXACL)
+               return (-EOPNOTSUPP);
+
+       acl = zpl_get_acl(ip, type);
+       if (IS_ERR(acl))
+               return (PTR_ERR(acl));
+       if (acl == NULL)
+               return (-ENODATA);
+
+       error = zpl_acl_to_xattr(acl, buffer, size);
+       zpl_posix_acl_release(acl);
+
+       return (error);
+}
+
+#ifdef HAVE_DENTRY_XATTR_GET
+static int
+zpl_xattr_acl_get_access(struct dentry *dentry, const char *name,
+    void *buffer, size_t size, int type)
+{
+       ASSERT3S(type, ==, ACL_TYPE_ACCESS);
+       return zpl_xattr_acl_get(dentry->d_inode, name, buffer, size, type);
+}
+
+static int
+zpl_xattr_acl_get_default(struct dentry *dentry, const char *name,
+    void *buffer, size_t size, int type)
+{
+       ASSERT3S(type, ==, ACL_TYPE_DEFAULT);
+       return zpl_xattr_acl_get(dentry->d_inode, name, buffer, size, type);
+}
+
+#else
+
+static int
+zpl_xattr_acl_get_access(struct inode *ip, const char *name,
+    void *buffer, size_t size)
+{
+       return zpl_xattr_acl_get(ip, name, buffer, size, ACL_TYPE_ACCESS);
+}
+
+static int
+zpl_xattr_acl_get_default(struct inode *ip, const char *name,
+    void *buffer, size_t size)
+{
+       return zpl_xattr_acl_get(ip, name, buffer, size, ACL_TYPE_DEFAULT);
+}
+#endif /* HAVE_DENTRY_XATTR_GET */
+
+static int
+zpl_xattr_acl_set(struct inode *ip, const char *name,
+    const void *value, size_t size, int flags, int type)
+{
+       struct posix_acl *acl;
+       int error = 0;
+
+       if (strcmp(name, "") != 0)
+               return (-EINVAL);
+
+       if (ITOZSB(ip)->z_acl_type != ZFS_ACLTYPE_POSIXACL)
+               return (-EOPNOTSUPP);
+
+       if (!zpl_inode_owner_or_capable(ip))
+               return (-EPERM);
+
+       if (value) {
+               acl = zpl_acl_from_xattr(value, size);
+               if (IS_ERR(acl))
+                       return (PTR_ERR(acl));
+               else if (acl) {
+                       error = posix_acl_valid(acl);
+                       if (error) {
+                               zpl_posix_acl_release(acl);
+                               return (error);
+                       }
+               }
+       } else {
+               acl = NULL;
+       }
+
+       error = zpl_set_acl(ip, type, acl);
+       zpl_posix_acl_release(acl);
+
+       return (error);
+}
+
+#ifdef HAVE_DENTRY_XATTR_SET
+static int
+zpl_xattr_acl_set_access(struct dentry *dentry, const char *name,
+    const void *value, size_t size, int flags, int type)
+{
+        ASSERT3S(type, ==, ACL_TYPE_ACCESS);
+        return zpl_xattr_acl_set(dentry->d_inode,
+           name, value, size, flags, type);
+}
+
+static int
+zpl_xattr_acl_set_default(struct dentry *dentry, const char *name,
+    const void *value, size_t size,int flags, int type)
+{
+        ASSERT3S(type, ==, ACL_TYPE_DEFAULT);
+        return zpl_xattr_acl_set(dentry->d_inode,
+           name, value, size, flags, type);
+}
+
+#else
+
+static int
+zpl_xattr_acl_set_access(struct inode *ip, const char *name,
+    const void *value, size_t size, int flags)
+{
+        return zpl_xattr_acl_set(ip,
+           name, value, size, flags, ACL_TYPE_ACCESS);
+}
+
+static int
+zpl_xattr_acl_set_default(struct inode *ip, const char *name,
+    const void *value, size_t size, int flags)
+{
+        return zpl_xattr_acl_set(ip,
+           name, value, size, flags, ACL_TYPE_DEFAULT);
+}
+#endif /* HAVE_DENTRY_XATTR_SET */
+
+struct xattr_handler zpl_xattr_acl_access_handler =
+{
+       .prefix = POSIX_ACL_XATTR_ACCESS,
+       .list   = zpl_xattr_acl_list_access,
+       .get    = zpl_xattr_acl_get_access,
+       .set    = zpl_xattr_acl_set_access,
+#ifdef HAVE_DENTRY_XATTR_LIST
+       .flags  = ACL_TYPE_ACCESS,
+#endif /* HAVE_DENTRY_XATTR_LIST */
+};
+
+struct xattr_handler zpl_xattr_acl_default_handler =
+{
+       .prefix = POSIX_ACL_XATTR_DEFAULT,
+       .list   = zpl_xattr_acl_list_default,
+       .get    = zpl_xattr_acl_get_default,
+       .set    = zpl_xattr_acl_set_default,
+#ifdef HAVE_DENTRY_XATTR_LIST
+       .flags  = ACL_TYPE_DEFAULT,
+#endif /* HAVE_DENTRY_XATTR_LIST */
+};
+
 xattr_handler_t *zpl_xattr_handlers[] = {
        &zpl_xattr_security_handler,
        &zpl_xattr_trusted_handler,
        &zpl_xattr_user_handler,
-#ifdef HAVE_POSIX_ACLS
        &zpl_xattr_acl_access_handler,
        &zpl_xattr_acl_default_handler,
-#endif /* HAVE_POSIX_ACLS */
        NULL
 };