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 <me@massimo-maggi.eu>
Signed-off-by: Richard Yao <ryao@gentoo.org>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #170
This commit is contained in:
Massimo Maggi 2013-10-28 09:22:15 -07:00 committed by Brian Behlendorf
parent 7c2448a33e
commit 023699cd62
17 changed files with 1140 additions and 30 deletions

249
config/kernel-acl.m4 Normal file
View File

@ -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)
])
])

View File

@ -84,3 +84,72 @@ AC_DEFUN([ZFS_AC_KERNEL_XATTR_HANDLER_SET], [
AC_MSG_RESULT(no) 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)
])
])

View File

@ -45,6 +45,19 @@ AC_DEFUN([ZFS_AC_CONFIG_KERNEL], [
ZFS_AC_KERNEL_CONST_XATTR_HANDLER ZFS_AC_KERNEL_CONST_XATTR_HANDLER
ZFS_AC_KERNEL_XATTR_HANDLER_GET ZFS_AC_KERNEL_XATTR_HANDLER_GET
ZFS_AC_KERNEL_XATTR_HANDLER_SET 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_SHOW_OPTIONS
ZFS_AC_KERNEL_FSYNC ZFS_AC_KERNEL_FSYNC
ZFS_AC_KERNEL_EVICT_INODE ZFS_AC_KERNEL_EVICT_INODE

View File

@ -174,4 +174,146 @@ lseek_execute(struct file *filp, struct inode *inode,
} }
#endif /* SEEK_HOLE && SEEK_DATA && !HAVE_LSEEK_EXECUTE */ #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 */ #endif /* _ZFS_VFS_H */

View File

@ -26,6 +26,8 @@
#ifndef _ZFS_XATTR_H #ifndef _ZFS_XATTR_H
#define _ZFS_XATTR_H #define _ZFS_XATTR_H
#include <linux/posix_acl_xattr.h>
/* /*
* 2.6.35 API change, * 2.6.35 API change,
* The const keyword was added to the 'struct xattr_handler' in the * 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) security_inode_init_security(ip, dip, nm, val, len)
#endif /* HAVE_6ARGS_SECURITY_INODE_INIT_SECURITY */ #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 */ #endif /* _ZFS_XATTR_H */

View File

@ -139,6 +139,7 @@ typedef enum {
ZFS_PROP_WRITTEN, ZFS_PROP_WRITTEN,
ZFS_PROP_CLONES, ZFS_PROP_CLONES,
ZFS_PROP_SNAPDEV, ZFS_PROP_SNAPDEV,
ZFS_PROP_ACLTYPE,
ZFS_NUM_PROPS ZFS_NUM_PROPS
} zfs_prop_t; } zfs_prop_t;

View File

@ -61,6 +61,11 @@ extern "C" {
*/ */
#define ZFS_SNAPDEV_HIDDEN 0 #define ZFS_SNAPDEV_HIDDEN 0
#define ZFS_SNAPDEV_VISIBLE 1 #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 * Field manipulation macros for the drr_versioninfo field of the

View File

@ -60,6 +60,7 @@ typedef struct zfs_sb {
struct zfs_fuid_info *z_fuid_replay; /* fuid info for replay */ struct zfs_fuid_info *z_fuid_replay; /* fuid info for replay */
zilog_t *z_log; /* intent log pointer */ zilog_t *z_log; /* intent log pointer */
uint_t z_acl_inherit; /* acl inheritance behavior */ 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 */ zfs_case_t z_case; /* case-sense */
boolean_t z_utf8; /* utf8-only */ boolean_t z_utf8; /* utf8-only */
int z_norm; /* normalization flags */ int z_norm; /* normalization flags */

View 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 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, extern int zpl_xattr_security_init(struct inode *ip, struct inode *dip,
const struct qstr *qstr); 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[]; extern xattr_handler_t *zpl_xattr_handlers[];

View 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. 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 .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. 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 .RE
.sp .sp
.ne 2 .ne 2
.mk .mk
.na .na
\fB\fBaclmode\fR=\fBdiscard\fR | \fBgroupmask\fR | \fBpassthrough\fR\fR \fB\fBacltype\fR=\fBnoacl\fR | \fBposixacl\fR \fR
.ad .ad
.sp .6 .sp .6
.RS 4n .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 .RE
.sp .sp
@ -2696,8 +2710,8 @@ userprop other Allows changing any user property
userquota other Allows accessing any userquota@... property userquota other Allows accessing any userquota@... property
userused other Allows reading any userused@... property userused other Allows reading any userused@... property
acltype property
aclinherit property aclinherit property
aclmode property
atime property atime property
canmount property canmount property
casesensitivity property casesensitivity property
@ -3068,7 +3082,7 @@ pool/home/bob setuid on default
pool/home/bob readonly off default pool/home/bob readonly off default
pool/home/bob zoned off default pool/home/bob zoned off default
pool/home/bob snapdir hidden 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 aclinherit restricted default
pool/home/bob canmount on default pool/home/bob canmount on default
pool/home/bob shareiscsi off default pool/home/bob shareiscsi off default

View File

@ -112,6 +112,14 @@ zfs_prop_init(void)
{ NULL } { 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[] = { static zprop_index_t acl_inherit_table[] = {
{ "discard", ZFS_ACL_DISCARD }, { "discard", ZFS_ACL_DISCARD },
{ "noallow", ZFS_ACL_NOALLOW }, { "noallow", ZFS_ACL_NOALLOW },
@ -226,6 +234,9 @@ zfs_prop_init(void)
zprop_register_index(ZFS_PROP_SNAPDEV, "snapdev", ZFS_SNAPDEV_HIDDEN, zprop_register_index(ZFS_PROP_SNAPDEV, "snapdev", ZFS_SNAPDEV_HIDDEN,
PROP_INHERIT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, PROP_INHERIT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
"hidden | visible", "SNAPDEV", snapdev_table); "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", zprop_register_index(ZFS_PROP_ACLINHERIT, "aclinherit",
ZFS_ACL_RESTRICTED, PROP_INHERIT, ZFS_TYPE_FILESYSTEM, ZFS_ACL_RESTRICTED, PROP_INHERIT, ZFS_TYPE_FILESYSTEM,
"discard | noallow | restricted | passthrough | passthrough-x", "discard | noallow | restricted | passthrough | passthrough-x",

View File

@ -1155,6 +1155,9 @@ zfs_acl_chown_setattr(znode_t *zp)
int error; int error;
zfs_acl_t *aclp; 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_lock));
ASSERT(MUTEX_HELD(&zp->z_acl_lock)); ASSERT(MUTEX_HELD(&zp->z_acl_lock));

View 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 static void
blksz_changed_cb(void *arg, uint64_t newval) 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, error = error ? error : dsl_prop_register(ds,
zfs_prop_to_name(ZFS_PROP_SNAPDIR), snapdir_changed_cb, zsb); zfs_prop_to_name(ZFS_PROP_SNAPDIR), snapdir_changed_cb, zsb);
error = error ? error : dsl_prop_register(ds, error = error ? error : dsl_prop_register(ds,
zfs_prop_to_name(ZFS_PROP_ACLINHERIT), acl_inherit_changed_cb, zfs_prop_to_name(ZFS_PROP_ACLTYPE), acltype_changed_cb, zsb);
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, error = error ? error : dsl_prop_register(ds,
zfs_prop_to_name(ZFS_PROP_VSCAN), vscan_changed_cb, zsb); zfs_prop_to_name(ZFS_PROP_VSCAN), vscan_changed_cb, zsb);
error = error ? error : dsl_prop_register(ds, error = error ? error : dsl_prop_register(ds,
@ -303,6 +323,8 @@ unregister:
exec_changed_cb, zsb); exec_changed_cb, zsb);
(void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_SNAPDIR), (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_SNAPDIR),
snapdir_changed_cb, zsb); 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), (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_ACLINHERIT),
acl_inherit_changed_cb, zsb); acl_inherit_changed_cb, zsb);
(void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_VSCAN), (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; goto out;
zsb->z_case = (uint_t)zval; 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 * Fold case on file systems that are always or sometimes case
* insensitive. * insensitive.
@ -904,6 +930,9 @@ zfs_unregister_callbacks(zfs_sb_t *zsb)
VERIFY(dsl_prop_unregister(ds, "snapdir", snapdir_changed_cb, VERIFY(dsl_prop_unregister(ds, "snapdir", snapdir_changed_cb,
zsb) == 0); zsb) == 0);
VERIFY(dsl_prop_unregister(ds, "acltype", acltype_changed_cb,
zsb) == 0);
VERIFY(dsl_prop_unregister(ds, "aclinherit", VERIFY(dsl_prop_unregister(ds, "aclinherit",
acl_inherit_changed_cb, zsb) == 0); 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))) if ((error = dsl_prop_get_integer(osname,"xattr",&pval,NULL)))
goto out; goto out;
xattr_changed_cb(zsb, pval); 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_issnap = B_TRUE;
zsb->z_os->os_sync = ZFS_SYNC_DISABLED; 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: case ZFS_PROP_CASE:
*value = ZFS_CASE_SENSITIVE; *value = ZFS_CASE_SENSITIVE;
break; break;
case ZFS_PROP_ACLTYPE:
*value = ZFS_ACLTYPE_OFF;
break;
default: default:
return (error); return (error);
} }
@ -1632,6 +1667,7 @@ zfs_init(void)
void void
zfs_fini(void) zfs_fini(void)
{ {
taskq_wait(system_taskq);
unregister_filesystem(&zpl_fs_type); unregister_filesystem(&zpl_fs_type);
zfs_znode_fini(); zfs_znode_fini();
zfsctl_fini(); zfsctl_fini();

View 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 * 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 int
zfs_dirty_inode(struct inode *ip, int flags) 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); znode_t *zp = ITOZ(ip);
zfs_sb_t *zsb = ITOZSB(ip); zfs_sb_t *zsb = ITOZSB(ip);
dmu_tx_t *tx; dmu_tx_t *tx;
uint64_t atime[2], mtime[2], ctime[2]; uint64_t mode, atime[2], mtime[2], ctime[2];
sa_bulk_attr_t bulk[3]; sa_bulk_attr_t bulk[4];
int error; int error;
int cnt = 0; int cnt = 0;
@ -3999,14 +3999,18 @@ zfs_dirty_inode(struct inode *ip, int flags)
} }
mutex_enter(&zp->z_lock); 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_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_MTIME(zsb), NULL, &mtime, 16);
SA_ADD_BULK_ATTR(bulk, cnt, SA_ZPL_CTIME(zsb), NULL, &ctime, 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_atime, atime);
ZFS_TIME_ENCODE(&ip->i_mtime, mtime); ZFS_TIME_ENCODE(&ip->i_mtime, mtime);
ZFS_TIME_ENCODE(&ip->i_ctime, ctime); ZFS_TIME_ENCODE(&ip->i_ctime, ctime);
mode = ip->i_mode;
zp->z_mode = mode;
zp->z_atime_dirty = 0; zp->z_atime_dirty = 0;
error = sa_bulk_update(zp->z_sa_hdl, bulk, cnt, tx); error = sa_bulk_update(zp->z_sa_hdl, bulk, cnt, tx);

View 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); error = -zfs_create(dir, dname(dentry), vap, 0, mode, &ip, cr, 0, NULL);
if (error == 0) { if (error == 0) {
error = zpl_xattr_security_init(ip, dir, &dentry->d_name); VERIFY0(zpl_xattr_security_init(ip, dir, &dentry->d_name));
VERIFY3S(error, ==, 0); VERIFY0(zpl_init_acl(ip, dir));
d_instantiate(dentry, ip); d_instantiate(dentry, ip);
} }
@ -136,8 +136,10 @@ zpl_mknod(struct inode *dir, struct dentry *dentry, zpl_umode_t mode,
vap->va_rdev = rdev; vap->va_rdev = rdev;
error = -zfs_create(dir, dname(dentry), vap, 0, mode, &ip, cr, 0, NULL); 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); d_instantiate(dentry, ip);
}
kmem_free(vap, sizeof(vattr_t)); kmem_free(vap, sizeof(vattr_t));
crfree(cr); 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); zpl_vap_init(vap, dir, mode | S_IFDIR, cr);
error = -zfs_mkdir(dir, dname(dentry), vap, &ip, cr, 0, NULL); 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); d_instantiate(dentry, ip);
}
kmem_free(vap, sizeof(vattr_t)); kmem_free(vap, sizeof(vattr_t));
crfree(cr); crfree(cr);
@ -223,11 +227,12 @@ zpl_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
static int static int
zpl_setattr(struct dentry *dentry, struct iattr *ia) zpl_setattr(struct dentry *dentry, struct iattr *ia)
{ {
struct inode *ip = dentry->d_inode;
cred_t *cr = CRED(); cred_t *cr = CRED();
vattr_t *vap; vattr_t *vap;
int error; int error;
error = inode_change_ok(dentry->d_inode, ia); error = inode_change_ok(ip, ia);
if (error) if (error)
return (error); return (error);
@ -242,7 +247,9 @@ zpl_setattr(struct dentry *dentry, struct iattr *ia)
vap->va_mtime = ia->ia_mtime; vap->va_mtime = ia->ia_mtime;
vap->va_ctime = ia->ia_ctime; 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)); kmem_free(vap, sizeof(vattr_t));
crfree(cr); crfree(cr);
@ -455,6 +462,13 @@ const struct inode_operations zpl_inode_operations = {
#ifdef HAVE_INODE_FALLOCATE #ifdef HAVE_INODE_FALLOCATE
.fallocate = zpl_fallocate, .fallocate = zpl_fallocate,
#endif /* HAVE_INODE_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 = { const struct inode_operations zpl_dir_inode_operations = {
@ -473,6 +487,13 @@ const struct inode_operations zpl_dir_inode_operations = {
.getxattr = generic_getxattr, .getxattr = generic_getxattr,
.removexattr = generic_removexattr, .removexattr = generic_removexattr,
.listxattr = zpl_xattr_list, .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 = { const struct inode_operations zpl_symlink_inode_operations = {
@ -494,6 +515,13 @@ const struct inode_operations zpl_special_inode_operations = {
.getxattr = generic_getxattr, .getxattr = generic_getxattr,
.removexattr = generic_removexattr, .removexattr = generic_removexattr,
.listxattr = zpl_xattr_list, .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 = { dentry_operations_t zpl_dentry_operations = {

View File

@ -179,28 +179,48 @@ zpl_umount_begin(struct super_block *sb)
} }
/* /*
* The Linux VFS automatically handles the following flags: * ZFS specific features must be explicitly handled here, the VFS will
* MNT_NOSUID, MNT_NODEV, MNT_NOEXEC, MNT_NOATIME, MNT_READONLY * 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.
*/ */
static int
__zpl_show_options(struct seq_file *seq, zfs_sb_t *zsb)
{
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 #ifdef HAVE_SHOW_OPTIONS_WITH_DENTRY
static int static int
zpl_show_options(struct seq_file *seq, struct dentry *root) zpl_show_options(struct seq_file *seq, struct dentry *root)
{ {
zfs_sb_t *zsb = root->d_sb->s_fs_info; return __zpl_show_options(seq, root->d_sb->s_fs_info);
seq_printf(seq, ",%s", zsb->z_flags & ZSB_XATTR ? "xattr" : "noxattr");
return (0);
} }
#else #else
static int static int
zpl_show_options(struct seq_file *seq, struct vfsmount *vfsp) zpl_show_options(struct seq_file *seq, struct vfsmount *vfsp)
{ {
zfs_sb_t *zsb = vfsp->mnt_sb->s_fs_info; return __zpl_show_options(seq, vfsp->mnt_sb->s_fs_info);
seq_printf(seq, ",%s", zsb->z_flags & ZSB_XATTR ? "xattr" : "noxattr");
return (0);
} }
#endif /* HAVE_SHOW_OPTIONS_WITH_DENTRY */ #endif /* HAVE_SHOW_OPTIONS_WITH_DENTRY */

View File

@ -722,13 +722,476 @@ xattr_handler_t zpl_xattr_security_handler = {
.set = zpl_xattr_security_set, .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[] = { xattr_handler_t *zpl_xattr_handlers[] = {
&zpl_xattr_security_handler, &zpl_xattr_security_handler,
&zpl_xattr_trusted_handler, &zpl_xattr_trusted_handler,
&zpl_xattr_user_handler, &zpl_xattr_user_handler,
#ifdef HAVE_POSIX_ACLS
&zpl_xattr_acl_access_handler, &zpl_xattr_acl_access_handler,
&zpl_xattr_acl_default_handler, &zpl_xattr_acl_default_handler,
#endif /* HAVE_POSIX_ACLS */
NULL NULL
}; };