1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
35c8ebd57SChristoph Hellwig * Copyright (C) 2002,2003 by Andreas Gruenbacher <a.gruenbacher@computer.org>
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Fixes from William Schumacher incorporated on 15 March 2001.
61da177e4SLinus Torvalds * (Reported by Charles Bertsch, <CBertsch@microtest.com>).
71da177e4SLinus Torvalds */
81da177e4SLinus Torvalds
91da177e4SLinus Torvalds /*
101da177e4SLinus Torvalds * This file contains generic functions for manipulating
111da177e4SLinus Torvalds * POSIX 1003.1e draft standard 17 ACLs.
121da177e4SLinus Torvalds */
131da177e4SLinus Torvalds
141da177e4SLinus Torvalds #include <linux/kernel.h>
151da177e4SLinus Torvalds #include <linux/slab.h>
1660063497SArun Sharma #include <linux/atomic.h>
171da177e4SLinus Torvalds #include <linux/fs.h>
181da177e4SLinus Torvalds #include <linux/sched.h>
195b825c3aSIngo Molnar #include <linux/cred.h>
201da177e4SLinus Torvalds #include <linux/posix_acl.h>
215c8ebd57SChristoph Hellwig #include <linux/posix_acl_xattr.h>
222aeccbe9SChristoph Hellwig #include <linux/xattr.h>
23630d9c47SPaul Gortmaker #include <linux/export.h>
245c8ebd57SChristoph Hellwig #include <linux/user_namespace.h>
25332f606bSMiklos Szeredi #include <linux/namei.h>
26a793d79eSChristian Brauner #include <linux/mnt_idmapping.h>
2736f05cabSJeff Layton #include <linux/iversion.h>
28e4cc9163SChristian Brauner #include <linux/security.h>
29e4cc9163SChristian Brauner #include <linux/evm.h>
30e4cc9163SChristian Brauner #include <linux/fsnotify.h>
315970e15dSJeff Layton #include <linux/filelock.h>
32e4cc9163SChristian Brauner
33e4cc9163SChristian Brauner #include "internal.h"
341da177e4SLinus Torvalds
acl_by_type(struct inode * inode,int type)3504c57f45SAndreas Gruenbacher static struct posix_acl **acl_by_type(struct inode *inode, int type)
360afaa120SAndrew Morton {
370afaa120SAndrew Morton switch (type) {
380afaa120SAndrew Morton case ACL_TYPE_ACCESS:
390afaa120SAndrew Morton return &inode->i_acl;
400afaa120SAndrew Morton case ACL_TYPE_DEFAULT:
410afaa120SAndrew Morton return &inode->i_default_acl;
420afaa120SAndrew Morton default:
430afaa120SAndrew Morton BUG();
440afaa120SAndrew Morton }
450afaa120SAndrew Morton }
460afaa120SAndrew Morton
get_cached_acl(struct inode * inode,int type)470afaa120SAndrew Morton struct posix_acl *get_cached_acl(struct inode *inode, int type)
480afaa120SAndrew Morton {
490afaa120SAndrew Morton struct posix_acl **p = acl_by_type(inode, type);
50b8a7a3a6SAndreas Gruenbacher struct posix_acl *acl;
51b8a7a3a6SAndreas Gruenbacher
52b8a7a3a6SAndreas Gruenbacher for (;;) {
53b8a7a3a6SAndreas Gruenbacher rcu_read_lock();
54b8a7a3a6SAndreas Gruenbacher acl = rcu_dereference(*p);
55b8a7a3a6SAndreas Gruenbacher if (!acl || is_uncached_acl(acl) ||
5666717260SElena Reshetova refcount_inc_not_zero(&acl->a_refcount))
57b8a7a3a6SAndreas Gruenbacher break;
58b8a7a3a6SAndreas Gruenbacher rcu_read_unlock();
59b8a7a3a6SAndreas Gruenbacher cpu_relax();
600afaa120SAndrew Morton }
61b8a7a3a6SAndreas Gruenbacher rcu_read_unlock();
620afaa120SAndrew Morton return acl;
630afaa120SAndrew Morton }
640afaa120SAndrew Morton EXPORT_SYMBOL(get_cached_acl);
650afaa120SAndrew Morton
get_cached_acl_rcu(struct inode * inode,int type)660afaa120SAndrew Morton struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type)
670afaa120SAndrew Morton {
68332f606bSMiklos Szeredi struct posix_acl *acl = rcu_dereference(*acl_by_type(inode, type));
69332f606bSMiklos Szeredi
70332f606bSMiklos Szeredi if (acl == ACL_DONT_CACHE) {
71332f606bSMiklos Szeredi struct posix_acl *ret;
72332f606bSMiklos Szeredi
73cac2f8b8SChristian Brauner ret = inode->i_op->get_inode_acl(inode, type, LOOKUP_RCU);
74332f606bSMiklos Szeredi if (!IS_ERR(ret))
75332f606bSMiklos Szeredi acl = ret;
76332f606bSMiklos Szeredi }
77332f606bSMiklos Szeredi
78332f606bSMiklos Szeredi return acl;
790afaa120SAndrew Morton }
800afaa120SAndrew Morton EXPORT_SYMBOL(get_cached_acl_rcu);
810afaa120SAndrew Morton
set_cached_acl(struct inode * inode,int type,struct posix_acl * acl)820afaa120SAndrew Morton void set_cached_acl(struct inode *inode, int type, struct posix_acl *acl)
830afaa120SAndrew Morton {
840afaa120SAndrew Morton struct posix_acl **p = acl_by_type(inode, type);
850afaa120SAndrew Morton struct posix_acl *old;
86b8a7a3a6SAndreas Gruenbacher
87b8a7a3a6SAndreas Gruenbacher old = xchg(p, posix_acl_dup(acl));
88b8a7a3a6SAndreas Gruenbacher if (!is_uncached_acl(old))
890afaa120SAndrew Morton posix_acl_release(old);
900afaa120SAndrew Morton }
910afaa120SAndrew Morton EXPORT_SYMBOL(set_cached_acl);
920afaa120SAndrew Morton
__forget_cached_acl(struct posix_acl ** p)93b8a7a3a6SAndreas Gruenbacher static void __forget_cached_acl(struct posix_acl **p)
94b8a7a3a6SAndreas Gruenbacher {
95b8a7a3a6SAndreas Gruenbacher struct posix_acl *old;
96b8a7a3a6SAndreas Gruenbacher
97b8a7a3a6SAndreas Gruenbacher old = xchg(p, ACL_NOT_CACHED);
98b8a7a3a6SAndreas Gruenbacher if (!is_uncached_acl(old))
99b8a7a3a6SAndreas Gruenbacher posix_acl_release(old);
100b8a7a3a6SAndreas Gruenbacher }
101b8a7a3a6SAndreas Gruenbacher
forget_cached_acl(struct inode * inode,int type)1020afaa120SAndrew Morton void forget_cached_acl(struct inode *inode, int type)
1030afaa120SAndrew Morton {
104b8a7a3a6SAndreas Gruenbacher __forget_cached_acl(acl_by_type(inode, type));
1050afaa120SAndrew Morton }
1060afaa120SAndrew Morton EXPORT_SYMBOL(forget_cached_acl);
1070afaa120SAndrew Morton
forget_all_cached_acls(struct inode * inode)1080afaa120SAndrew Morton void forget_all_cached_acls(struct inode *inode)
1090afaa120SAndrew Morton {
110b8a7a3a6SAndreas Gruenbacher __forget_cached_acl(&inode->i_acl);
111b8a7a3a6SAndreas Gruenbacher __forget_cached_acl(&inode->i_default_acl);
1120afaa120SAndrew Morton }
1130afaa120SAndrew Morton EXPORT_SYMBOL(forget_all_cached_acls);
1141da177e4SLinus Torvalds
__get_acl(struct mnt_idmap * idmap,struct dentry * dentry,struct inode * inode,int type)11577435322SChristian Brauner static struct posix_acl *__get_acl(struct mnt_idmap *idmap,
1164f353ba4SChristian Brauner struct dentry *dentry, struct inode *inode,
1174f353ba4SChristian Brauner int type)
1182982baa2SChristoph Hellwig {
119d6fdf29fSUros Bizjak struct posix_acl *sentinel;
120b8a7a3a6SAndreas Gruenbacher struct posix_acl **p;
1212982baa2SChristoph Hellwig struct posix_acl *acl;
1222982baa2SChristoph Hellwig
123b8a7a3a6SAndreas Gruenbacher /*
124b8a7a3a6SAndreas Gruenbacher * The sentinel is used to detect when another operation like
125cac2f8b8SChristian Brauner * set_cached_acl() or forget_cached_acl() races with get_inode_acl().
126b8a7a3a6SAndreas Gruenbacher * It is guaranteed that is_uncached_acl(sentinel) is true.
127b8a7a3a6SAndreas Gruenbacher */
128b8a7a3a6SAndreas Gruenbacher
1292982baa2SChristoph Hellwig acl = get_cached_acl(inode, type);
130b8a7a3a6SAndreas Gruenbacher if (!is_uncached_acl(acl))
1312982baa2SChristoph Hellwig return acl;
1322982baa2SChristoph Hellwig
1332982baa2SChristoph Hellwig if (!IS_POSIXACL(inode))
1342982baa2SChristoph Hellwig return NULL;
1352982baa2SChristoph Hellwig
136b8a7a3a6SAndreas Gruenbacher sentinel = uncached_acl_sentinel(current);
137b8a7a3a6SAndreas Gruenbacher p = acl_by_type(inode, type);
138b8a7a3a6SAndreas Gruenbacher
1392982baa2SChristoph Hellwig /*
140b8a7a3a6SAndreas Gruenbacher * If the ACL isn't being read yet, set our sentinel. Otherwise, the
141b8a7a3a6SAndreas Gruenbacher * current value of the ACL will not be ACL_NOT_CACHED and so our own
142b8a7a3a6SAndreas Gruenbacher * sentinel will not be set; another task will update the cache. We
143b8a7a3a6SAndreas Gruenbacher * could wait for that other task to complete its job, but it's easier
144cac2f8b8SChristian Brauner * to just call ->get_inode_acl to fetch the ACL ourself. (This is
145cac2f8b8SChristian Brauner * going to be an unlikely race.)
146b8a7a3a6SAndreas Gruenbacher */
147d1cef29aSArnd Bergmann cmpxchg(p, ACL_NOT_CACHED, sentinel);
148b8a7a3a6SAndreas Gruenbacher
149b8a7a3a6SAndreas Gruenbacher /*
1504f353ba4SChristian Brauner * Normally, the ACL returned by ->get{_inode}_acl will be cached.
151b8a7a3a6SAndreas Gruenbacher * A filesystem can prevent that by calling
1524f353ba4SChristian Brauner * forget_cached_acl(inode, type) in ->get{_inode}_acl.
1532982baa2SChristoph Hellwig *
1544f353ba4SChristian Brauner * If the filesystem doesn't have a get{_inode}_ acl() function at all,
155cac2f8b8SChristian Brauner * we'll just create the negative cache entry.
1562982baa2SChristoph Hellwig */
1574f353ba4SChristian Brauner if (dentry && inode->i_op->get_acl) {
15877435322SChristian Brauner acl = inode->i_op->get_acl(idmap, dentry, type);
1594f353ba4SChristian Brauner } else if (inode->i_op->get_inode_acl) {
1604f353ba4SChristian Brauner acl = inode->i_op->get_inode_acl(inode, type, false);
1614f353ba4SChristian Brauner } else {
1622982baa2SChristoph Hellwig set_cached_acl(inode, type, NULL);
1632982baa2SChristoph Hellwig return NULL;
1642982baa2SChristoph Hellwig }
165b8a7a3a6SAndreas Gruenbacher if (IS_ERR(acl)) {
166b8a7a3a6SAndreas Gruenbacher /*
167b8a7a3a6SAndreas Gruenbacher * Remove our sentinel so that we don't block future attempts
168b8a7a3a6SAndreas Gruenbacher * to cache the ACL.
169b8a7a3a6SAndreas Gruenbacher */
170b8a7a3a6SAndreas Gruenbacher cmpxchg(p, sentinel, ACL_NOT_CACHED);
171b8a7a3a6SAndreas Gruenbacher return acl;
172b8a7a3a6SAndreas Gruenbacher }
173b8a7a3a6SAndreas Gruenbacher
174b8a7a3a6SAndreas Gruenbacher /*
175b8a7a3a6SAndreas Gruenbacher * Cache the result, but only if our sentinel is still in place.
176b8a7a3a6SAndreas Gruenbacher */
177b8a7a3a6SAndreas Gruenbacher posix_acl_dup(acl);
1784e1da8feSUros Bizjak if (unlikely(!try_cmpxchg(p, &sentinel, acl)))
179b8a7a3a6SAndreas Gruenbacher posix_acl_release(acl);
180b8a7a3a6SAndreas Gruenbacher return acl;
1812982baa2SChristoph Hellwig }
1824f353ba4SChristian Brauner
get_inode_acl(struct inode * inode,int type)1834f353ba4SChristian Brauner struct posix_acl *get_inode_acl(struct inode *inode, int type)
1844f353ba4SChristian Brauner {
18577435322SChristian Brauner return __get_acl(&nop_mnt_idmap, NULL, inode, type);
1864f353ba4SChristian Brauner }
187cac2f8b8SChristian Brauner EXPORT_SYMBOL(get_inode_acl);
1882982baa2SChristoph Hellwig
1891da177e4SLinus Torvalds /*
190f61f6da0SChuck Lever * Init a fresh posix_acl
191f61f6da0SChuck Lever */
192f61f6da0SChuck Lever void
posix_acl_init(struct posix_acl * acl,int count)193f61f6da0SChuck Lever posix_acl_init(struct posix_acl *acl, int count)
194f61f6da0SChuck Lever {
19566717260SElena Reshetova refcount_set(&acl->a_refcount, 1);
196f61f6da0SChuck Lever acl->a_count = count;
197f61f6da0SChuck Lever }
1980afaa120SAndrew Morton EXPORT_SYMBOL(posix_acl_init);
199f61f6da0SChuck Lever
200f61f6da0SChuck Lever /*
2011da177e4SLinus Torvalds * Allocate a new ACL with the specified number of entries.
2021da177e4SLinus Torvalds */
2031da177e4SLinus Torvalds struct posix_acl *
posix_acl_alloc(int count,gfp_t flags)204dd0fc66fSAl Viro posix_acl_alloc(int count, gfp_t flags)
2051da177e4SLinus Torvalds {
2061da177e4SLinus Torvalds const size_t size = sizeof(struct posix_acl) +
2071da177e4SLinus Torvalds count * sizeof(struct posix_acl_entry);
2081da177e4SLinus Torvalds struct posix_acl *acl = kmalloc(size, flags);
209f61f6da0SChuck Lever if (acl)
210f61f6da0SChuck Lever posix_acl_init(acl, count);
2111da177e4SLinus Torvalds return acl;
2121da177e4SLinus Torvalds }
2130afaa120SAndrew Morton EXPORT_SYMBOL(posix_acl_alloc);
2141da177e4SLinus Torvalds
2151da177e4SLinus Torvalds /*
2161da177e4SLinus Torvalds * Clone an ACL.
2171da177e4SLinus Torvalds */
2188043bffdSChristian Brauner struct posix_acl *
posix_acl_clone(const struct posix_acl * acl,gfp_t flags)219dd0fc66fSAl Viro posix_acl_clone(const struct posix_acl *acl, gfp_t flags)
2201da177e4SLinus Torvalds {
2211da177e4SLinus Torvalds struct posix_acl *clone = NULL;
2221da177e4SLinus Torvalds
2231da177e4SLinus Torvalds if (acl) {
2241da177e4SLinus Torvalds int size = sizeof(struct posix_acl) + acl->a_count *
2251da177e4SLinus Torvalds sizeof(struct posix_acl_entry);
22652978be6SAlexey Dobriyan clone = kmemdup(acl, size, flags);
22752978be6SAlexey Dobriyan if (clone)
22866717260SElena Reshetova refcount_set(&clone->a_refcount, 1);
2291da177e4SLinus Torvalds }
2301da177e4SLinus Torvalds return clone;
2311da177e4SLinus Torvalds }
2328043bffdSChristian Brauner EXPORT_SYMBOL_GPL(posix_acl_clone);
2331da177e4SLinus Torvalds
2341da177e4SLinus Torvalds /*
2351da177e4SLinus Torvalds * Check if an acl is valid. Returns 0 if it is, or -E... otherwise.
2361da177e4SLinus Torvalds */
2371da177e4SLinus Torvalds int
posix_acl_valid(struct user_namespace * user_ns,const struct posix_acl * acl)2380d4d717fSEric W. Biederman posix_acl_valid(struct user_namespace *user_ns, const struct posix_acl *acl)
2391da177e4SLinus Torvalds {
2401da177e4SLinus Torvalds const struct posix_acl_entry *pa, *pe;
2411da177e4SLinus Torvalds int state = ACL_USER_OBJ;
2421da177e4SLinus Torvalds int needs_mask = 0;
2431da177e4SLinus Torvalds
2441da177e4SLinus Torvalds FOREACH_ACL_ENTRY(pa, acl, pe) {
2451da177e4SLinus Torvalds if (pa->e_perm & ~(ACL_READ|ACL_WRITE|ACL_EXECUTE))
2461da177e4SLinus Torvalds return -EINVAL;
2471da177e4SLinus Torvalds switch (pa->e_tag) {
2481da177e4SLinus Torvalds case ACL_USER_OBJ:
2491da177e4SLinus Torvalds if (state == ACL_USER_OBJ) {
2501da177e4SLinus Torvalds state = ACL_USER;
2511da177e4SLinus Torvalds break;
2521da177e4SLinus Torvalds }
2531da177e4SLinus Torvalds return -EINVAL;
2541da177e4SLinus Torvalds
2551da177e4SLinus Torvalds case ACL_USER:
2561da177e4SLinus Torvalds if (state != ACL_USER)
2571da177e4SLinus Torvalds return -EINVAL;
2580d4d717fSEric W. Biederman if (!kuid_has_mapping(user_ns, pa->e_uid))
2591da177e4SLinus Torvalds return -EINVAL;
2601da177e4SLinus Torvalds needs_mask = 1;
2611da177e4SLinus Torvalds break;
2621da177e4SLinus Torvalds
2631da177e4SLinus Torvalds case ACL_GROUP_OBJ:
2641da177e4SLinus Torvalds if (state == ACL_USER) {
2651da177e4SLinus Torvalds state = ACL_GROUP;
2661da177e4SLinus Torvalds break;
2671da177e4SLinus Torvalds }
2681da177e4SLinus Torvalds return -EINVAL;
2691da177e4SLinus Torvalds
2701da177e4SLinus Torvalds case ACL_GROUP:
2711da177e4SLinus Torvalds if (state != ACL_GROUP)
2721da177e4SLinus Torvalds return -EINVAL;
2730d4d717fSEric W. Biederman if (!kgid_has_mapping(user_ns, pa->e_gid))
2741da177e4SLinus Torvalds return -EINVAL;
2751da177e4SLinus Torvalds needs_mask = 1;
2761da177e4SLinus Torvalds break;
2771da177e4SLinus Torvalds
2781da177e4SLinus Torvalds case ACL_MASK:
2791da177e4SLinus Torvalds if (state != ACL_GROUP)
2801da177e4SLinus Torvalds return -EINVAL;
2811da177e4SLinus Torvalds state = ACL_OTHER;
2821da177e4SLinus Torvalds break;
2831da177e4SLinus Torvalds
2841da177e4SLinus Torvalds case ACL_OTHER:
2851da177e4SLinus Torvalds if (state == ACL_OTHER ||
2861da177e4SLinus Torvalds (state == ACL_GROUP && !needs_mask)) {
2871da177e4SLinus Torvalds state = 0;
2881da177e4SLinus Torvalds break;
2891da177e4SLinus Torvalds }
2901da177e4SLinus Torvalds return -EINVAL;
2911da177e4SLinus Torvalds
2921da177e4SLinus Torvalds default:
2931da177e4SLinus Torvalds return -EINVAL;
2941da177e4SLinus Torvalds }
2951da177e4SLinus Torvalds }
2961da177e4SLinus Torvalds if (state == 0)
2971da177e4SLinus Torvalds return 0;
2981da177e4SLinus Torvalds return -EINVAL;
2991da177e4SLinus Torvalds }
3000afaa120SAndrew Morton EXPORT_SYMBOL(posix_acl_valid);
3011da177e4SLinus Torvalds
3021da177e4SLinus Torvalds /*
3031da177e4SLinus Torvalds * Returns 0 if the acl can be exactly represented in the traditional
3041da177e4SLinus Torvalds * file mode permission bits, or else 1. Returns -E... on error.
3051da177e4SLinus Torvalds */
3061da177e4SLinus Torvalds int
posix_acl_equiv_mode(const struct posix_acl * acl,umode_t * mode_p)307d6952123SAl Viro posix_acl_equiv_mode(const struct posix_acl *acl, umode_t *mode_p)
3081da177e4SLinus Torvalds {
3091da177e4SLinus Torvalds const struct posix_acl_entry *pa, *pe;
310d6952123SAl Viro umode_t mode = 0;
3111da177e4SLinus Torvalds int not_equiv = 0;
3121da177e4SLinus Torvalds
31350c6e282SChristoph Hellwig /*
31450c6e282SChristoph Hellwig * A null ACL can always be presented as mode bits.
31550c6e282SChristoph Hellwig */
31650c6e282SChristoph Hellwig if (!acl)
31750c6e282SChristoph Hellwig return 0;
31850c6e282SChristoph Hellwig
3191da177e4SLinus Torvalds FOREACH_ACL_ENTRY(pa, acl, pe) {
3201da177e4SLinus Torvalds switch (pa->e_tag) {
3211da177e4SLinus Torvalds case ACL_USER_OBJ:
3221da177e4SLinus Torvalds mode |= (pa->e_perm & S_IRWXO) << 6;
3231da177e4SLinus Torvalds break;
3241da177e4SLinus Torvalds case ACL_GROUP_OBJ:
3251da177e4SLinus Torvalds mode |= (pa->e_perm & S_IRWXO) << 3;
3261da177e4SLinus Torvalds break;
3271da177e4SLinus Torvalds case ACL_OTHER:
3281da177e4SLinus Torvalds mode |= pa->e_perm & S_IRWXO;
3291da177e4SLinus Torvalds break;
3301da177e4SLinus Torvalds case ACL_MASK:
3311da177e4SLinus Torvalds mode = (mode & ~S_IRWXG) |
3321da177e4SLinus Torvalds ((pa->e_perm & S_IRWXO) << 3);
3331da177e4SLinus Torvalds not_equiv = 1;
3341da177e4SLinus Torvalds break;
3351da177e4SLinus Torvalds case ACL_USER:
3361da177e4SLinus Torvalds case ACL_GROUP:
3371da177e4SLinus Torvalds not_equiv = 1;
3381da177e4SLinus Torvalds break;
3391da177e4SLinus Torvalds default:
3401da177e4SLinus Torvalds return -EINVAL;
3411da177e4SLinus Torvalds }
3421da177e4SLinus Torvalds }
3431da177e4SLinus Torvalds if (mode_p)
3441da177e4SLinus Torvalds *mode_p = (*mode_p & ~S_IRWXUGO) | mode;
3451da177e4SLinus Torvalds return not_equiv;
3461da177e4SLinus Torvalds }
3470afaa120SAndrew Morton EXPORT_SYMBOL(posix_acl_equiv_mode);
3481da177e4SLinus Torvalds
3491da177e4SLinus Torvalds /*
3501da177e4SLinus Torvalds * Create an ACL representing the file mode permission bits of an inode.
3511da177e4SLinus Torvalds */
3521da177e4SLinus Torvalds struct posix_acl *
posix_acl_from_mode(umode_t mode,gfp_t flags)3533a5fba19SAl Viro posix_acl_from_mode(umode_t mode, gfp_t flags)
3541da177e4SLinus Torvalds {
3551da177e4SLinus Torvalds struct posix_acl *acl = posix_acl_alloc(3, flags);
3561da177e4SLinus Torvalds if (!acl)
3571da177e4SLinus Torvalds return ERR_PTR(-ENOMEM);
3581da177e4SLinus Torvalds
3591da177e4SLinus Torvalds acl->a_entries[0].e_tag = ACL_USER_OBJ;
3601da177e4SLinus Torvalds acl->a_entries[0].e_perm = (mode & S_IRWXU) >> 6;
3611da177e4SLinus Torvalds
3621da177e4SLinus Torvalds acl->a_entries[1].e_tag = ACL_GROUP_OBJ;
3631da177e4SLinus Torvalds acl->a_entries[1].e_perm = (mode & S_IRWXG) >> 3;
3641da177e4SLinus Torvalds
3651da177e4SLinus Torvalds acl->a_entries[2].e_tag = ACL_OTHER;
3661da177e4SLinus Torvalds acl->a_entries[2].e_perm = (mode & S_IRWXO);
3671da177e4SLinus Torvalds return acl;
3681da177e4SLinus Torvalds }
3690afaa120SAndrew Morton EXPORT_SYMBOL(posix_acl_from_mode);
3701da177e4SLinus Torvalds
3711da177e4SLinus Torvalds /*
3721da177e4SLinus Torvalds * Return 0 if current is granted want access to the inode
3731da177e4SLinus Torvalds * by the acl. Returns -E... otherwise.
3741da177e4SLinus Torvalds */
3751da177e4SLinus Torvalds int
posix_acl_permission(struct mnt_idmap * idmap,struct inode * inode,const struct posix_acl * acl,int want)376700b7940SChristian Brauner posix_acl_permission(struct mnt_idmap *idmap, struct inode *inode,
37747291baaSChristian Brauner const struct posix_acl *acl, int want)
3781da177e4SLinus Torvalds {
3791da177e4SLinus Torvalds const struct posix_acl_entry *pa, *pe, *mask_obj;
380abfcf55dSChristian Brauner struct user_namespace *fs_userns = i_user_ns(inode);
3811da177e4SLinus Torvalds int found = 0;
382e933c15fSChristian Brauner vfsuid_t vfsuid;
383e933c15fSChristian Brauner vfsgid_t vfsgid;
3841da177e4SLinus Torvalds
38563d72b93SLinus Torvalds want &= MAY_READ | MAY_WRITE | MAY_EXEC;
386d124b60aSAndreas Gruenbacher
3871da177e4SLinus Torvalds FOREACH_ACL_ENTRY(pa, acl, pe) {
3881da177e4SLinus Torvalds switch(pa->e_tag) {
3891da177e4SLinus Torvalds case ACL_USER_OBJ:
3901da177e4SLinus Torvalds /* (May have been checked already) */
391e67fe633SChristian Brauner vfsuid = i_uid_into_vfsuid(idmap, inode);
392e933c15fSChristian Brauner if (vfsuid_eq_kuid(vfsuid, current_fsuid()))
3931da177e4SLinus Torvalds goto check_perm;
3941da177e4SLinus Torvalds break;
3951da177e4SLinus Torvalds case ACL_USER:
3964d7ca409SChristian Brauner vfsuid = make_vfsuid(idmap, fs_userns,
39744720713SChristian Brauner pa->e_uid);
398e933c15fSChristian Brauner if (vfsuid_eq_kuid(vfsuid, current_fsuid()))
3991da177e4SLinus Torvalds goto mask;
4001da177e4SLinus Torvalds break;
4011da177e4SLinus Torvalds case ACL_GROUP_OBJ:
402e67fe633SChristian Brauner vfsgid = i_gid_into_vfsgid(idmap, inode);
403e933c15fSChristian Brauner if (vfsgid_in_group_p(vfsgid)) {
4041da177e4SLinus Torvalds found = 1;
4051da177e4SLinus Torvalds if ((pa->e_perm & want) == want)
4061da177e4SLinus Torvalds goto mask;
4071da177e4SLinus Torvalds }
4081da177e4SLinus Torvalds break;
4091da177e4SLinus Torvalds case ACL_GROUP:
4104d7ca409SChristian Brauner vfsgid = make_vfsgid(idmap, fs_userns,
41144720713SChristian Brauner pa->e_gid);
412e933c15fSChristian Brauner if (vfsgid_in_group_p(vfsgid)) {
4131da177e4SLinus Torvalds found = 1;
4141da177e4SLinus Torvalds if ((pa->e_perm & want) == want)
4151da177e4SLinus Torvalds goto mask;
4161da177e4SLinus Torvalds }
4171da177e4SLinus Torvalds break;
4181da177e4SLinus Torvalds case ACL_MASK:
4191da177e4SLinus Torvalds break;
4201da177e4SLinus Torvalds case ACL_OTHER:
4211da177e4SLinus Torvalds if (found)
4221da177e4SLinus Torvalds return -EACCES;
4231da177e4SLinus Torvalds else
4241da177e4SLinus Torvalds goto check_perm;
4251da177e4SLinus Torvalds default:
4261da177e4SLinus Torvalds return -EIO;
4271da177e4SLinus Torvalds }
4281da177e4SLinus Torvalds }
4291da177e4SLinus Torvalds return -EIO;
4301da177e4SLinus Torvalds
4311da177e4SLinus Torvalds mask:
4321da177e4SLinus Torvalds for (mask_obj = pa+1; mask_obj != pe; mask_obj++) {
4331da177e4SLinus Torvalds if (mask_obj->e_tag == ACL_MASK) {
4341da177e4SLinus Torvalds if ((pa->e_perm & mask_obj->e_perm & want) == want)
4351da177e4SLinus Torvalds return 0;
4361da177e4SLinus Torvalds return -EACCES;
4371da177e4SLinus Torvalds }
4381da177e4SLinus Torvalds }
4391da177e4SLinus Torvalds
4401da177e4SLinus Torvalds check_perm:
4411da177e4SLinus Torvalds if ((pa->e_perm & want) == want)
4421da177e4SLinus Torvalds return 0;
4431da177e4SLinus Torvalds return -EACCES;
4441da177e4SLinus Torvalds }
4451da177e4SLinus Torvalds
4461da177e4SLinus Torvalds /*
4471da177e4SLinus Torvalds * Modify acl when creating a new inode. The caller must ensure the acl is
4481da177e4SLinus Torvalds * only referenced once.
4491da177e4SLinus Torvalds *
4501da177e4SLinus Torvalds * mode_p initially must contain the mode parameter to the open() / creat()
4511da177e4SLinus Torvalds * system calls. All permissions that are not granted by the acl are removed.
4521da177e4SLinus Torvalds * The permissions in the acl are changed to reflect the mode_p parameter.
4531da177e4SLinus Torvalds */
posix_acl_create_masq(struct posix_acl * acl,umode_t * mode_p)454d3fb6120SAl Viro static int posix_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
4551da177e4SLinus Torvalds {
4561da177e4SLinus Torvalds struct posix_acl_entry *pa, *pe;
4571da177e4SLinus Torvalds struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
458d3fb6120SAl Viro umode_t mode = *mode_p;
4591da177e4SLinus Torvalds int not_equiv = 0;
4601da177e4SLinus Torvalds
4611da177e4SLinus Torvalds /* assert(atomic_read(acl->a_refcount) == 1); */
4621da177e4SLinus Torvalds
4631da177e4SLinus Torvalds FOREACH_ACL_ENTRY(pa, acl, pe) {
4641da177e4SLinus Torvalds switch(pa->e_tag) {
4651da177e4SLinus Torvalds case ACL_USER_OBJ:
4661da177e4SLinus Torvalds pa->e_perm &= (mode >> 6) | ~S_IRWXO;
4671da177e4SLinus Torvalds mode &= (pa->e_perm << 6) | ~S_IRWXU;
4681da177e4SLinus Torvalds break;
4691da177e4SLinus Torvalds
4701da177e4SLinus Torvalds case ACL_USER:
4711da177e4SLinus Torvalds case ACL_GROUP:
4721da177e4SLinus Torvalds not_equiv = 1;
4731da177e4SLinus Torvalds break;
4741da177e4SLinus Torvalds
4751da177e4SLinus Torvalds case ACL_GROUP_OBJ:
4761da177e4SLinus Torvalds group_obj = pa;
4771da177e4SLinus Torvalds break;
4781da177e4SLinus Torvalds
4791da177e4SLinus Torvalds case ACL_OTHER:
4801da177e4SLinus Torvalds pa->e_perm &= mode | ~S_IRWXO;
4811da177e4SLinus Torvalds mode &= pa->e_perm | ~S_IRWXO;
4821da177e4SLinus Torvalds break;
4831da177e4SLinus Torvalds
4841da177e4SLinus Torvalds case ACL_MASK:
4851da177e4SLinus Torvalds mask_obj = pa;
4861da177e4SLinus Torvalds not_equiv = 1;
4871da177e4SLinus Torvalds break;
4881da177e4SLinus Torvalds
4891da177e4SLinus Torvalds default:
4901da177e4SLinus Torvalds return -EIO;
4911da177e4SLinus Torvalds }
4921da177e4SLinus Torvalds }
4931da177e4SLinus Torvalds
4941da177e4SLinus Torvalds if (mask_obj) {
4951da177e4SLinus Torvalds mask_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
4961da177e4SLinus Torvalds mode &= (mask_obj->e_perm << 3) | ~S_IRWXG;
4971da177e4SLinus Torvalds } else {
4981da177e4SLinus Torvalds if (!group_obj)
4991da177e4SLinus Torvalds return -EIO;
5001da177e4SLinus Torvalds group_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
5011da177e4SLinus Torvalds mode &= (group_obj->e_perm << 3) | ~S_IRWXG;
5021da177e4SLinus Torvalds }
5031da177e4SLinus Torvalds
5041da177e4SLinus Torvalds *mode_p = (*mode_p & ~S_IRWXUGO) | mode;
5051da177e4SLinus Torvalds return not_equiv;
5061da177e4SLinus Torvalds }
5071da177e4SLinus Torvalds
5081da177e4SLinus Torvalds /*
5091da177e4SLinus Torvalds * Modify the ACL for the chmod syscall.
5101da177e4SLinus Torvalds */
__posix_acl_chmod_masq(struct posix_acl * acl,umode_t mode)5115bf3258fSChristoph Hellwig static int __posix_acl_chmod_masq(struct posix_acl *acl, umode_t mode)
5121da177e4SLinus Torvalds {
5131da177e4SLinus Torvalds struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
5141da177e4SLinus Torvalds struct posix_acl_entry *pa, *pe;
5151da177e4SLinus Torvalds
5161da177e4SLinus Torvalds /* assert(atomic_read(acl->a_refcount) == 1); */
5171da177e4SLinus Torvalds
5181da177e4SLinus Torvalds FOREACH_ACL_ENTRY(pa, acl, pe) {
5191da177e4SLinus Torvalds switch(pa->e_tag) {
5201da177e4SLinus Torvalds case ACL_USER_OBJ:
5211da177e4SLinus Torvalds pa->e_perm = (mode & S_IRWXU) >> 6;
5221da177e4SLinus Torvalds break;
5231da177e4SLinus Torvalds
5241da177e4SLinus Torvalds case ACL_USER:
5251da177e4SLinus Torvalds case ACL_GROUP:
5261da177e4SLinus Torvalds break;
5271da177e4SLinus Torvalds
5281da177e4SLinus Torvalds case ACL_GROUP_OBJ:
5291da177e4SLinus Torvalds group_obj = pa;
5301da177e4SLinus Torvalds break;
5311da177e4SLinus Torvalds
5321da177e4SLinus Torvalds case ACL_MASK:
5331da177e4SLinus Torvalds mask_obj = pa;
5341da177e4SLinus Torvalds break;
5351da177e4SLinus Torvalds
5361da177e4SLinus Torvalds case ACL_OTHER:
5371da177e4SLinus Torvalds pa->e_perm = (mode & S_IRWXO);
5381da177e4SLinus Torvalds break;
5391da177e4SLinus Torvalds
5401da177e4SLinus Torvalds default:
5411da177e4SLinus Torvalds return -EIO;
5421da177e4SLinus Torvalds }
5431da177e4SLinus Torvalds }
5441da177e4SLinus Torvalds
5451da177e4SLinus Torvalds if (mask_obj) {
5461da177e4SLinus Torvalds mask_obj->e_perm = (mode & S_IRWXG) >> 3;
5471da177e4SLinus Torvalds } else {
5481da177e4SLinus Torvalds if (!group_obj)
5491da177e4SLinus Torvalds return -EIO;
5501da177e4SLinus Torvalds group_obj->e_perm = (mode & S_IRWXG) >> 3;
5511da177e4SLinus Torvalds }
5521da177e4SLinus Torvalds
5531da177e4SLinus Torvalds return 0;
5541da177e4SLinus Torvalds }
555bc26ab5fSAl Viro
556bc26ab5fSAl Viro int
__posix_acl_create(struct posix_acl ** acl,gfp_t gfp,umode_t * mode_p)55737bc1539SChristoph Hellwig __posix_acl_create(struct posix_acl **acl, gfp_t gfp, umode_t *mode_p)
558826cae2fSAl Viro {
559826cae2fSAl Viro struct posix_acl *clone = posix_acl_clone(*acl, gfp);
560826cae2fSAl Viro int err = -ENOMEM;
561826cae2fSAl Viro if (clone) {
562826cae2fSAl Viro err = posix_acl_create_masq(clone, mode_p);
563826cae2fSAl Viro if (err < 0) {
564826cae2fSAl Viro posix_acl_release(clone);
565826cae2fSAl Viro clone = NULL;
566826cae2fSAl Viro }
567826cae2fSAl Viro }
568826cae2fSAl Viro posix_acl_release(*acl);
569826cae2fSAl Viro *acl = clone;
570826cae2fSAl Viro return err;
571826cae2fSAl Viro }
57237bc1539SChristoph Hellwig EXPORT_SYMBOL(__posix_acl_create);
573826cae2fSAl Viro
574826cae2fSAl Viro int
__posix_acl_chmod(struct posix_acl ** acl,gfp_t gfp,umode_t mode)5755bf3258fSChristoph Hellwig __posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode)
576bc26ab5fSAl Viro {
577bc26ab5fSAl Viro struct posix_acl *clone = posix_acl_clone(*acl, gfp);
578bc26ab5fSAl Viro int err = -ENOMEM;
579bc26ab5fSAl Viro if (clone) {
5805bf3258fSChristoph Hellwig err = __posix_acl_chmod_masq(clone, mode);
581bc26ab5fSAl Viro if (err) {
582bc26ab5fSAl Viro posix_acl_release(clone);
583bc26ab5fSAl Viro clone = NULL;
584bc26ab5fSAl Viro }
585bc26ab5fSAl Viro }
586bc26ab5fSAl Viro posix_acl_release(*acl);
587bc26ab5fSAl Viro *acl = clone;
588bc26ab5fSAl Viro return err;
589bc26ab5fSAl Viro }
5905bf3258fSChristoph Hellwig EXPORT_SYMBOL(__posix_acl_chmod);
5915bf3258fSChristoph Hellwig
592e65ce2a5SChristian Brauner /**
593e65ce2a5SChristian Brauner * posix_acl_chmod - chmod a posix acl
594e65ce2a5SChristian Brauner *
59513e83a49SChristian Brauner * @idmap: idmap of the mount @inode was found from
596138060baSChristian Brauner * @dentry: dentry to check permissions on
597e65ce2a5SChristian Brauner * @mode: the new mode of @inode
598e65ce2a5SChristian Brauner *
59913e83a49SChristian Brauner * If the dentry has been found through an idmapped mount the idmap of
60013e83a49SChristian Brauner * the vfsmount must be passed through @idmap. This function will then
60113e83a49SChristian Brauner * take care to map the inode according to @idmap before checking
602e65ce2a5SChristian Brauner * permissions. On non-idmapped mounts or if permission checking is to be
60313e83a49SChristian Brauner * performed on the raw inode simply passs @nop_mnt_idmap.
604e65ce2a5SChristian Brauner */
6055bf3258fSChristoph Hellwig int
posix_acl_chmod(struct mnt_idmap * idmap,struct dentry * dentry,umode_t mode)60613e83a49SChristian Brauner posix_acl_chmod(struct mnt_idmap *idmap, struct dentry *dentry,
607e65ce2a5SChristian Brauner umode_t mode)
6085bf3258fSChristoph Hellwig {
609138060baSChristian Brauner struct inode *inode = d_inode(dentry);
6105bf3258fSChristoph Hellwig struct posix_acl *acl;
6115bf3258fSChristoph Hellwig int ret = 0;
6125bf3258fSChristoph Hellwig
6135bf3258fSChristoph Hellwig if (!IS_POSIXACL(inode))
6145bf3258fSChristoph Hellwig return 0;
6155bf3258fSChristoph Hellwig if (!inode->i_op->set_acl)
6165bf3258fSChristoph Hellwig return -EOPNOTSUPP;
6175bf3258fSChristoph Hellwig
618cac2f8b8SChristian Brauner acl = get_inode_acl(inode, ACL_TYPE_ACCESS);
619789b663aSTrond Myklebust if (IS_ERR_OR_NULL(acl)) {
620789b663aSTrond Myklebust if (acl == ERR_PTR(-EOPNOTSUPP))
621789b663aSTrond Myklebust return 0;
6225bf3258fSChristoph Hellwig return PTR_ERR(acl);
623789b663aSTrond Myklebust }
6245bf3258fSChristoph Hellwig
62537bc1539SChristoph Hellwig ret = __posix_acl_chmod(&acl, GFP_KERNEL, mode);
6265bf3258fSChristoph Hellwig if (ret)
6275bf3258fSChristoph Hellwig return ret;
62813e83a49SChristian Brauner ret = inode->i_op->set_acl(idmap, dentry, acl, ACL_TYPE_ACCESS);
6295bf3258fSChristoph Hellwig posix_acl_release(acl);
6305bf3258fSChristoph Hellwig return ret;
6315bf3258fSChristoph Hellwig }
632bc26ab5fSAl Viro EXPORT_SYMBOL(posix_acl_chmod);
6335c8ebd57SChristoph Hellwig
63437bc1539SChristoph Hellwig int
posix_acl_create(struct inode * dir,umode_t * mode,struct posix_acl ** default_acl,struct posix_acl ** acl)63537bc1539SChristoph Hellwig posix_acl_create(struct inode *dir, umode_t *mode,
63637bc1539SChristoph Hellwig struct posix_acl **default_acl, struct posix_acl **acl)
63737bc1539SChristoph Hellwig {
63837bc1539SChristoph Hellwig struct posix_acl *p;
639c0c3a718SDan Carpenter struct posix_acl *clone;
64037bc1539SChristoph Hellwig int ret;
64137bc1539SChristoph Hellwig
642c0c3a718SDan Carpenter *acl = NULL;
643c0c3a718SDan Carpenter *default_acl = NULL;
644c0c3a718SDan Carpenter
64537bc1539SChristoph Hellwig if (S_ISLNK(*mode) || !IS_POSIXACL(dir))
646c0c3a718SDan Carpenter return 0;
64737bc1539SChristoph Hellwig
648cac2f8b8SChristian Brauner p = get_inode_acl(dir, ACL_TYPE_DEFAULT);
649c0c3a718SDan Carpenter if (!p || p == ERR_PTR(-EOPNOTSUPP)) {
650c0c3a718SDan Carpenter *mode &= ~current_umask();
651c0c3a718SDan Carpenter return 0;
65237bc1539SChristoph Hellwig }
653c0c3a718SDan Carpenter if (IS_ERR(p))
654c0c3a718SDan Carpenter return PTR_ERR(p);
65537bc1539SChristoph Hellwig
656beaf226bSMiklos Szeredi ret = -ENOMEM;
657c0c3a718SDan Carpenter clone = posix_acl_clone(p, GFP_NOFS);
658c0c3a718SDan Carpenter if (!clone)
659beaf226bSMiklos Szeredi goto err_release;
66037bc1539SChristoph Hellwig
661c0c3a718SDan Carpenter ret = posix_acl_create_masq(clone, mode);
662fed0b588SOmar Sandoval if (ret < 0)
663beaf226bSMiklos Szeredi goto err_release_clone;
66437bc1539SChristoph Hellwig
665c0c3a718SDan Carpenter if (ret == 0)
666c0c3a718SDan Carpenter posix_acl_release(clone);
667c0c3a718SDan Carpenter else
668c0c3a718SDan Carpenter *acl = clone;
66937bc1539SChristoph Hellwig
670c0c3a718SDan Carpenter if (!S_ISDIR(*mode))
67137bc1539SChristoph Hellwig posix_acl_release(p);
672c0c3a718SDan Carpenter else
67337bc1539SChristoph Hellwig *default_acl = p;
67437bc1539SChristoph Hellwig
67537bc1539SChristoph Hellwig return 0;
676fed0b588SOmar Sandoval
677beaf226bSMiklos Szeredi err_release_clone:
678c0c3a718SDan Carpenter posix_acl_release(clone);
679beaf226bSMiklos Szeredi err_release:
680fed0b588SOmar Sandoval posix_acl_release(p);
681beaf226bSMiklos Szeredi return ret;
68237bc1539SChristoph Hellwig }
68337bc1539SChristoph Hellwig EXPORT_SYMBOL_GPL(posix_acl_create);
68437bc1539SChristoph Hellwig
68507393101SJan Kara /**
68607393101SJan Kara * posix_acl_update_mode - update mode in set_acl
687700b7940SChristian Brauner * @idmap: idmap of the mount @inode was found from
688e39e773aSRandy Dunlap * @inode: target inode
689e39e773aSRandy Dunlap * @mode_p: mode (pointer) for update
690e39e773aSRandy Dunlap * @acl: acl pointer
69107393101SJan Kara *
69207393101SJan Kara * Update the file mode when setting an ACL: compute the new file permission
69307393101SJan Kara * bits based on the ACL. In addition, if the ACL is equivalent to the new
694e39e773aSRandy Dunlap * file mode, set *@acl to NULL to indicate that no ACL should be set.
69507393101SJan Kara *
696e39e773aSRandy Dunlap * As with chmod, clear the setgid bit if the caller is not in the owning group
69707393101SJan Kara * or capable of CAP_FSETID (see inode_change_ok).
69807393101SJan Kara *
699700b7940SChristian Brauner * If the inode has been found through an idmapped mount the idmap of
700700b7940SChristian Brauner * the vfsmount must be passed through @idmap. This function will then
701700b7940SChristian Brauner * take care to map the inode according to @idmap before checking
702e65ce2a5SChristian Brauner * permissions. On non-idmapped mounts or if permission checking is to be
703700b7940SChristian Brauner * performed on the raw inode simply passs @nop_mnt_idmap.
704e65ce2a5SChristian Brauner *
70507393101SJan Kara * Called from set_acl inode operations.
70607393101SJan Kara */
posix_acl_update_mode(struct mnt_idmap * idmap,struct inode * inode,umode_t * mode_p,struct posix_acl ** acl)707700b7940SChristian Brauner int posix_acl_update_mode(struct mnt_idmap *idmap,
708e65ce2a5SChristian Brauner struct inode *inode, umode_t *mode_p,
70907393101SJan Kara struct posix_acl **acl)
71007393101SJan Kara {
71107393101SJan Kara umode_t mode = inode->i_mode;
71207393101SJan Kara int error;
71307393101SJan Kara
71407393101SJan Kara error = posix_acl_equiv_mode(*acl, &mode);
71507393101SJan Kara if (error < 0)
71607393101SJan Kara return error;
71707393101SJan Kara if (error == 0)
71807393101SJan Kara *acl = NULL;
719e67fe633SChristian Brauner if (!vfsgid_in_group_p(i_gid_into_vfsgid(idmap, inode)) &&
7209452e93eSChristian Brauner !capable_wrt_inode_uidgid(idmap, inode, CAP_FSETID))
72107393101SJan Kara mode &= ~S_ISGID;
72207393101SJan Kara *mode_p = mode;
72307393101SJan Kara return 0;
72407393101SJan Kara }
72507393101SJan Kara EXPORT_SYMBOL(posix_acl_update_mode);
72607393101SJan Kara
7275c8ebd57SChristoph Hellwig /*
7285c8ebd57SChristoph Hellwig * Fix up the uids and gids in posix acl extended attributes in place.
7295c8ebd57SChristoph Hellwig */
posix_acl_fix_xattr_common(const void * value,size_t size)730985a6d0bSChristian Brauner static int posix_acl_fix_xattr_common(const void *value, size_t size)
7310c5fd887SChristian Brauner {
732985a6d0bSChristian Brauner const struct posix_acl_xattr_header *header = value;
7330c5fd887SChristian Brauner int count;
7340c5fd887SChristian Brauner
7350c5fd887SChristian Brauner if (!header)
7360c5fd887SChristian Brauner return -EINVAL;
7370c5fd887SChristian Brauner if (size < sizeof(struct posix_acl_xattr_header))
7380c5fd887SChristian Brauner return -EINVAL;
7390c5fd887SChristian Brauner if (header->a_version != cpu_to_le32(POSIX_ACL_XATTR_VERSION))
740985a6d0bSChristian Brauner return -EOPNOTSUPP;
7410c5fd887SChristian Brauner
7420c5fd887SChristian Brauner count = posix_acl_xattr_count(size);
7430c5fd887SChristian Brauner if (count < 0)
7440c5fd887SChristian Brauner return -EINVAL;
7450c5fd887SChristian Brauner if (count == 0)
746985a6d0bSChristian Brauner return 0;
7470c5fd887SChristian Brauner
7480c5fd887SChristian Brauner return count;
7490c5fd887SChristian Brauner }
7500c5fd887SChristian Brauner
7516b70fe06SChristian Brauner /**
7520a26bde2SChristian Brauner * posix_acl_from_xattr - convert POSIX ACLs from backing store to VFS format
7530a26bde2SChristian Brauner * @userns: the filesystem's idmapping
7546b70fe06SChristian Brauner * @value: the uapi representation of POSIX ACLs
7556b70fe06SChristian Brauner * @size: the size of @void
7566b70fe06SChristian Brauner *
7570a26bde2SChristian Brauner * Filesystems that store POSIX ACLs in the unaltered uapi format should use
7580a26bde2SChristian Brauner * posix_acl_from_xattr() when reading them from the backing store and
7590a26bde2SChristian Brauner * converting them into the struct posix_acl VFS format. The helper is
7600a26bde2SChristian Brauner * specifically intended to be called from the acl inode operation.
7610a26bde2SChristian Brauner *
7620a26bde2SChristian Brauner * The posix_acl_from_xattr() function will map the raw {g,u}id values stored
7630a26bde2SChristian Brauner * in ACL_{GROUP,USER} entries into idmapping in @userns.
7640a26bde2SChristian Brauner *
7650a26bde2SChristian Brauner * Note that posix_acl_from_xattr() does not take idmapped mounts into account.
7660a26bde2SChristian Brauner * If it did it calling it from the get acl inode operation would return POSIX
7670a26bde2SChristian Brauner * ACLs mapped according to an idmapped mount which would mean that the value
7680a26bde2SChristian Brauner * couldn't be cached for the filesystem. Idmapped mounts are taken into
7690a26bde2SChristian Brauner * account on the fly during permission checking or right at the VFS -
7700a26bde2SChristian Brauner * userspace boundary before reporting them to the user.
7716b70fe06SChristian Brauner *
7726b70fe06SChristian Brauner * Return: Allocated struct posix_acl on success, NULL for a valid header but
7736b70fe06SChristian Brauner * without actual POSIX ACL entries, or ERR_PTR() encoded error code.
7745c8ebd57SChristoph Hellwig */
posix_acl_from_xattr(struct user_namespace * userns,const void * value,size_t size)7750a26bde2SChristian Brauner struct posix_acl *posix_acl_from_xattr(struct user_namespace *userns,
7760a26bde2SChristian Brauner const void *value, size_t size)
7775c8ebd57SChristoph Hellwig {
7782211d5baSAndreas Gruenbacher const struct posix_acl_xattr_header *header = value;
7792211d5baSAndreas Gruenbacher const struct posix_acl_xattr_entry *entry = (const void *)(header + 1), *end;
7805c8ebd57SChristoph Hellwig int count;
7815c8ebd57SChristoph Hellwig struct posix_acl *acl;
7825c8ebd57SChristoph Hellwig struct posix_acl_entry *acl_e;
7835c8ebd57SChristoph Hellwig
784985a6d0bSChristian Brauner count = posix_acl_fix_xattr_common(value, size);
7855c8ebd57SChristoph Hellwig if (count < 0)
786985a6d0bSChristian Brauner return ERR_PTR(count);
7875c8ebd57SChristoph Hellwig if (count == 0)
7885c8ebd57SChristoph Hellwig return NULL;
7895c8ebd57SChristoph Hellwig
7905c8ebd57SChristoph Hellwig acl = posix_acl_alloc(count, GFP_NOFS);
7915c8ebd57SChristoph Hellwig if (!acl)
7925c8ebd57SChristoph Hellwig return ERR_PTR(-ENOMEM);
7935c8ebd57SChristoph Hellwig acl_e = acl->a_entries;
7945c8ebd57SChristoph Hellwig
7955c8ebd57SChristoph Hellwig for (end = entry + count; entry != end; acl_e++, entry++) {
7965c8ebd57SChristoph Hellwig acl_e->e_tag = le16_to_cpu(entry->e_tag);
7975c8ebd57SChristoph Hellwig acl_e->e_perm = le16_to_cpu(entry->e_perm);
7985c8ebd57SChristoph Hellwig
7995c8ebd57SChristoph Hellwig switch(acl_e->e_tag) {
8005c8ebd57SChristoph Hellwig case ACL_USER_OBJ:
8015c8ebd57SChristoph Hellwig case ACL_GROUP_OBJ:
8025c8ebd57SChristoph Hellwig case ACL_MASK:
8035c8ebd57SChristoph Hellwig case ACL_OTHER:
8045c8ebd57SChristoph Hellwig break;
8055c8ebd57SChristoph Hellwig
8065c8ebd57SChristoph Hellwig case ACL_USER:
8070a26bde2SChristian Brauner acl_e->e_uid = make_kuid(userns,
8080a26bde2SChristian Brauner le32_to_cpu(entry->e_id));
8095c8ebd57SChristoph Hellwig if (!uid_valid(acl_e->e_uid))
8105c8ebd57SChristoph Hellwig goto fail;
8115c8ebd57SChristoph Hellwig break;
8125c8ebd57SChristoph Hellwig case ACL_GROUP:
8130a26bde2SChristian Brauner acl_e->e_gid = make_kgid(userns,
8140a26bde2SChristian Brauner le32_to_cpu(entry->e_id));
8155c8ebd57SChristoph Hellwig if (!gid_valid(acl_e->e_gid))
8165c8ebd57SChristoph Hellwig goto fail;
8175c8ebd57SChristoph Hellwig break;
8185c8ebd57SChristoph Hellwig
8195c8ebd57SChristoph Hellwig default:
8205c8ebd57SChristoph Hellwig goto fail;
8215c8ebd57SChristoph Hellwig }
8225c8ebd57SChristoph Hellwig }
8235c8ebd57SChristoph Hellwig return acl;
8245c8ebd57SChristoph Hellwig
8255c8ebd57SChristoph Hellwig fail:
8265c8ebd57SChristoph Hellwig posix_acl_release(acl);
8275c8ebd57SChristoph Hellwig return ERR_PTR(-EINVAL);
8285c8ebd57SChristoph Hellwig }
8295c8ebd57SChristoph Hellwig EXPORT_SYMBOL (posix_acl_from_xattr);
8305c8ebd57SChristoph Hellwig
8315c8ebd57SChristoph Hellwig /*
8325c8ebd57SChristoph Hellwig * Convert from in-memory to extended attribute representation.
8335c8ebd57SChristoph Hellwig */
8345c8ebd57SChristoph Hellwig int
posix_acl_to_xattr(struct user_namespace * user_ns,const struct posix_acl * acl,void * buffer,size_t size)8355c8ebd57SChristoph Hellwig posix_acl_to_xattr(struct user_namespace *user_ns, const struct posix_acl *acl,
8365c8ebd57SChristoph Hellwig void *buffer, size_t size)
8375c8ebd57SChristoph Hellwig {
8382211d5baSAndreas Gruenbacher struct posix_acl_xattr_header *ext_acl = buffer;
8392211d5baSAndreas Gruenbacher struct posix_acl_xattr_entry *ext_entry;
8405c8ebd57SChristoph Hellwig int real_size, n;
8415c8ebd57SChristoph Hellwig
8425c8ebd57SChristoph Hellwig real_size = posix_acl_xattr_size(acl->a_count);
8435c8ebd57SChristoph Hellwig if (!buffer)
8445c8ebd57SChristoph Hellwig return real_size;
8455c8ebd57SChristoph Hellwig if (real_size > size)
8465c8ebd57SChristoph Hellwig return -ERANGE;
8475c8ebd57SChristoph Hellwig
8482211d5baSAndreas Gruenbacher ext_entry = (void *)(ext_acl + 1);
8495c8ebd57SChristoph Hellwig ext_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
8505c8ebd57SChristoph Hellwig
8515c8ebd57SChristoph Hellwig for (n=0; n < acl->a_count; n++, ext_entry++) {
8525c8ebd57SChristoph Hellwig const struct posix_acl_entry *acl_e = &acl->a_entries[n];
8535c8ebd57SChristoph Hellwig ext_entry->e_tag = cpu_to_le16(acl_e->e_tag);
8545c8ebd57SChristoph Hellwig ext_entry->e_perm = cpu_to_le16(acl_e->e_perm);
8555c8ebd57SChristoph Hellwig switch(acl_e->e_tag) {
8565c8ebd57SChristoph Hellwig case ACL_USER:
8575c8ebd57SChristoph Hellwig ext_entry->e_id =
8585c8ebd57SChristoph Hellwig cpu_to_le32(from_kuid(user_ns, acl_e->e_uid));
8595c8ebd57SChristoph Hellwig break;
8605c8ebd57SChristoph Hellwig case ACL_GROUP:
8615c8ebd57SChristoph Hellwig ext_entry->e_id =
8625c8ebd57SChristoph Hellwig cpu_to_le32(from_kgid(user_ns, acl_e->e_gid));
8635c8ebd57SChristoph Hellwig break;
8645c8ebd57SChristoph Hellwig default:
8655c8ebd57SChristoph Hellwig ext_entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID);
8665c8ebd57SChristoph Hellwig break;
8675c8ebd57SChristoph Hellwig }
8685c8ebd57SChristoph Hellwig }
8695c8ebd57SChristoph Hellwig return real_size;
8705c8ebd57SChristoph Hellwig }
8715c8ebd57SChristoph Hellwig EXPORT_SYMBOL (posix_acl_to_xattr);
8722aeccbe9SChristoph Hellwig
8734f353ba4SChristian Brauner /**
8744f353ba4SChristian Brauner * vfs_posix_acl_to_xattr - convert from kernel to userspace representation
8755a6f52d2SChristian Brauner * @idmap: idmap of the mount
8764f353ba4SChristian Brauner * @inode: inode the posix acls are set on
8774f353ba4SChristian Brauner * @acl: the posix acls as represented by the vfs
8784f353ba4SChristian Brauner * @buffer: the buffer into which to convert @acl
8794f353ba4SChristian Brauner * @size: size of @buffer
8804f353ba4SChristian Brauner *
8814f353ba4SChristian Brauner * This converts @acl from the VFS representation in the filesystem idmapping
8824f353ba4SChristian Brauner * to the uapi form reportable to userspace. And mount and caller idmappings
8834f353ba4SChristian Brauner * are handled appropriately.
8844f353ba4SChristian Brauner *
8854f353ba4SChristian Brauner * Return: On success, the size of the stored uapi posix acls, on error a
8864f353ba4SChristian Brauner * negative errno.
8874f353ba4SChristian Brauner */
vfs_posix_acl_to_xattr(struct mnt_idmap * idmap,struct inode * inode,const struct posix_acl * acl,void * buffer,size_t size)8885a6f52d2SChristian Brauner static ssize_t vfs_posix_acl_to_xattr(struct mnt_idmap *idmap,
889a351b1f4SChristian Brauner struct inode *inode,
890a351b1f4SChristian Brauner const struct posix_acl *acl, void *buffer,
891a351b1f4SChristian Brauner size_t size)
8924f353ba4SChristian Brauner
8934f353ba4SChristian Brauner {
8944f353ba4SChristian Brauner struct posix_acl_xattr_header *ext_acl = buffer;
8954f353ba4SChristian Brauner struct posix_acl_xattr_entry *ext_entry;
8964f353ba4SChristian Brauner struct user_namespace *fs_userns, *caller_userns;
8974f353ba4SChristian Brauner ssize_t real_size, n;
8984f353ba4SChristian Brauner vfsuid_t vfsuid;
8994f353ba4SChristian Brauner vfsgid_t vfsgid;
9004f353ba4SChristian Brauner
9014f353ba4SChristian Brauner real_size = posix_acl_xattr_size(acl->a_count);
9024f353ba4SChristian Brauner if (!buffer)
9034f353ba4SChristian Brauner return real_size;
9044f353ba4SChristian Brauner if (real_size > size)
9054f353ba4SChristian Brauner return -ERANGE;
9064f353ba4SChristian Brauner
9074f353ba4SChristian Brauner ext_entry = (void *)(ext_acl + 1);
9084f353ba4SChristian Brauner ext_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
9094f353ba4SChristian Brauner
9104f353ba4SChristian Brauner fs_userns = i_user_ns(inode);
9114f353ba4SChristian Brauner caller_userns = current_user_ns();
9124f353ba4SChristian Brauner for (n=0; n < acl->a_count; n++, ext_entry++) {
9134f353ba4SChristian Brauner const struct posix_acl_entry *acl_e = &acl->a_entries[n];
9144f353ba4SChristian Brauner ext_entry->e_tag = cpu_to_le16(acl_e->e_tag);
9154f353ba4SChristian Brauner ext_entry->e_perm = cpu_to_le16(acl_e->e_perm);
9164f353ba4SChristian Brauner switch(acl_e->e_tag) {
9174f353ba4SChristian Brauner case ACL_USER:
9184d7ca409SChristian Brauner vfsuid = make_vfsuid(idmap, fs_userns, acl_e->e_uid);
9194f353ba4SChristian Brauner ext_entry->e_id = cpu_to_le32(from_kuid(
9204f353ba4SChristian Brauner caller_userns, vfsuid_into_kuid(vfsuid)));
9214f353ba4SChristian Brauner break;
9224f353ba4SChristian Brauner case ACL_GROUP:
9234d7ca409SChristian Brauner vfsgid = make_vfsgid(idmap, fs_userns, acl_e->e_gid);
9244f353ba4SChristian Brauner ext_entry->e_id = cpu_to_le32(from_kgid(
9254f353ba4SChristian Brauner caller_userns, vfsgid_into_kgid(vfsgid)));
9264f353ba4SChristian Brauner break;
9274f353ba4SChristian Brauner default:
9284f353ba4SChristian Brauner ext_entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID);
9294f353ba4SChristian Brauner break;
9304f353ba4SChristian Brauner }
9314f353ba4SChristian Brauner }
9324f353ba4SChristian Brauner return real_size;
9334f353ba4SChristian Brauner }
9344f353ba4SChristian Brauner
935485e71e8SAndreas Gruenbacher int
set_posix_acl(struct mnt_idmap * idmap,struct dentry * dentry,int type,struct posix_acl * acl)93613e83a49SChristian Brauner set_posix_acl(struct mnt_idmap *idmap, struct dentry *dentry,
937e65ce2a5SChristian Brauner int type, struct posix_acl *acl)
938485e71e8SAndreas Gruenbacher {
939138060baSChristian Brauner struct inode *inode = d_inode(dentry);
940138060baSChristian Brauner
941485e71e8SAndreas Gruenbacher if (!IS_POSIXACL(inode))
942485e71e8SAndreas Gruenbacher return -EOPNOTSUPP;
943485e71e8SAndreas Gruenbacher if (!inode->i_op->set_acl)
944485e71e8SAndreas Gruenbacher return -EOPNOTSUPP;
945485e71e8SAndreas Gruenbacher
946485e71e8SAndreas Gruenbacher if (type == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode))
947485e71e8SAndreas Gruenbacher return acl ? -EACCES : 0;
94801beba79SChristian Brauner if (!inode_owner_or_capable(idmap, inode))
949485e71e8SAndreas Gruenbacher return -EPERM;
950485e71e8SAndreas Gruenbacher
951485e71e8SAndreas Gruenbacher if (acl) {
952a867d734SLinus Torvalds int ret = posix_acl_valid(inode->i_sb->s_user_ns, acl);
953485e71e8SAndreas Gruenbacher if (ret)
954485e71e8SAndreas Gruenbacher return ret;
955485e71e8SAndreas Gruenbacher }
95613e83a49SChristian Brauner return inode->i_op->set_acl(idmap, dentry, acl, type);
957485e71e8SAndreas Gruenbacher }
958485e71e8SAndreas Gruenbacher EXPORT_SYMBOL(set_posix_acl);
959485e71e8SAndreas Gruenbacher
posix_acl_listxattr(struct inode * inode,char ** buffer,ssize_t * remaining_size)960f2620f16SChristian Brauner int posix_acl_listxattr(struct inode *inode, char **buffer,
961f2620f16SChristian Brauner ssize_t *remaining_size)
962f2620f16SChristian Brauner {
963f2620f16SChristian Brauner int err;
964f2620f16SChristian Brauner
965f2620f16SChristian Brauner if (!IS_POSIXACL(inode))
966f2620f16SChristian Brauner return 0;
967f2620f16SChristian Brauner
968f2620f16SChristian Brauner if (inode->i_acl) {
969f2620f16SChristian Brauner err = xattr_list_one(buffer, remaining_size,
970f2620f16SChristian Brauner XATTR_NAME_POSIX_ACL_ACCESS);
971f2620f16SChristian Brauner if (err)
972f2620f16SChristian Brauner return err;
973f2620f16SChristian Brauner }
974f2620f16SChristian Brauner
975f2620f16SChristian Brauner if (inode->i_default_acl) {
976f2620f16SChristian Brauner err = xattr_list_one(buffer, remaining_size,
977f2620f16SChristian Brauner XATTR_NAME_POSIX_ACL_DEFAULT);
978f2620f16SChristian Brauner if (err)
979f2620f16SChristian Brauner return err;
980f2620f16SChristian Brauner }
981f2620f16SChristian Brauner
982f2620f16SChristian Brauner return 0;
983f2620f16SChristian Brauner }
984f2620f16SChristian Brauner
985764a5c6bSAndreas Gruenbacher static bool
posix_acl_xattr_list(struct dentry * dentry)986764a5c6bSAndreas Gruenbacher posix_acl_xattr_list(struct dentry *dentry)
9872aeccbe9SChristoph Hellwig {
988764a5c6bSAndreas Gruenbacher return IS_POSIXACL(d_backing_inode(dentry));
9892aeccbe9SChristoph Hellwig }
9902aeccbe9SChristoph Hellwig
991d549b741SChristian Brauner /*
992d549b741SChristian Brauner * nop_posix_acl_access - legacy xattr handler for access POSIX ACLs
993d549b741SChristian Brauner *
994d549b741SChristian Brauner * This is the legacy POSIX ACL access xattr handler. It is used by some
995d549b741SChristian Brauner * filesystems to implement their ->listxattr() inode operation. New code
996d549b741SChristian Brauner * should never use them.
997d549b741SChristian Brauner */
998d549b741SChristian Brauner const struct xattr_handler nop_posix_acl_access = {
99998e9cb57SAndreas Gruenbacher .name = XATTR_NAME_POSIX_ACL_ACCESS,
10002aeccbe9SChristoph Hellwig .list = posix_acl_xattr_list,
10012aeccbe9SChristoph Hellwig };
1002d549b741SChristian Brauner EXPORT_SYMBOL_GPL(nop_posix_acl_access);
10032aeccbe9SChristoph Hellwig
1004d549b741SChristian Brauner /*
1005d549b741SChristian Brauner * nop_posix_acl_default - legacy xattr handler for default POSIX ACLs
1006d549b741SChristian Brauner *
1007d549b741SChristian Brauner * This is the legacy POSIX ACL default xattr handler. It is used by some
1008d549b741SChristian Brauner * filesystems to implement their ->listxattr() inode operation. New code
1009d549b741SChristian Brauner * should never use them.
1010d549b741SChristian Brauner */
1011d549b741SChristian Brauner const struct xattr_handler nop_posix_acl_default = {
101298e9cb57SAndreas Gruenbacher .name = XATTR_NAME_POSIX_ACL_DEFAULT,
10132aeccbe9SChristoph Hellwig .list = posix_acl_xattr_list,
10142aeccbe9SChristoph Hellwig };
1015d549b741SChristian Brauner EXPORT_SYMBOL_GPL(nop_posix_acl_default);
1016feda821eSChristoph Hellwig
simple_set_acl(struct mnt_idmap * idmap,struct dentry * dentry,struct posix_acl * acl,int type)101713e83a49SChristian Brauner int simple_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
1018549c7297SChristian Brauner struct posix_acl *acl, int type)
1019feda821eSChristoph Hellwig {
1020feda821eSChristoph Hellwig int error;
1021138060baSChristian Brauner struct inode *inode = d_inode(dentry);
1022feda821eSChristoph Hellwig
1023feda821eSChristoph Hellwig if (type == ACL_TYPE_ACCESS) {
1024700b7940SChristian Brauner error = posix_acl_update_mode(idmap, inode,
1025497de07dSGu Zheng &inode->i_mode, &acl);
1026497de07dSGu Zheng if (error)
1027497de07dSGu Zheng return error;
1028feda821eSChristoph Hellwig }
1029feda821eSChristoph Hellwig
1030*2276e5baSJeff Layton inode_set_ctime_current(inode);
103136f05cabSJeff Layton if (IS_I_VERSION(inode))
103236f05cabSJeff Layton inode_inc_iversion(inode);
1033feda821eSChristoph Hellwig set_cached_acl(inode, type, acl);
1034feda821eSChristoph Hellwig return 0;
1035feda821eSChristoph Hellwig }
1036feda821eSChristoph Hellwig
simple_acl_create(struct inode * dir,struct inode * inode)1037feda821eSChristoph Hellwig int simple_acl_create(struct inode *dir, struct inode *inode)
1038feda821eSChristoph Hellwig {
1039feda821eSChristoph Hellwig struct posix_acl *default_acl, *acl;
1040feda821eSChristoph Hellwig int error;
1041feda821eSChristoph Hellwig
1042feda821eSChristoph Hellwig error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
1043feda821eSChristoph Hellwig if (error)
1044feda821eSChristoph Hellwig return error;
1045feda821eSChristoph Hellwig
1046feda821eSChristoph Hellwig set_cached_acl(inode, ACL_TYPE_DEFAULT, default_acl);
1047feda821eSChristoph Hellwig set_cached_acl(inode, ACL_TYPE_ACCESS, acl);
1048feda821eSChristoph Hellwig
1049feda821eSChristoph Hellwig if (default_acl)
1050feda821eSChristoph Hellwig posix_acl_release(default_acl);
1051feda821eSChristoph Hellwig if (acl)
1052feda821eSChristoph Hellwig posix_acl_release(acl);
1053feda821eSChristoph Hellwig return 0;
1054feda821eSChristoph Hellwig }
1055e4cc9163SChristian Brauner
vfs_set_acl_idmapped_mnt(struct mnt_idmap * idmap,struct user_namespace * fs_userns,struct posix_acl * acl)1056700b7940SChristian Brauner static int vfs_set_acl_idmapped_mnt(struct mnt_idmap *idmap,
1057e4cc9163SChristian Brauner struct user_namespace *fs_userns,
1058e4cc9163SChristian Brauner struct posix_acl *acl)
1059e4cc9163SChristian Brauner {
1060e4cc9163SChristian Brauner for (int n = 0; n < acl->a_count; n++) {
1061e4cc9163SChristian Brauner struct posix_acl_entry *acl_e = &acl->a_entries[n];
1062e4cc9163SChristian Brauner
1063e4cc9163SChristian Brauner switch (acl_e->e_tag) {
1064e4cc9163SChristian Brauner case ACL_USER:
10654d7ca409SChristian Brauner acl_e->e_uid = from_vfsuid(idmap, fs_userns,
1066e4cc9163SChristian Brauner VFSUIDT_INIT(acl_e->e_uid));
1067e4cc9163SChristian Brauner break;
1068e4cc9163SChristian Brauner case ACL_GROUP:
10694d7ca409SChristian Brauner acl_e->e_gid = from_vfsgid(idmap, fs_userns,
1070e4cc9163SChristian Brauner VFSGIDT_INIT(acl_e->e_gid));
1071e4cc9163SChristian Brauner break;
1072e4cc9163SChristian Brauner }
1073e4cc9163SChristian Brauner }
1074e4cc9163SChristian Brauner
1075e4cc9163SChristian Brauner return 0;
1076e4cc9163SChristian Brauner }
1077e4cc9163SChristian Brauner
1078e4cc9163SChristian Brauner /**
1079e4cc9163SChristian Brauner * vfs_set_acl - set posix acls
108013e83a49SChristian Brauner * @idmap: idmap of the mount
1081e4cc9163SChristian Brauner * @dentry: the dentry based on which to set the posix acls
1082e4cc9163SChristian Brauner * @acl_name: the name of the posix acl
1083e4cc9163SChristian Brauner * @kacl: the posix acls in the appropriate VFS format
1084e4cc9163SChristian Brauner *
1085e4cc9163SChristian Brauner * This function sets @kacl. The caller must all posix_acl_release() on @kacl
1086e4cc9163SChristian Brauner * afterwards.
1087e4cc9163SChristian Brauner *
1088e4cc9163SChristian Brauner * Return: On success 0, on error negative errno.
1089e4cc9163SChristian Brauner */
vfs_set_acl(struct mnt_idmap * idmap,struct dentry * dentry,const char * acl_name,struct posix_acl * kacl)109013e83a49SChristian Brauner int vfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
1091e4cc9163SChristian Brauner const char *acl_name, struct posix_acl *kacl)
1092e4cc9163SChristian Brauner {
1093e4cc9163SChristian Brauner int acl_type;
1094e4cc9163SChristian Brauner int error;
1095e4cc9163SChristian Brauner struct inode *inode = d_inode(dentry);
1096e4cc9163SChristian Brauner struct inode *delegated_inode = NULL;
1097e4cc9163SChristian Brauner
1098e4cc9163SChristian Brauner acl_type = posix_acl_type(acl_name);
1099e4cc9163SChristian Brauner if (acl_type < 0)
1100e4cc9163SChristian Brauner return -EINVAL;
1101e4cc9163SChristian Brauner
1102e4cc9163SChristian Brauner if (kacl) {
1103e4cc9163SChristian Brauner /*
1104e4cc9163SChristian Brauner * If we're on an idmapped mount translate from mount specific
1105e4cc9163SChristian Brauner * vfs{g,u}id_t into global filesystem k{g,u}id_t.
1106e4cc9163SChristian Brauner * Afterwards we can cache the POSIX ACLs filesystem wide and -
1107e4cc9163SChristian Brauner * if this is a filesystem with a backing store - ultimately
1108e4cc9163SChristian Brauner * translate them to backing store values.
1109e4cc9163SChristian Brauner */
1110700b7940SChristian Brauner error = vfs_set_acl_idmapped_mnt(idmap, i_user_ns(inode), kacl);
1111e4cc9163SChristian Brauner if (error)
1112e4cc9163SChristian Brauner return error;
1113e4cc9163SChristian Brauner }
1114e4cc9163SChristian Brauner
1115e4cc9163SChristian Brauner retry_deleg:
1116e4cc9163SChristian Brauner inode_lock(inode);
1117e4cc9163SChristian Brauner
1118e4cc9163SChristian Brauner /*
1119e4cc9163SChristian Brauner * We only care about restrictions the inode struct itself places upon
1120e4cc9163SChristian Brauner * us otherwise POSIX ACLs aren't subject to any VFS restrictions.
1121e4cc9163SChristian Brauner */
11224609e1f1SChristian Brauner error = may_write_xattr(idmap, inode);
1123e4cc9163SChristian Brauner if (error)
1124e4cc9163SChristian Brauner goto out_inode_unlock;
1125e4cc9163SChristian Brauner
1126700b7940SChristian Brauner error = security_inode_set_acl(idmap, dentry, acl_name, kacl);
1127e4cc9163SChristian Brauner if (error)
1128e4cc9163SChristian Brauner goto out_inode_unlock;
1129e4cc9163SChristian Brauner
1130e4cc9163SChristian Brauner error = try_break_deleg(inode, &delegated_inode);
1131e4cc9163SChristian Brauner if (error)
1132e4cc9163SChristian Brauner goto out_inode_unlock;
1133e4cc9163SChristian Brauner
1134e499214cSChristian Brauner if (likely(!is_bad_inode(inode)))
113513e83a49SChristian Brauner error = set_posix_acl(idmap, dentry, acl_type, kacl);
1136e4cc9163SChristian Brauner else
1137e499214cSChristian Brauner error = -EIO;
1138e4cc9163SChristian Brauner if (!error) {
1139e4cc9163SChristian Brauner fsnotify_xattr(dentry);
1140e4cc9163SChristian Brauner evm_inode_post_set_acl(dentry, acl_name, kacl);
1141e4cc9163SChristian Brauner }
1142e4cc9163SChristian Brauner
1143e4cc9163SChristian Brauner out_inode_unlock:
1144e4cc9163SChristian Brauner inode_unlock(inode);
1145e4cc9163SChristian Brauner
1146e4cc9163SChristian Brauner if (delegated_inode) {
1147e4cc9163SChristian Brauner error = break_deleg_wait(&delegated_inode);
1148e4cc9163SChristian Brauner if (!error)
1149e4cc9163SChristian Brauner goto retry_deleg;
1150e4cc9163SChristian Brauner }
1151e4cc9163SChristian Brauner
1152e4cc9163SChristian Brauner return error;
1153e4cc9163SChristian Brauner }
1154e4cc9163SChristian Brauner EXPORT_SYMBOL_GPL(vfs_set_acl);
11554f353ba4SChristian Brauner
11564f353ba4SChristian Brauner /**
11574f353ba4SChristian Brauner * vfs_get_acl - get posix acls
115877435322SChristian Brauner * @idmap: idmap of the mount
11594f353ba4SChristian Brauner * @dentry: the dentry based on which to retrieve the posix acls
11604f353ba4SChristian Brauner * @acl_name: the name of the posix acl
11614f353ba4SChristian Brauner *
11624f353ba4SChristian Brauner * This function retrieves @kacl from the filesystem. The caller must all
11634f353ba4SChristian Brauner * posix_acl_release() on @kacl.
11644f353ba4SChristian Brauner *
11654f353ba4SChristian Brauner * Return: On success POSIX ACLs in VFS format, on error negative errno.
11664f353ba4SChristian Brauner */
vfs_get_acl(struct mnt_idmap * idmap,struct dentry * dentry,const char * acl_name)116777435322SChristian Brauner struct posix_acl *vfs_get_acl(struct mnt_idmap *idmap,
11684f353ba4SChristian Brauner struct dentry *dentry, const char *acl_name)
11694f353ba4SChristian Brauner {
11704f353ba4SChristian Brauner struct inode *inode = d_inode(dentry);
11714f353ba4SChristian Brauner struct posix_acl *acl;
11724f353ba4SChristian Brauner int acl_type, error;
11734f353ba4SChristian Brauner
11744f353ba4SChristian Brauner acl_type = posix_acl_type(acl_name);
11754f353ba4SChristian Brauner if (acl_type < 0)
11764f353ba4SChristian Brauner return ERR_PTR(-EINVAL);
11774f353ba4SChristian Brauner
11784f353ba4SChristian Brauner /*
11794f353ba4SChristian Brauner * The VFS has no restrictions on reading POSIX ACLs so calling
11804f353ba4SChristian Brauner * something like xattr_permission() isn't needed. Only LSMs get a say.
11814f353ba4SChristian Brauner */
1182700b7940SChristian Brauner error = security_inode_get_acl(idmap, dentry, acl_name);
11834f353ba4SChristian Brauner if (error)
11844f353ba4SChristian Brauner return ERR_PTR(error);
11854f353ba4SChristian Brauner
11864f353ba4SChristian Brauner if (!IS_POSIXACL(inode))
11874f353ba4SChristian Brauner return ERR_PTR(-EOPNOTSUPP);
11884f353ba4SChristian Brauner if (S_ISLNK(inode->i_mode))
11894f353ba4SChristian Brauner return ERR_PTR(-EOPNOTSUPP);
11904f353ba4SChristian Brauner
119177435322SChristian Brauner acl = __get_acl(idmap, dentry, inode, acl_type);
11924f353ba4SChristian Brauner if (IS_ERR(acl))
11934f353ba4SChristian Brauner return acl;
11944f353ba4SChristian Brauner if (!acl)
11954f353ba4SChristian Brauner return ERR_PTR(-ENODATA);
11964f353ba4SChristian Brauner
11974f353ba4SChristian Brauner return acl;
11984f353ba4SChristian Brauner }
11994f353ba4SChristian Brauner EXPORT_SYMBOL_GPL(vfs_get_acl);
1200aeb7f005SChristian Brauner
1201aeb7f005SChristian Brauner /**
1202aeb7f005SChristian Brauner * vfs_remove_acl - remove posix acls
120313e83a49SChristian Brauner * @idmap: idmap of the mount
1204aeb7f005SChristian Brauner * @dentry: the dentry based on which to retrieve the posix acls
1205aeb7f005SChristian Brauner * @acl_name: the name of the posix acl
1206aeb7f005SChristian Brauner *
1207aeb7f005SChristian Brauner * This function removes posix acls.
1208aeb7f005SChristian Brauner *
1209aeb7f005SChristian Brauner * Return: On success 0, on error negative errno.
1210aeb7f005SChristian Brauner */
vfs_remove_acl(struct mnt_idmap * idmap,struct dentry * dentry,const char * acl_name)121113e83a49SChristian Brauner int vfs_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
1212aeb7f005SChristian Brauner const char *acl_name)
1213aeb7f005SChristian Brauner {
1214aeb7f005SChristian Brauner int acl_type;
1215aeb7f005SChristian Brauner int error;
1216aeb7f005SChristian Brauner struct inode *inode = d_inode(dentry);
1217aeb7f005SChristian Brauner struct inode *delegated_inode = NULL;
1218aeb7f005SChristian Brauner
1219aeb7f005SChristian Brauner acl_type = posix_acl_type(acl_name);
1220aeb7f005SChristian Brauner if (acl_type < 0)
1221aeb7f005SChristian Brauner return -EINVAL;
1222aeb7f005SChristian Brauner
1223aeb7f005SChristian Brauner retry_deleg:
1224aeb7f005SChristian Brauner inode_lock(inode);
1225aeb7f005SChristian Brauner
1226aeb7f005SChristian Brauner /*
1227aeb7f005SChristian Brauner * We only care about restrictions the inode struct itself places upon
1228aeb7f005SChristian Brauner * us otherwise POSIX ACLs aren't subject to any VFS restrictions.
1229aeb7f005SChristian Brauner */
12304609e1f1SChristian Brauner error = may_write_xattr(idmap, inode);
1231aeb7f005SChristian Brauner if (error)
1232aeb7f005SChristian Brauner goto out_inode_unlock;
1233aeb7f005SChristian Brauner
1234700b7940SChristian Brauner error = security_inode_remove_acl(idmap, dentry, acl_name);
1235aeb7f005SChristian Brauner if (error)
1236aeb7f005SChristian Brauner goto out_inode_unlock;
1237aeb7f005SChristian Brauner
1238aeb7f005SChristian Brauner error = try_break_deleg(inode, &delegated_inode);
1239aeb7f005SChristian Brauner if (error)
1240aeb7f005SChristian Brauner goto out_inode_unlock;
1241aeb7f005SChristian Brauner
1242e499214cSChristian Brauner if (likely(!is_bad_inode(inode)))
124313e83a49SChristian Brauner error = set_posix_acl(idmap, dentry, acl_type, NULL);
1244aeb7f005SChristian Brauner else
1245e499214cSChristian Brauner error = -EIO;
1246aeb7f005SChristian Brauner if (!error) {
1247aeb7f005SChristian Brauner fsnotify_xattr(dentry);
1248700b7940SChristian Brauner evm_inode_post_remove_acl(idmap, dentry, acl_name);
1249aeb7f005SChristian Brauner }
1250aeb7f005SChristian Brauner
1251aeb7f005SChristian Brauner out_inode_unlock:
1252aeb7f005SChristian Brauner inode_unlock(inode);
1253aeb7f005SChristian Brauner
1254aeb7f005SChristian Brauner if (delegated_inode) {
1255aeb7f005SChristian Brauner error = break_deleg_wait(&delegated_inode);
1256aeb7f005SChristian Brauner if (!error)
1257aeb7f005SChristian Brauner goto retry_deleg;
1258aeb7f005SChristian Brauner }
1259aeb7f005SChristian Brauner
1260aeb7f005SChristian Brauner return error;
1261aeb7f005SChristian Brauner }
1262aeb7f005SChristian Brauner EXPORT_SYMBOL_GPL(vfs_remove_acl);
1263318e6685SChristian Brauner
do_set_acl(struct mnt_idmap * idmap,struct dentry * dentry,const char * acl_name,const void * kvalue,size_t size)12645a6f52d2SChristian Brauner int do_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
1265318e6685SChristian Brauner const char *acl_name, const void *kvalue, size_t size)
1266318e6685SChristian Brauner {
1267318e6685SChristian Brauner int error;
1268318e6685SChristian Brauner struct posix_acl *acl = NULL;
1269318e6685SChristian Brauner
1270318e6685SChristian Brauner if (size) {
1271318e6685SChristian Brauner /*
1272318e6685SChristian Brauner * Note that posix_acl_from_xattr() uses GFP_NOFS when it
1273318e6685SChristian Brauner * probably doesn't need to here.
1274318e6685SChristian Brauner */
1275318e6685SChristian Brauner acl = posix_acl_from_xattr(current_user_ns(), kvalue, size);
1276318e6685SChristian Brauner if (IS_ERR(acl))
1277318e6685SChristian Brauner return PTR_ERR(acl);
1278318e6685SChristian Brauner }
1279318e6685SChristian Brauner
128013e83a49SChristian Brauner error = vfs_set_acl(idmap, dentry, acl_name, acl);
1281318e6685SChristian Brauner posix_acl_release(acl);
1282318e6685SChristian Brauner return error;
1283318e6685SChristian Brauner }
1284318e6685SChristian Brauner
do_get_acl(struct mnt_idmap * idmap,struct dentry * dentry,const char * acl_name,void * kvalue,size_t size)12855a6f52d2SChristian Brauner ssize_t do_get_acl(struct mnt_idmap *idmap, struct dentry *dentry,
1286318e6685SChristian Brauner const char *acl_name, void *kvalue, size_t size)
1287318e6685SChristian Brauner {
1288318e6685SChristian Brauner ssize_t error;
1289318e6685SChristian Brauner struct posix_acl *acl;
1290318e6685SChristian Brauner
129177435322SChristian Brauner acl = vfs_get_acl(idmap, dentry, acl_name);
1292318e6685SChristian Brauner if (IS_ERR(acl))
1293318e6685SChristian Brauner return PTR_ERR(acl);
1294318e6685SChristian Brauner
12955a6f52d2SChristian Brauner error = vfs_posix_acl_to_xattr(idmap, d_inode(dentry),
1296318e6685SChristian Brauner acl, kvalue, size);
1297318e6685SChristian Brauner posix_acl_release(acl);
1298318e6685SChristian Brauner return error;
1299318e6685SChristian Brauner }
1300