aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMassimo Maggi <[email protected]>2013-10-28 09:22:15 -0700
committerBrian Behlendorf <[email protected]>2013-10-29 14:54:26 -0700
commit023699cd62eb033ebed5e5fae4e13acaba4c5461 (patch)
treecc36188907422afa2ae4f74c217760d5379805b4
parent7c2448a33ee71be1671c158a167559d1320ff839 (diff)
Posix ACL Support
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 <[email protected]> Signed-off-by: Richard Yao <[email protected]> Signed-off-by: Brian Behlendorf <[email protected]> Closes #170
-rw-r--r--config/kernel-acl.m4249
-rw-r--r--config/kernel-xattr-handler.m469
-rw-r--r--config/kernel.m413
-rw-r--r--include/linux/vfs_compat.h142
-rw-r--r--include/linux/xattr_compat.h35
-rw-r--r--include/sys/fs/zfs.h1
-rw-r--r--include/sys/zfs_ioctl.h5
-rw-r--r--include/sys/zfs_vfsops.h1
-rw-r--r--include/sys/zpl.h16
-rw-r--r--man/man8/zfs.822
-rw-r--r--module/zcommon/zfs_prop.c11
-rw-r--r--module/zfs/zfs_acl.c3
-rw-r--r--module/zfs/zfs_vfsops.c40
-rw-r--r--module/zfs/zfs_vnops.c12
-rw-r--r--module/zfs/zpl_inode.c40
-rw-r--r--module/zfs/zpl_super.c42
-rw-r--r--module/zfs/zpl_xattr.c467
17 files changed, 1139 insertions, 29 deletions
diff --git a/config/kernel-acl.m4 b/config/kernel-acl.m4
new file mode 100644
index 000000000..e9a254780
--- /dev/null
+++ b/config/kernel-acl.m4
@@ -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)
+ ])
+])
diff --git a/config/kernel-xattr-handler.m4 b/config/kernel-xattr-handler.m4
index 943f9033c..2ba2fcbcd 100644
--- a/config/kernel-xattr-handler.m4
+++ b/config/kernel-xattr-handler.m4
@@ -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)
+ ])
+])
diff --git a/config/kernel.m4 b/config/kernel.m4
index 74ce22ce6..cbf0ca3d6 100644
--- a/config/kernel.m4
+++ b/config/kernel.m4
@@ -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
diff --git a/include/linux/vfs_compat.h b/include/linux/vfs_compat.h
index 17fa3ff7b..e5371dacd 100644
--- a/include/linux/vfs_compat.h
+++ b/include/linux/vfs_compat.h
@@ -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 */
diff --git a/include/linux/xattr_compat.h b/include/linux/xattr_compat.h
index 84d8fdeb8..f06ba1fa0 100644
--- a/include/linux/xattr_compat.h
+++ b/include/linux/xattr_compat.h
@@ -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 */
diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h
index 164bf3591..92a843b8c 100644
--- a/include/sys/fs/zfs.h
+++ b/include/sys/fs/zfs.h
@@ -139,6 +139,7 @@ typedef enum {
ZFS_PROP_WRITTEN,
ZFS_PROP_CLONES,
ZFS_PROP_SNAPDEV,
+ ZFS_PROP_ACLTYPE,
ZFS_NUM_PROPS
} zfs_prop_t;
diff --git a/include/sys/zfs_ioctl.h b/include/sys/zfs_ioctl.h
index 8838322a9..0ee6cc1cd 100644
--- a/include/sys/zfs_ioctl.h
+++ b/include/sys/zfs_ioctl.h
@@ -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
diff --git a/include/sys/zfs_vfsops.h b/include/sys/zfs_vfsops.h
index f685c1296..c9e9ba7f9 100644
--- a/include/sys/zfs_vfsops.h
+++ b/include/sys/zfs_vfsops.h
@@ -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 */
diff --git a/include/sys/zpl.h b/include/sys/zpl.h
index 89cf8240c..1e338b1cd 100644
--- a/include/sys/zpl.h
+++ b/include/sys/zpl.h
@@ -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[];
diff --git a/man/man8/zfs.8 b/man/man8/zfs.8
index fd612afd3..65d98ce76 100644
--- a/man/man8/zfs.8
+++ b/man/man8/zfs.8
@@ -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
diff --git a/module/zcommon/zfs_prop.c b/module/zcommon/zfs_prop.c
index b27e4f36f..722995dc5 100644
--- a/module/zcommon/zfs_prop.c
+++ b/module/zcommon/zfs_prop.c
@@ -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",
diff --git a/module/zfs/zfs_acl.c b/module/zfs/zfs_acl.c
index 8ab5abe81..25f236a14 100644
--- a/module/zfs/zfs_acl.c
+++ b/module/zfs/zfs_acl.c
@@ -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));
diff --git a/module/zfs/zfs_vfsops.c b/module/zfs/zfs_vfsops.c
index eeac0391c..3af174479 100644
--- a/module/zfs/zfs_vfsops.c
+++ b/module/zfs/zfs_vfsops.c
@@ -155,6 +155,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)
{
zfs_sb_t *zsb = arg;
@@ -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();
diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c
index 436a25eaf..423ca3800 100644
--- a/module/zfs/zfs_vnops.c
+++ b/module/zfs/zfs_vnops.c
@@ -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);
diff --git a/module/zfs/zpl_inode.c b/module/zfs/zpl_inode.c
index 720d2d9fa..e15f0451a 100644
--- a/module/zfs/zpl_inode.c
+++ b/module/zfs/zpl_inode.c
@@ -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 = {
diff --git a/module/zfs/zpl_super.c b/module/zfs/zpl_super.c
index eee4a50e0..72859ac5c 100644
--- a/module/zfs/zpl_super.c
+++ b/module/zfs/zpl_super.c
@@ -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 */
diff --git a/module/zfs/zpl_xattr.c b/module/zfs/zpl_xattr.c
index 0918e7404..276ea3361 100644
--- a/module/zfs/zpl_xattr.c
+++ b/module/zfs/zpl_xattr.c
@@ -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
};