Merge tag 'sound-3.14-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai...
[firefly-linux-kernel-4.4.55.git] / fs / posix_acl.c
index 08218550b0dbc6b393313a89d877b0460085d46d..11c54fd51e16d6248926287c407b06da39495231 100644 (file)
 #include <linux/export.h>
 #include <linux/user_namespace.h>
 
-EXPORT_SYMBOL(posix_acl_init);
-EXPORT_SYMBOL(posix_acl_alloc);
-EXPORT_SYMBOL(posix_acl_valid);
-EXPORT_SYMBOL(posix_acl_equiv_mode);
-EXPORT_SYMBOL(posix_acl_from_mode);
+struct posix_acl **acl_by_type(struct inode *inode, int type)
+{
+       switch (type) {
+       case ACL_TYPE_ACCESS:
+               return &inode->i_acl;
+       case ACL_TYPE_DEFAULT:
+               return &inode->i_default_acl;
+       default:
+               BUG();
+       }
+}
+EXPORT_SYMBOL(acl_by_type);
+
+struct posix_acl *get_cached_acl(struct inode *inode, int type)
+{
+       struct posix_acl **p = acl_by_type(inode, type);
+       struct posix_acl *acl = ACCESS_ONCE(*p);
+       if (acl) {
+               spin_lock(&inode->i_lock);
+               acl = *p;
+               if (acl != ACL_NOT_CACHED)
+                       acl = posix_acl_dup(acl);
+               spin_unlock(&inode->i_lock);
+       }
+       return acl;
+}
+EXPORT_SYMBOL(get_cached_acl);
+
+struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type)
+{
+       return rcu_dereference(*acl_by_type(inode, type));
+}
+EXPORT_SYMBOL(get_cached_acl_rcu);
+
+void set_cached_acl(struct inode *inode, int type, struct posix_acl *acl)
+{
+       struct posix_acl **p = acl_by_type(inode, type);
+       struct posix_acl *old;
+       spin_lock(&inode->i_lock);
+       old = *p;
+       rcu_assign_pointer(*p, posix_acl_dup(acl));
+       spin_unlock(&inode->i_lock);
+       if (old != ACL_NOT_CACHED)
+               posix_acl_release(old);
+}
+EXPORT_SYMBOL(set_cached_acl);
+
+void forget_cached_acl(struct inode *inode, int type)
+{
+       struct posix_acl **p = acl_by_type(inode, type);
+       struct posix_acl *old;
+       spin_lock(&inode->i_lock);
+       old = *p;
+       *p = ACL_NOT_CACHED;
+       spin_unlock(&inode->i_lock);
+       if (old != ACL_NOT_CACHED)
+               posix_acl_release(old);
+}
+EXPORT_SYMBOL(forget_cached_acl);
+
+void forget_all_cached_acls(struct inode *inode)
+{
+       struct posix_acl *old_access, *old_default;
+       spin_lock(&inode->i_lock);
+       old_access = inode->i_acl;
+       old_default = inode->i_default_acl;
+       inode->i_acl = inode->i_default_acl = ACL_NOT_CACHED;
+       spin_unlock(&inode->i_lock);
+       if (old_access != ACL_NOT_CACHED)
+               posix_acl_release(old_access);
+       if (old_default != ACL_NOT_CACHED)
+               posix_acl_release(old_default);
+}
+EXPORT_SYMBOL(forget_all_cached_acls);
 
 struct posix_acl *get_acl(struct inode *inode, int type)
 {
@@ -63,6 +132,7 @@ posix_acl_init(struct posix_acl *acl, int count)
        atomic_set(&acl->a_refcount, 1);
        acl->a_count = count;
 }
+EXPORT_SYMBOL(posix_acl_init);
 
 /*
  * Allocate a new ACL with the specified number of entries.
@@ -77,6 +147,7 @@ posix_acl_alloc(int count, gfp_t flags)
                posix_acl_init(acl, count);
        return acl;
 }
+EXPORT_SYMBOL(posix_acl_alloc);
 
 /*
  * Clone an ACL.
@@ -104,8 +175,6 @@ posix_acl_valid(const struct posix_acl *acl)
 {
        const struct posix_acl_entry *pa, *pe;
        int state = ACL_USER_OBJ;
-       kuid_t prev_uid = INVALID_UID;
-       kgid_t prev_gid = INVALID_GID;
        int needs_mask = 0;
 
        FOREACH_ACL_ENTRY(pa, acl, pe) {
@@ -124,10 +193,6 @@ posix_acl_valid(const struct posix_acl *acl)
                                        return -EINVAL;
                                if (!uid_valid(pa->e_uid))
                                        return -EINVAL;
-                               if (uid_valid(prev_uid) &&
-                                   uid_lte(pa->e_uid, prev_uid))
-                                       return -EINVAL;
-                               prev_uid = pa->e_uid;
                                needs_mask = 1;
                                break;
 
@@ -143,10 +208,6 @@ posix_acl_valid(const struct posix_acl *acl)
                                        return -EINVAL;
                                if (!gid_valid(pa->e_gid))
                                        return -EINVAL;
-                               if (gid_valid(prev_gid) &&
-                                   gid_lte(pa->e_gid, prev_gid))
-                                       return -EINVAL;
-                               prev_gid = pa->e_gid;
                                needs_mask = 1;
                                break;
 
@@ -172,6 +233,7 @@ posix_acl_valid(const struct posix_acl *acl)
                return 0;
        return -EINVAL;
 }
+EXPORT_SYMBOL(posix_acl_valid);
 
 /*
  * Returns 0 if the acl can be exactly represented in the traditional
@@ -212,6 +274,7 @@ posix_acl_equiv_mode(const struct posix_acl *acl, umode_t *mode_p)
                 *mode_p = (*mode_p & ~S_IRWXUGO) | mode;
         return not_equiv;
 }
+EXPORT_SYMBOL(posix_acl_equiv_mode);
 
 /*
  * Create an ACL representing the file mode permission bits of an inode.
@@ -233,6 +296,7 @@ posix_acl_from_mode(umode_t mode, gfp_t flags)
        acl->a_entries[2].e_perm = (mode & S_IRWXO);
        return acl;
 }
+EXPORT_SYMBOL(posix_acl_from_mode);
 
 /*
  * Return 0 if current is granted want access to the inode
@@ -410,7 +474,7 @@ static int __posix_acl_chmod_masq(struct posix_acl *acl, umode_t mode)
 }
 
 int
-posix_acl_create(struct posix_acl **acl, gfp_t gfp, umode_t *mode_p)
+__posix_acl_create(struct posix_acl **acl, gfp_t gfp, umode_t *mode_p)
 {
        struct posix_acl *clone = posix_acl_clone(*acl, gfp);
        int err = -ENOMEM;
@@ -425,7 +489,7 @@ posix_acl_create(struct posix_acl **acl, gfp_t gfp, umode_t *mode_p)
        *acl = clone;
        return err;
 }
-EXPORT_SYMBOL(posix_acl_create);
+EXPORT_SYMBOL(__posix_acl_create);
 
 int
 __posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode)
@@ -446,7 +510,7 @@ __posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode)
 EXPORT_SYMBOL(__posix_acl_chmod);
 
 int
-posix_acl_chmod(struct inode *inode)
+posix_acl_chmod(struct inode *inode, umode_t mode)
 {
        struct posix_acl *acl;
        int ret = 0;
@@ -457,10 +521,13 @@ posix_acl_chmod(struct inode *inode)
                return -EOPNOTSUPP;
 
        acl = get_acl(inode, ACL_TYPE_ACCESS);
-       if (IS_ERR_OR_NULL(acl))
+       if (IS_ERR_OR_NULL(acl)) {
+               if (acl == ERR_PTR(-EOPNOTSUPP))
+                       return 0;
                return PTR_ERR(acl);
+       }
 
-       ret = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
+       ret = __posix_acl_chmod(&acl, GFP_KERNEL, mode);
        if (ret)
                return ret;
        ret = inode->i_op->set_acl(inode, acl, ACL_TYPE_ACCESS);
@@ -469,6 +536,58 @@ posix_acl_chmod(struct inode *inode)
 }
 EXPORT_SYMBOL(posix_acl_chmod);
 
+int
+posix_acl_create(struct inode *dir, umode_t *mode,
+               struct posix_acl **default_acl, struct posix_acl **acl)
+{
+       struct posix_acl *p;
+       int ret;
+
+       if (S_ISLNK(*mode) || !IS_POSIXACL(dir))
+               goto no_acl;
+
+       p = get_acl(dir, ACL_TYPE_DEFAULT);
+       if (IS_ERR(p)) {
+               if (p == ERR_PTR(-EOPNOTSUPP))
+                       goto apply_umask;
+               return PTR_ERR(p);
+       }
+
+       if (!p)
+               goto apply_umask;
+
+       *acl = posix_acl_clone(p, GFP_NOFS);
+       if (!*acl)
+               return -ENOMEM;
+
+       ret = posix_acl_create_masq(*acl, mode);
+       if (ret < 0) {
+               posix_acl_release(*acl);
+               return -ENOMEM;
+       }
+
+       if (ret == 0) {
+               posix_acl_release(*acl);
+               *acl = NULL;
+       }
+
+       if (!S_ISDIR(*mode)) {
+               posix_acl_release(p);
+               *default_acl = NULL;
+       } else {
+               *default_acl = p;
+       }
+       return 0;
+
+apply_umask:
+       *mode &= ~current_umask();
+no_acl:
+       *default_acl = NULL;
+       *acl = NULL;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(posix_acl_create);
+
 /*
  * Fix up the uids and gids in posix acl extended attributes in place.
  */
@@ -737,3 +856,39 @@ const struct xattr_handler posix_acl_default_xattr_handler = {
        .set = posix_acl_xattr_set,
 };
 EXPORT_SYMBOL_GPL(posix_acl_default_xattr_handler);
+
+int simple_set_acl(struct inode *inode, struct posix_acl *acl, int type)
+{
+       int error;
+
+       if (type == ACL_TYPE_ACCESS) {
+               error = posix_acl_equiv_mode(acl, &inode->i_mode);
+               if (error < 0)
+                       return 0;
+               if (error == 0)
+                       acl = NULL;
+       }
+
+       inode->i_ctime = CURRENT_TIME;
+       set_cached_acl(inode, type, acl);
+       return 0;
+}
+
+int simple_acl_create(struct inode *dir, struct inode *inode)
+{
+       struct posix_acl *default_acl, *acl;
+       int error;
+
+       error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
+       if (error)
+               return error;
+
+       set_cached_acl(inode, ACL_TYPE_DEFAULT, default_acl);
+       set_cached_acl(inode, ACL_TYPE_ACCESS, acl);
+
+       if (default_acl)
+               posix_acl_release(default_acl);
+       if (acl)
+               posix_acl_release(acl);
+       return 0;
+}