xref: /openbmc/linux/fs/posix_acl.c (revision 2276e5ba)
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