diff options
Diffstat (limited to 'include/linux')
-rw-r--r-- | include/linux/vfs_compat.h | 142 | ||||
-rw-r--r-- | include/linux/xattr_compat.h | 35 |
2 files changed, 177 insertions, 0 deletions
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 */ |