diff options
Diffstat (limited to 'include/linux/vfs_compat.h')
-rw-r--r-- | include/linux/vfs_compat.h | 142 |
1 files changed, 142 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 */ |