18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2002,2003 by Andreas Gruenbacher <a.gruenbacher@computer.org> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Fixes from William Schumacher incorporated on 15 March 2001. 68c2ecf20Sopenharmony_ci * (Reported by Charles Bertsch, <CBertsch@microtest.com>). 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci/* 108c2ecf20Sopenharmony_ci * This file contains generic functions for manipulating 118c2ecf20Sopenharmony_ci * POSIX 1003.1e draft standard 17 ACLs. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/atomic.h> 178c2ecf20Sopenharmony_ci#include <linux/fs.h> 188c2ecf20Sopenharmony_ci#include <linux/sched.h> 198c2ecf20Sopenharmony_ci#include <linux/cred.h> 208c2ecf20Sopenharmony_ci#include <linux/posix_acl.h> 218c2ecf20Sopenharmony_ci#include <linux/posix_acl_xattr.h> 228c2ecf20Sopenharmony_ci#include <linux/xattr.h> 238c2ecf20Sopenharmony_ci#include <linux/export.h> 248c2ecf20Sopenharmony_ci#include <linux/user_namespace.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic struct posix_acl **acl_by_type(struct inode *inode, int type) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci switch (type) { 298c2ecf20Sopenharmony_ci case ACL_TYPE_ACCESS: 308c2ecf20Sopenharmony_ci return &inode->i_acl; 318c2ecf20Sopenharmony_ci case ACL_TYPE_DEFAULT: 328c2ecf20Sopenharmony_ci return &inode->i_default_acl; 338c2ecf20Sopenharmony_ci default: 348c2ecf20Sopenharmony_ci BUG(); 358c2ecf20Sopenharmony_ci } 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistruct posix_acl *get_cached_acl(struct inode *inode, int type) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct posix_acl **p = acl_by_type(inode, type); 418c2ecf20Sopenharmony_ci struct posix_acl *acl; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci for (;;) { 448c2ecf20Sopenharmony_ci rcu_read_lock(); 458c2ecf20Sopenharmony_ci acl = rcu_dereference(*p); 468c2ecf20Sopenharmony_ci if (!acl || is_uncached_acl(acl) || 478c2ecf20Sopenharmony_ci refcount_inc_not_zero(&acl->a_refcount)) 488c2ecf20Sopenharmony_ci break; 498c2ecf20Sopenharmony_ci rcu_read_unlock(); 508c2ecf20Sopenharmony_ci cpu_relax(); 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci rcu_read_unlock(); 538c2ecf20Sopenharmony_ci return acl; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ciEXPORT_SYMBOL(get_cached_acl); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistruct posix_acl *get_cached_acl_rcu(struct inode *inode, int type) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci return rcu_dereference(*acl_by_type(inode, type)); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(get_cached_acl_rcu); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_civoid set_cached_acl(struct inode *inode, int type, struct posix_acl *acl) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct posix_acl **p = acl_by_type(inode, type); 668c2ecf20Sopenharmony_ci struct posix_acl *old; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci old = xchg(p, posix_acl_dup(acl)); 698c2ecf20Sopenharmony_ci if (!is_uncached_acl(old)) 708c2ecf20Sopenharmony_ci posix_acl_release(old); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(set_cached_acl); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void __forget_cached_acl(struct posix_acl **p) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct posix_acl *old; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci old = xchg(p, ACL_NOT_CACHED); 798c2ecf20Sopenharmony_ci if (!is_uncached_acl(old)) 808c2ecf20Sopenharmony_ci posix_acl_release(old); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_civoid forget_cached_acl(struct inode *inode, int type) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci __forget_cached_acl(acl_by_type(inode, type)); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(forget_cached_acl); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_civoid forget_all_cached_acls(struct inode *inode) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci __forget_cached_acl(&inode->i_acl); 928c2ecf20Sopenharmony_ci __forget_cached_acl(&inode->i_default_acl); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(forget_all_cached_acls); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistruct posix_acl *get_acl(struct inode *inode, int type) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci void *sentinel; 998c2ecf20Sopenharmony_ci struct posix_acl **p; 1008c2ecf20Sopenharmony_ci struct posix_acl *acl; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* 1038c2ecf20Sopenharmony_ci * The sentinel is used to detect when another operation like 1048c2ecf20Sopenharmony_ci * set_cached_acl() or forget_cached_acl() races with get_acl(). 1058c2ecf20Sopenharmony_ci * It is guaranteed that is_uncached_acl(sentinel) is true. 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci acl = get_cached_acl(inode, type); 1098c2ecf20Sopenharmony_ci if (!is_uncached_acl(acl)) 1108c2ecf20Sopenharmony_ci return acl; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (!IS_POSIXACL(inode)) 1138c2ecf20Sopenharmony_ci return NULL; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci sentinel = uncached_acl_sentinel(current); 1168c2ecf20Sopenharmony_ci p = acl_by_type(inode, type); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* 1198c2ecf20Sopenharmony_ci * If the ACL isn't being read yet, set our sentinel. Otherwise, the 1208c2ecf20Sopenharmony_ci * current value of the ACL will not be ACL_NOT_CACHED and so our own 1218c2ecf20Sopenharmony_ci * sentinel will not be set; another task will update the cache. We 1228c2ecf20Sopenharmony_ci * could wait for that other task to complete its job, but it's easier 1238c2ecf20Sopenharmony_ci * to just call ->get_acl to fetch the ACL ourself. (This is going to 1248c2ecf20Sopenharmony_ci * be an unlikely race.) 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_ci if (cmpxchg(p, ACL_NOT_CACHED, sentinel) != ACL_NOT_CACHED) 1278c2ecf20Sopenharmony_ci /* fall through */ ; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* 1308c2ecf20Sopenharmony_ci * Normally, the ACL returned by ->get_acl will be cached. 1318c2ecf20Sopenharmony_ci * A filesystem can prevent that by calling 1328c2ecf20Sopenharmony_ci * forget_cached_acl(inode, type) in ->get_acl. 1338c2ecf20Sopenharmony_ci * 1348c2ecf20Sopenharmony_ci * If the filesystem doesn't have a get_acl() function at all, we'll 1358c2ecf20Sopenharmony_ci * just create the negative cache entry. 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_ci if (!inode->i_op->get_acl) { 1388c2ecf20Sopenharmony_ci set_cached_acl(inode, type, NULL); 1398c2ecf20Sopenharmony_ci return NULL; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci acl = inode->i_op->get_acl(inode, type); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (IS_ERR(acl)) { 1448c2ecf20Sopenharmony_ci /* 1458c2ecf20Sopenharmony_ci * Remove our sentinel so that we don't block future attempts 1468c2ecf20Sopenharmony_ci * to cache the ACL. 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_ci cmpxchg(p, sentinel, ACL_NOT_CACHED); 1498c2ecf20Sopenharmony_ci return acl; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* 1538c2ecf20Sopenharmony_ci * Cache the result, but only if our sentinel is still in place. 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_ci posix_acl_dup(acl); 1568c2ecf20Sopenharmony_ci if (unlikely(cmpxchg(p, sentinel, acl) != sentinel)) 1578c2ecf20Sopenharmony_ci posix_acl_release(acl); 1588c2ecf20Sopenharmony_ci return acl; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(get_acl); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/* 1638c2ecf20Sopenharmony_ci * Init a fresh posix_acl 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_civoid 1668c2ecf20Sopenharmony_ciposix_acl_init(struct posix_acl *acl, int count) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci refcount_set(&acl->a_refcount, 1); 1698c2ecf20Sopenharmony_ci acl->a_count = count; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(posix_acl_init); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci/* 1748c2ecf20Sopenharmony_ci * Allocate a new ACL with the specified number of entries. 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_cistruct posix_acl * 1778c2ecf20Sopenharmony_ciposix_acl_alloc(int count, gfp_t flags) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci const size_t size = sizeof(struct posix_acl) + 1808c2ecf20Sopenharmony_ci count * sizeof(struct posix_acl_entry); 1818c2ecf20Sopenharmony_ci struct posix_acl *acl = kmalloc(size, flags); 1828c2ecf20Sopenharmony_ci if (acl) 1838c2ecf20Sopenharmony_ci posix_acl_init(acl, count); 1848c2ecf20Sopenharmony_ci return acl; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(posix_acl_alloc); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci/* 1898c2ecf20Sopenharmony_ci * Clone an ACL. 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_cistatic struct posix_acl * 1928c2ecf20Sopenharmony_ciposix_acl_clone(const struct posix_acl *acl, gfp_t flags) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct posix_acl *clone = NULL; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci if (acl) { 1978c2ecf20Sopenharmony_ci int size = sizeof(struct posix_acl) + acl->a_count * 1988c2ecf20Sopenharmony_ci sizeof(struct posix_acl_entry); 1998c2ecf20Sopenharmony_ci clone = kmemdup(acl, size, flags); 2008c2ecf20Sopenharmony_ci if (clone) 2018c2ecf20Sopenharmony_ci refcount_set(&clone->a_refcount, 1); 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci return clone; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci/* 2078c2ecf20Sopenharmony_ci * Check if an acl is valid. Returns 0 if it is, or -E... otherwise. 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_ciint 2108c2ecf20Sopenharmony_ciposix_acl_valid(struct user_namespace *user_ns, const struct posix_acl *acl) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci const struct posix_acl_entry *pa, *pe; 2138c2ecf20Sopenharmony_ci int state = ACL_USER_OBJ; 2148c2ecf20Sopenharmony_ci int needs_mask = 0; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci FOREACH_ACL_ENTRY(pa, acl, pe) { 2178c2ecf20Sopenharmony_ci if (pa->e_perm & ~(ACL_READ|ACL_WRITE|ACL_EXECUTE)) 2188c2ecf20Sopenharmony_ci return -EINVAL; 2198c2ecf20Sopenharmony_ci switch (pa->e_tag) { 2208c2ecf20Sopenharmony_ci case ACL_USER_OBJ: 2218c2ecf20Sopenharmony_ci if (state == ACL_USER_OBJ) { 2228c2ecf20Sopenharmony_ci state = ACL_USER; 2238c2ecf20Sopenharmony_ci break; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci return -EINVAL; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci case ACL_USER: 2288c2ecf20Sopenharmony_ci if (state != ACL_USER) 2298c2ecf20Sopenharmony_ci return -EINVAL; 2308c2ecf20Sopenharmony_ci if (!kuid_has_mapping(user_ns, pa->e_uid)) 2318c2ecf20Sopenharmony_ci return -EINVAL; 2328c2ecf20Sopenharmony_ci needs_mask = 1; 2338c2ecf20Sopenharmony_ci break; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci case ACL_GROUP_OBJ: 2368c2ecf20Sopenharmony_ci if (state == ACL_USER) { 2378c2ecf20Sopenharmony_ci state = ACL_GROUP; 2388c2ecf20Sopenharmony_ci break; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci return -EINVAL; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci case ACL_GROUP: 2438c2ecf20Sopenharmony_ci if (state != ACL_GROUP) 2448c2ecf20Sopenharmony_ci return -EINVAL; 2458c2ecf20Sopenharmony_ci if (!kgid_has_mapping(user_ns, pa->e_gid)) 2468c2ecf20Sopenharmony_ci return -EINVAL; 2478c2ecf20Sopenharmony_ci needs_mask = 1; 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci case ACL_MASK: 2518c2ecf20Sopenharmony_ci if (state != ACL_GROUP) 2528c2ecf20Sopenharmony_ci return -EINVAL; 2538c2ecf20Sopenharmony_ci state = ACL_OTHER; 2548c2ecf20Sopenharmony_ci break; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci case ACL_OTHER: 2578c2ecf20Sopenharmony_ci if (state == ACL_OTHER || 2588c2ecf20Sopenharmony_ci (state == ACL_GROUP && !needs_mask)) { 2598c2ecf20Sopenharmony_ci state = 0; 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci return -EINVAL; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci default: 2658c2ecf20Sopenharmony_ci return -EINVAL; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci if (state == 0) 2698c2ecf20Sopenharmony_ci return 0; 2708c2ecf20Sopenharmony_ci return -EINVAL; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(posix_acl_valid); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci/* 2758c2ecf20Sopenharmony_ci * Returns 0 if the acl can be exactly represented in the traditional 2768c2ecf20Sopenharmony_ci * file mode permission bits, or else 1. Returns -E... on error. 2778c2ecf20Sopenharmony_ci */ 2788c2ecf20Sopenharmony_ciint 2798c2ecf20Sopenharmony_ciposix_acl_equiv_mode(const struct posix_acl *acl, umode_t *mode_p) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci const struct posix_acl_entry *pa, *pe; 2828c2ecf20Sopenharmony_ci umode_t mode = 0; 2838c2ecf20Sopenharmony_ci int not_equiv = 0; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* 2868c2ecf20Sopenharmony_ci * A null ACL can always be presented as mode bits. 2878c2ecf20Sopenharmony_ci */ 2888c2ecf20Sopenharmony_ci if (!acl) 2898c2ecf20Sopenharmony_ci return 0; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci FOREACH_ACL_ENTRY(pa, acl, pe) { 2928c2ecf20Sopenharmony_ci switch (pa->e_tag) { 2938c2ecf20Sopenharmony_ci case ACL_USER_OBJ: 2948c2ecf20Sopenharmony_ci mode |= (pa->e_perm & S_IRWXO) << 6; 2958c2ecf20Sopenharmony_ci break; 2968c2ecf20Sopenharmony_ci case ACL_GROUP_OBJ: 2978c2ecf20Sopenharmony_ci mode |= (pa->e_perm & S_IRWXO) << 3; 2988c2ecf20Sopenharmony_ci break; 2998c2ecf20Sopenharmony_ci case ACL_OTHER: 3008c2ecf20Sopenharmony_ci mode |= pa->e_perm & S_IRWXO; 3018c2ecf20Sopenharmony_ci break; 3028c2ecf20Sopenharmony_ci case ACL_MASK: 3038c2ecf20Sopenharmony_ci mode = (mode & ~S_IRWXG) | 3048c2ecf20Sopenharmony_ci ((pa->e_perm & S_IRWXO) << 3); 3058c2ecf20Sopenharmony_ci not_equiv = 1; 3068c2ecf20Sopenharmony_ci break; 3078c2ecf20Sopenharmony_ci case ACL_USER: 3088c2ecf20Sopenharmony_ci case ACL_GROUP: 3098c2ecf20Sopenharmony_ci not_equiv = 1; 3108c2ecf20Sopenharmony_ci break; 3118c2ecf20Sopenharmony_ci default: 3128c2ecf20Sopenharmony_ci return -EINVAL; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci if (mode_p) 3168c2ecf20Sopenharmony_ci *mode_p = (*mode_p & ~S_IRWXUGO) | mode; 3178c2ecf20Sopenharmony_ci return not_equiv; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ciEXPORT_SYMBOL(posix_acl_equiv_mode); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci/* 3228c2ecf20Sopenharmony_ci * Create an ACL representing the file mode permission bits of an inode. 3238c2ecf20Sopenharmony_ci */ 3248c2ecf20Sopenharmony_cistruct posix_acl * 3258c2ecf20Sopenharmony_ciposix_acl_from_mode(umode_t mode, gfp_t flags) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci struct posix_acl *acl = posix_acl_alloc(3, flags); 3288c2ecf20Sopenharmony_ci if (!acl) 3298c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci acl->a_entries[0].e_tag = ACL_USER_OBJ; 3328c2ecf20Sopenharmony_ci acl->a_entries[0].e_perm = (mode & S_IRWXU) >> 6; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci acl->a_entries[1].e_tag = ACL_GROUP_OBJ; 3358c2ecf20Sopenharmony_ci acl->a_entries[1].e_perm = (mode & S_IRWXG) >> 3; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci acl->a_entries[2].e_tag = ACL_OTHER; 3388c2ecf20Sopenharmony_ci acl->a_entries[2].e_perm = (mode & S_IRWXO); 3398c2ecf20Sopenharmony_ci return acl; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(posix_acl_from_mode); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci/* 3448c2ecf20Sopenharmony_ci * Return 0 if current is granted want access to the inode 3458c2ecf20Sopenharmony_ci * by the acl. Returns -E... otherwise. 3468c2ecf20Sopenharmony_ci */ 3478c2ecf20Sopenharmony_ciint 3488c2ecf20Sopenharmony_ciposix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci const struct posix_acl_entry *pa, *pe, *mask_obj; 3518c2ecf20Sopenharmony_ci int found = 0; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci want &= MAY_READ | MAY_WRITE | MAY_EXEC; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci FOREACH_ACL_ENTRY(pa, acl, pe) { 3568c2ecf20Sopenharmony_ci switch(pa->e_tag) { 3578c2ecf20Sopenharmony_ci case ACL_USER_OBJ: 3588c2ecf20Sopenharmony_ci /* (May have been checked already) */ 3598c2ecf20Sopenharmony_ci if (uid_eq(inode->i_uid, current_fsuid())) 3608c2ecf20Sopenharmony_ci goto check_perm; 3618c2ecf20Sopenharmony_ci break; 3628c2ecf20Sopenharmony_ci case ACL_USER: 3638c2ecf20Sopenharmony_ci if (uid_eq(pa->e_uid, current_fsuid())) 3648c2ecf20Sopenharmony_ci goto mask; 3658c2ecf20Sopenharmony_ci break; 3668c2ecf20Sopenharmony_ci case ACL_GROUP_OBJ: 3678c2ecf20Sopenharmony_ci if (in_group_p(inode->i_gid)) { 3688c2ecf20Sopenharmony_ci found = 1; 3698c2ecf20Sopenharmony_ci if ((pa->e_perm & want) == want) 3708c2ecf20Sopenharmony_ci goto mask; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci break; 3738c2ecf20Sopenharmony_ci case ACL_GROUP: 3748c2ecf20Sopenharmony_ci if (in_group_p(pa->e_gid)) { 3758c2ecf20Sopenharmony_ci found = 1; 3768c2ecf20Sopenharmony_ci if ((pa->e_perm & want) == want) 3778c2ecf20Sopenharmony_ci goto mask; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci break; 3808c2ecf20Sopenharmony_ci case ACL_MASK: 3818c2ecf20Sopenharmony_ci break; 3828c2ecf20Sopenharmony_ci case ACL_OTHER: 3838c2ecf20Sopenharmony_ci if (found) 3848c2ecf20Sopenharmony_ci return -EACCES; 3858c2ecf20Sopenharmony_ci else 3868c2ecf20Sopenharmony_ci goto check_perm; 3878c2ecf20Sopenharmony_ci default: 3888c2ecf20Sopenharmony_ci return -EIO; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci return -EIO; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cimask: 3948c2ecf20Sopenharmony_ci for (mask_obj = pa+1; mask_obj != pe; mask_obj++) { 3958c2ecf20Sopenharmony_ci if (mask_obj->e_tag == ACL_MASK) { 3968c2ecf20Sopenharmony_ci if ((pa->e_perm & mask_obj->e_perm & want) == want) 3978c2ecf20Sopenharmony_ci return 0; 3988c2ecf20Sopenharmony_ci return -EACCES; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cicheck_perm: 4038c2ecf20Sopenharmony_ci if ((pa->e_perm & want) == want) 4048c2ecf20Sopenharmony_ci return 0; 4058c2ecf20Sopenharmony_ci return -EACCES; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci/* 4098c2ecf20Sopenharmony_ci * Modify acl when creating a new inode. The caller must ensure the acl is 4108c2ecf20Sopenharmony_ci * only referenced once. 4118c2ecf20Sopenharmony_ci * 4128c2ecf20Sopenharmony_ci * mode_p initially must contain the mode parameter to the open() / creat() 4138c2ecf20Sopenharmony_ci * system calls. All permissions that are not granted by the acl are removed. 4148c2ecf20Sopenharmony_ci * The permissions in the acl are changed to reflect the mode_p parameter. 4158c2ecf20Sopenharmony_ci */ 4168c2ecf20Sopenharmony_cistatic int posix_acl_create_masq(struct posix_acl *acl, umode_t *mode_p) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci struct posix_acl_entry *pa, *pe; 4198c2ecf20Sopenharmony_ci struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL; 4208c2ecf20Sopenharmony_ci umode_t mode = *mode_p; 4218c2ecf20Sopenharmony_ci int not_equiv = 0; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* assert(atomic_read(acl->a_refcount) == 1); */ 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci FOREACH_ACL_ENTRY(pa, acl, pe) { 4268c2ecf20Sopenharmony_ci switch(pa->e_tag) { 4278c2ecf20Sopenharmony_ci case ACL_USER_OBJ: 4288c2ecf20Sopenharmony_ci pa->e_perm &= (mode >> 6) | ~S_IRWXO; 4298c2ecf20Sopenharmony_ci mode &= (pa->e_perm << 6) | ~S_IRWXU; 4308c2ecf20Sopenharmony_ci break; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci case ACL_USER: 4338c2ecf20Sopenharmony_ci case ACL_GROUP: 4348c2ecf20Sopenharmony_ci not_equiv = 1; 4358c2ecf20Sopenharmony_ci break; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci case ACL_GROUP_OBJ: 4388c2ecf20Sopenharmony_ci group_obj = pa; 4398c2ecf20Sopenharmony_ci break; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci case ACL_OTHER: 4428c2ecf20Sopenharmony_ci pa->e_perm &= mode | ~S_IRWXO; 4438c2ecf20Sopenharmony_ci mode &= pa->e_perm | ~S_IRWXO; 4448c2ecf20Sopenharmony_ci break; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci case ACL_MASK: 4478c2ecf20Sopenharmony_ci mask_obj = pa; 4488c2ecf20Sopenharmony_ci not_equiv = 1; 4498c2ecf20Sopenharmony_ci break; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci default: 4528c2ecf20Sopenharmony_ci return -EIO; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (mask_obj) { 4578c2ecf20Sopenharmony_ci mask_obj->e_perm &= (mode >> 3) | ~S_IRWXO; 4588c2ecf20Sopenharmony_ci mode &= (mask_obj->e_perm << 3) | ~S_IRWXG; 4598c2ecf20Sopenharmony_ci } else { 4608c2ecf20Sopenharmony_ci if (!group_obj) 4618c2ecf20Sopenharmony_ci return -EIO; 4628c2ecf20Sopenharmony_ci group_obj->e_perm &= (mode >> 3) | ~S_IRWXO; 4638c2ecf20Sopenharmony_ci mode &= (group_obj->e_perm << 3) | ~S_IRWXG; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci *mode_p = (*mode_p & ~S_IRWXUGO) | mode; 4678c2ecf20Sopenharmony_ci return not_equiv; 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci/* 4718c2ecf20Sopenharmony_ci * Modify the ACL for the chmod syscall. 4728c2ecf20Sopenharmony_ci */ 4738c2ecf20Sopenharmony_cistatic int __posix_acl_chmod_masq(struct posix_acl *acl, umode_t mode) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL; 4768c2ecf20Sopenharmony_ci struct posix_acl_entry *pa, *pe; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci /* assert(atomic_read(acl->a_refcount) == 1); */ 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci FOREACH_ACL_ENTRY(pa, acl, pe) { 4818c2ecf20Sopenharmony_ci switch(pa->e_tag) { 4828c2ecf20Sopenharmony_ci case ACL_USER_OBJ: 4838c2ecf20Sopenharmony_ci pa->e_perm = (mode & S_IRWXU) >> 6; 4848c2ecf20Sopenharmony_ci break; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci case ACL_USER: 4878c2ecf20Sopenharmony_ci case ACL_GROUP: 4888c2ecf20Sopenharmony_ci break; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci case ACL_GROUP_OBJ: 4918c2ecf20Sopenharmony_ci group_obj = pa; 4928c2ecf20Sopenharmony_ci break; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci case ACL_MASK: 4958c2ecf20Sopenharmony_ci mask_obj = pa; 4968c2ecf20Sopenharmony_ci break; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci case ACL_OTHER: 4998c2ecf20Sopenharmony_ci pa->e_perm = (mode & S_IRWXO); 5008c2ecf20Sopenharmony_ci break; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci default: 5038c2ecf20Sopenharmony_ci return -EIO; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci if (mask_obj) { 5088c2ecf20Sopenharmony_ci mask_obj->e_perm = (mode & S_IRWXG) >> 3; 5098c2ecf20Sopenharmony_ci } else { 5108c2ecf20Sopenharmony_ci if (!group_obj) 5118c2ecf20Sopenharmony_ci return -EIO; 5128c2ecf20Sopenharmony_ci group_obj->e_perm = (mode & S_IRWXG) >> 3; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci return 0; 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ciint 5198c2ecf20Sopenharmony_ci__posix_acl_create(struct posix_acl **acl, gfp_t gfp, umode_t *mode_p) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci struct posix_acl *clone = posix_acl_clone(*acl, gfp); 5228c2ecf20Sopenharmony_ci int err = -ENOMEM; 5238c2ecf20Sopenharmony_ci if (clone) { 5248c2ecf20Sopenharmony_ci err = posix_acl_create_masq(clone, mode_p); 5258c2ecf20Sopenharmony_ci if (err < 0) { 5268c2ecf20Sopenharmony_ci posix_acl_release(clone); 5278c2ecf20Sopenharmony_ci clone = NULL; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci posix_acl_release(*acl); 5318c2ecf20Sopenharmony_ci *acl = clone; 5328c2ecf20Sopenharmony_ci return err; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__posix_acl_create); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ciint 5378c2ecf20Sopenharmony_ci__posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci struct posix_acl *clone = posix_acl_clone(*acl, gfp); 5408c2ecf20Sopenharmony_ci int err = -ENOMEM; 5418c2ecf20Sopenharmony_ci if (clone) { 5428c2ecf20Sopenharmony_ci err = __posix_acl_chmod_masq(clone, mode); 5438c2ecf20Sopenharmony_ci if (err) { 5448c2ecf20Sopenharmony_ci posix_acl_release(clone); 5458c2ecf20Sopenharmony_ci clone = NULL; 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci posix_acl_release(*acl); 5498c2ecf20Sopenharmony_ci *acl = clone; 5508c2ecf20Sopenharmony_ci return err; 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__posix_acl_chmod); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ciint 5558c2ecf20Sopenharmony_ciposix_acl_chmod(struct inode *inode, umode_t mode) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci struct posix_acl *acl; 5588c2ecf20Sopenharmony_ci int ret = 0; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci if (!IS_POSIXACL(inode)) 5618c2ecf20Sopenharmony_ci return 0; 5628c2ecf20Sopenharmony_ci if (!inode->i_op->set_acl) 5638c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci acl = get_acl(inode, ACL_TYPE_ACCESS); 5668c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(acl)) { 5678c2ecf20Sopenharmony_ci if (acl == ERR_PTR(-EOPNOTSUPP)) 5688c2ecf20Sopenharmony_ci return 0; 5698c2ecf20Sopenharmony_ci return PTR_ERR(acl); 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci ret = __posix_acl_chmod(&acl, GFP_KERNEL, mode); 5738c2ecf20Sopenharmony_ci if (ret) 5748c2ecf20Sopenharmony_ci return ret; 5758c2ecf20Sopenharmony_ci ret = inode->i_op->set_acl(inode, acl, ACL_TYPE_ACCESS); 5768c2ecf20Sopenharmony_ci posix_acl_release(acl); 5778c2ecf20Sopenharmony_ci return ret; 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(posix_acl_chmod); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ciint 5828c2ecf20Sopenharmony_ciposix_acl_create(struct inode *dir, umode_t *mode, 5838c2ecf20Sopenharmony_ci struct posix_acl **default_acl, struct posix_acl **acl) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci struct posix_acl *p; 5868c2ecf20Sopenharmony_ci struct posix_acl *clone; 5878c2ecf20Sopenharmony_ci int ret; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci *acl = NULL; 5908c2ecf20Sopenharmony_ci *default_acl = NULL; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci if (S_ISLNK(*mode) || !IS_POSIXACL(dir)) 5938c2ecf20Sopenharmony_ci return 0; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci p = get_acl(dir, ACL_TYPE_DEFAULT); 5968c2ecf20Sopenharmony_ci if (!p || p == ERR_PTR(-EOPNOTSUPP)) { 5978c2ecf20Sopenharmony_ci *mode &= ~current_umask(); 5988c2ecf20Sopenharmony_ci return 0; 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci if (IS_ERR(p)) 6018c2ecf20Sopenharmony_ci return PTR_ERR(p); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci ret = -ENOMEM; 6048c2ecf20Sopenharmony_ci clone = posix_acl_clone(p, GFP_NOFS); 6058c2ecf20Sopenharmony_ci if (!clone) 6068c2ecf20Sopenharmony_ci goto err_release; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci ret = posix_acl_create_masq(clone, mode); 6098c2ecf20Sopenharmony_ci if (ret < 0) 6108c2ecf20Sopenharmony_ci goto err_release_clone; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (ret == 0) 6138c2ecf20Sopenharmony_ci posix_acl_release(clone); 6148c2ecf20Sopenharmony_ci else 6158c2ecf20Sopenharmony_ci *acl = clone; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci if (!S_ISDIR(*mode)) 6188c2ecf20Sopenharmony_ci posix_acl_release(p); 6198c2ecf20Sopenharmony_ci else 6208c2ecf20Sopenharmony_ci *default_acl = p; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci return 0; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_cierr_release_clone: 6258c2ecf20Sopenharmony_ci posix_acl_release(clone); 6268c2ecf20Sopenharmony_cierr_release: 6278c2ecf20Sopenharmony_ci posix_acl_release(p); 6288c2ecf20Sopenharmony_ci return ret; 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(posix_acl_create); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci/** 6338c2ecf20Sopenharmony_ci * posix_acl_update_mode - update mode in set_acl 6348c2ecf20Sopenharmony_ci * @inode: target inode 6358c2ecf20Sopenharmony_ci * @mode_p: mode (pointer) for update 6368c2ecf20Sopenharmony_ci * @acl: acl pointer 6378c2ecf20Sopenharmony_ci * 6388c2ecf20Sopenharmony_ci * Update the file mode when setting an ACL: compute the new file permission 6398c2ecf20Sopenharmony_ci * bits based on the ACL. In addition, if the ACL is equivalent to the new 6408c2ecf20Sopenharmony_ci * file mode, set *@acl to NULL to indicate that no ACL should be set. 6418c2ecf20Sopenharmony_ci * 6428c2ecf20Sopenharmony_ci * As with chmod, clear the setgid bit if the caller is not in the owning group 6438c2ecf20Sopenharmony_ci * or capable of CAP_FSETID (see inode_change_ok). 6448c2ecf20Sopenharmony_ci * 6458c2ecf20Sopenharmony_ci * Called from set_acl inode operations. 6468c2ecf20Sopenharmony_ci */ 6478c2ecf20Sopenharmony_ciint posix_acl_update_mode(struct inode *inode, umode_t *mode_p, 6488c2ecf20Sopenharmony_ci struct posix_acl **acl) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci umode_t mode = inode->i_mode; 6518c2ecf20Sopenharmony_ci int error; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci error = posix_acl_equiv_mode(*acl, &mode); 6548c2ecf20Sopenharmony_ci if (error < 0) 6558c2ecf20Sopenharmony_ci return error; 6568c2ecf20Sopenharmony_ci if (error == 0) 6578c2ecf20Sopenharmony_ci *acl = NULL; 6588c2ecf20Sopenharmony_ci if (!in_group_p(inode->i_gid) && 6598c2ecf20Sopenharmony_ci !capable_wrt_inode_uidgid(inode, CAP_FSETID)) 6608c2ecf20Sopenharmony_ci mode &= ~S_ISGID; 6618c2ecf20Sopenharmony_ci *mode_p = mode; 6628c2ecf20Sopenharmony_ci return 0; 6638c2ecf20Sopenharmony_ci} 6648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(posix_acl_update_mode); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci/* 6678c2ecf20Sopenharmony_ci * Fix up the uids and gids in posix acl extended attributes in place. 6688c2ecf20Sopenharmony_ci */ 6698c2ecf20Sopenharmony_cistatic void posix_acl_fix_xattr_userns( 6708c2ecf20Sopenharmony_ci struct user_namespace *to, struct user_namespace *from, 6718c2ecf20Sopenharmony_ci void *value, size_t size) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci struct posix_acl_xattr_header *header = value; 6748c2ecf20Sopenharmony_ci struct posix_acl_xattr_entry *entry = (void *)(header + 1), *end; 6758c2ecf20Sopenharmony_ci int count; 6768c2ecf20Sopenharmony_ci kuid_t uid; 6778c2ecf20Sopenharmony_ci kgid_t gid; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (!value) 6808c2ecf20Sopenharmony_ci return; 6818c2ecf20Sopenharmony_ci if (size < sizeof(struct posix_acl_xattr_header)) 6828c2ecf20Sopenharmony_ci return; 6838c2ecf20Sopenharmony_ci if (header->a_version != cpu_to_le32(POSIX_ACL_XATTR_VERSION)) 6848c2ecf20Sopenharmony_ci return; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci count = posix_acl_xattr_count(size); 6878c2ecf20Sopenharmony_ci if (count < 0) 6888c2ecf20Sopenharmony_ci return; 6898c2ecf20Sopenharmony_ci if (count == 0) 6908c2ecf20Sopenharmony_ci return; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci for (end = entry + count; entry != end; entry++) { 6938c2ecf20Sopenharmony_ci switch(le16_to_cpu(entry->e_tag)) { 6948c2ecf20Sopenharmony_ci case ACL_USER: 6958c2ecf20Sopenharmony_ci uid = make_kuid(from, le32_to_cpu(entry->e_id)); 6968c2ecf20Sopenharmony_ci entry->e_id = cpu_to_le32(from_kuid(to, uid)); 6978c2ecf20Sopenharmony_ci break; 6988c2ecf20Sopenharmony_ci case ACL_GROUP: 6998c2ecf20Sopenharmony_ci gid = make_kgid(from, le32_to_cpu(entry->e_id)); 7008c2ecf20Sopenharmony_ci entry->e_id = cpu_to_le32(from_kgid(to, gid)); 7018c2ecf20Sopenharmony_ci break; 7028c2ecf20Sopenharmony_ci default: 7038c2ecf20Sopenharmony_ci break; 7048c2ecf20Sopenharmony_ci } 7058c2ecf20Sopenharmony_ci } 7068c2ecf20Sopenharmony_ci} 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_civoid posix_acl_fix_xattr_from_user(void *value, size_t size) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci struct user_namespace *user_ns = current_user_ns(); 7118c2ecf20Sopenharmony_ci if (user_ns == &init_user_ns) 7128c2ecf20Sopenharmony_ci return; 7138c2ecf20Sopenharmony_ci posix_acl_fix_xattr_userns(&init_user_ns, user_ns, value, size); 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_civoid posix_acl_fix_xattr_to_user(void *value, size_t size) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci struct user_namespace *user_ns = current_user_ns(); 7198c2ecf20Sopenharmony_ci if (user_ns == &init_user_ns) 7208c2ecf20Sopenharmony_ci return; 7218c2ecf20Sopenharmony_ci posix_acl_fix_xattr_userns(user_ns, &init_user_ns, value, size); 7228c2ecf20Sopenharmony_ci} 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci/* 7258c2ecf20Sopenharmony_ci * Convert from extended attribute to in-memory representation. 7268c2ecf20Sopenharmony_ci */ 7278c2ecf20Sopenharmony_cistruct posix_acl * 7288c2ecf20Sopenharmony_ciposix_acl_from_xattr(struct user_namespace *user_ns, 7298c2ecf20Sopenharmony_ci const void *value, size_t size) 7308c2ecf20Sopenharmony_ci{ 7318c2ecf20Sopenharmony_ci const struct posix_acl_xattr_header *header = value; 7328c2ecf20Sopenharmony_ci const struct posix_acl_xattr_entry *entry = (const void *)(header + 1), *end; 7338c2ecf20Sopenharmony_ci int count; 7348c2ecf20Sopenharmony_ci struct posix_acl *acl; 7358c2ecf20Sopenharmony_ci struct posix_acl_entry *acl_e; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci if (!value) 7388c2ecf20Sopenharmony_ci return NULL; 7398c2ecf20Sopenharmony_ci if (size < sizeof(struct posix_acl_xattr_header)) 7408c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 7418c2ecf20Sopenharmony_ci if (header->a_version != cpu_to_le32(POSIX_ACL_XATTR_VERSION)) 7428c2ecf20Sopenharmony_ci return ERR_PTR(-EOPNOTSUPP); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci count = posix_acl_xattr_count(size); 7458c2ecf20Sopenharmony_ci if (count < 0) 7468c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 7478c2ecf20Sopenharmony_ci if (count == 0) 7488c2ecf20Sopenharmony_ci return NULL; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci acl = posix_acl_alloc(count, GFP_NOFS); 7518c2ecf20Sopenharmony_ci if (!acl) 7528c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 7538c2ecf20Sopenharmony_ci acl_e = acl->a_entries; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci for (end = entry + count; entry != end; acl_e++, entry++) { 7568c2ecf20Sopenharmony_ci acl_e->e_tag = le16_to_cpu(entry->e_tag); 7578c2ecf20Sopenharmony_ci acl_e->e_perm = le16_to_cpu(entry->e_perm); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci switch(acl_e->e_tag) { 7608c2ecf20Sopenharmony_ci case ACL_USER_OBJ: 7618c2ecf20Sopenharmony_ci case ACL_GROUP_OBJ: 7628c2ecf20Sopenharmony_ci case ACL_MASK: 7638c2ecf20Sopenharmony_ci case ACL_OTHER: 7648c2ecf20Sopenharmony_ci break; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci case ACL_USER: 7678c2ecf20Sopenharmony_ci acl_e->e_uid = 7688c2ecf20Sopenharmony_ci make_kuid(user_ns, 7698c2ecf20Sopenharmony_ci le32_to_cpu(entry->e_id)); 7708c2ecf20Sopenharmony_ci if (!uid_valid(acl_e->e_uid)) 7718c2ecf20Sopenharmony_ci goto fail; 7728c2ecf20Sopenharmony_ci break; 7738c2ecf20Sopenharmony_ci case ACL_GROUP: 7748c2ecf20Sopenharmony_ci acl_e->e_gid = 7758c2ecf20Sopenharmony_ci make_kgid(user_ns, 7768c2ecf20Sopenharmony_ci le32_to_cpu(entry->e_id)); 7778c2ecf20Sopenharmony_ci if (!gid_valid(acl_e->e_gid)) 7788c2ecf20Sopenharmony_ci goto fail; 7798c2ecf20Sopenharmony_ci break; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci default: 7828c2ecf20Sopenharmony_ci goto fail; 7838c2ecf20Sopenharmony_ci } 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci return acl; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_cifail: 7888c2ecf20Sopenharmony_ci posix_acl_release(acl); 7898c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 7908c2ecf20Sopenharmony_ci} 7918c2ecf20Sopenharmony_ciEXPORT_SYMBOL (posix_acl_from_xattr); 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci/* 7948c2ecf20Sopenharmony_ci * Convert from in-memory to extended attribute representation. 7958c2ecf20Sopenharmony_ci */ 7968c2ecf20Sopenharmony_ciint 7978c2ecf20Sopenharmony_ciposix_acl_to_xattr(struct user_namespace *user_ns, const struct posix_acl *acl, 7988c2ecf20Sopenharmony_ci void *buffer, size_t size) 7998c2ecf20Sopenharmony_ci{ 8008c2ecf20Sopenharmony_ci struct posix_acl_xattr_header *ext_acl = buffer; 8018c2ecf20Sopenharmony_ci struct posix_acl_xattr_entry *ext_entry; 8028c2ecf20Sopenharmony_ci int real_size, n; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci real_size = posix_acl_xattr_size(acl->a_count); 8058c2ecf20Sopenharmony_ci if (!buffer) 8068c2ecf20Sopenharmony_ci return real_size; 8078c2ecf20Sopenharmony_ci if (real_size > size) 8088c2ecf20Sopenharmony_ci return -ERANGE; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci ext_entry = (void *)(ext_acl + 1); 8118c2ecf20Sopenharmony_ci ext_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci for (n=0; n < acl->a_count; n++, ext_entry++) { 8148c2ecf20Sopenharmony_ci const struct posix_acl_entry *acl_e = &acl->a_entries[n]; 8158c2ecf20Sopenharmony_ci ext_entry->e_tag = cpu_to_le16(acl_e->e_tag); 8168c2ecf20Sopenharmony_ci ext_entry->e_perm = cpu_to_le16(acl_e->e_perm); 8178c2ecf20Sopenharmony_ci switch(acl_e->e_tag) { 8188c2ecf20Sopenharmony_ci case ACL_USER: 8198c2ecf20Sopenharmony_ci ext_entry->e_id = 8208c2ecf20Sopenharmony_ci cpu_to_le32(from_kuid(user_ns, acl_e->e_uid)); 8218c2ecf20Sopenharmony_ci break; 8228c2ecf20Sopenharmony_ci case ACL_GROUP: 8238c2ecf20Sopenharmony_ci ext_entry->e_id = 8248c2ecf20Sopenharmony_ci cpu_to_le32(from_kgid(user_ns, acl_e->e_gid)); 8258c2ecf20Sopenharmony_ci break; 8268c2ecf20Sopenharmony_ci default: 8278c2ecf20Sopenharmony_ci ext_entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID); 8288c2ecf20Sopenharmony_ci break; 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci } 8318c2ecf20Sopenharmony_ci return real_size; 8328c2ecf20Sopenharmony_ci} 8338c2ecf20Sopenharmony_ciEXPORT_SYMBOL (posix_acl_to_xattr); 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_cistatic int 8368c2ecf20Sopenharmony_ciposix_acl_xattr_get(const struct xattr_handler *handler, 8378c2ecf20Sopenharmony_ci struct dentry *unused, struct inode *inode, 8388c2ecf20Sopenharmony_ci const char *name, void *value, size_t size) 8398c2ecf20Sopenharmony_ci{ 8408c2ecf20Sopenharmony_ci struct posix_acl *acl; 8418c2ecf20Sopenharmony_ci int error; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci if (!IS_POSIXACL(inode)) 8448c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 8458c2ecf20Sopenharmony_ci if (S_ISLNK(inode->i_mode)) 8468c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci acl = get_acl(inode, handler->flags); 8498c2ecf20Sopenharmony_ci if (IS_ERR(acl)) 8508c2ecf20Sopenharmony_ci return PTR_ERR(acl); 8518c2ecf20Sopenharmony_ci if (acl == NULL) 8528c2ecf20Sopenharmony_ci return -ENODATA; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci error = posix_acl_to_xattr(&init_user_ns, acl, value, size); 8558c2ecf20Sopenharmony_ci posix_acl_release(acl); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci return error; 8588c2ecf20Sopenharmony_ci} 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ciint 8618c2ecf20Sopenharmony_ciset_posix_acl(struct inode *inode, int type, struct posix_acl *acl) 8628c2ecf20Sopenharmony_ci{ 8638c2ecf20Sopenharmony_ci if (!IS_POSIXACL(inode)) 8648c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 8658c2ecf20Sopenharmony_ci if (!inode->i_op->set_acl) 8668c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci if (type == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode)) 8698c2ecf20Sopenharmony_ci return acl ? -EACCES : 0; 8708c2ecf20Sopenharmony_ci if (!inode_owner_or_capable(inode)) 8718c2ecf20Sopenharmony_ci return -EPERM; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci if (acl) { 8748c2ecf20Sopenharmony_ci int ret = posix_acl_valid(inode->i_sb->s_user_ns, acl); 8758c2ecf20Sopenharmony_ci if (ret) 8768c2ecf20Sopenharmony_ci return ret; 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci return inode->i_op->set_acl(inode, acl, type); 8798c2ecf20Sopenharmony_ci} 8808c2ecf20Sopenharmony_ciEXPORT_SYMBOL(set_posix_acl); 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_cistatic int 8838c2ecf20Sopenharmony_ciposix_acl_xattr_set(const struct xattr_handler *handler, 8848c2ecf20Sopenharmony_ci struct dentry *unused, struct inode *inode, 8858c2ecf20Sopenharmony_ci const char *name, const void *value, 8868c2ecf20Sopenharmony_ci size_t size, int flags) 8878c2ecf20Sopenharmony_ci{ 8888c2ecf20Sopenharmony_ci struct posix_acl *acl = NULL; 8898c2ecf20Sopenharmony_ci int ret; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci if (value) { 8928c2ecf20Sopenharmony_ci acl = posix_acl_from_xattr(&init_user_ns, value, size); 8938c2ecf20Sopenharmony_ci if (IS_ERR(acl)) 8948c2ecf20Sopenharmony_ci return PTR_ERR(acl); 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci ret = set_posix_acl(inode, handler->flags, acl); 8978c2ecf20Sopenharmony_ci posix_acl_release(acl); 8988c2ecf20Sopenharmony_ci return ret; 8998c2ecf20Sopenharmony_ci} 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_cistatic bool 9028c2ecf20Sopenharmony_ciposix_acl_xattr_list(struct dentry *dentry) 9038c2ecf20Sopenharmony_ci{ 9048c2ecf20Sopenharmony_ci return IS_POSIXACL(d_backing_inode(dentry)); 9058c2ecf20Sopenharmony_ci} 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ciconst struct xattr_handler posix_acl_access_xattr_handler = { 9088c2ecf20Sopenharmony_ci .name = XATTR_NAME_POSIX_ACL_ACCESS, 9098c2ecf20Sopenharmony_ci .flags = ACL_TYPE_ACCESS, 9108c2ecf20Sopenharmony_ci .list = posix_acl_xattr_list, 9118c2ecf20Sopenharmony_ci .get = posix_acl_xattr_get, 9128c2ecf20Sopenharmony_ci .set = posix_acl_xattr_set, 9138c2ecf20Sopenharmony_ci}; 9148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(posix_acl_access_xattr_handler); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ciconst struct xattr_handler posix_acl_default_xattr_handler = { 9178c2ecf20Sopenharmony_ci .name = XATTR_NAME_POSIX_ACL_DEFAULT, 9188c2ecf20Sopenharmony_ci .flags = ACL_TYPE_DEFAULT, 9198c2ecf20Sopenharmony_ci .list = posix_acl_xattr_list, 9208c2ecf20Sopenharmony_ci .get = posix_acl_xattr_get, 9218c2ecf20Sopenharmony_ci .set = posix_acl_xattr_set, 9228c2ecf20Sopenharmony_ci}; 9238c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(posix_acl_default_xattr_handler); 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ciint simple_set_acl(struct inode *inode, struct posix_acl *acl, int type) 9268c2ecf20Sopenharmony_ci{ 9278c2ecf20Sopenharmony_ci int error; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci if (type == ACL_TYPE_ACCESS) { 9308c2ecf20Sopenharmony_ci error = posix_acl_update_mode(inode, 9318c2ecf20Sopenharmony_ci &inode->i_mode, &acl); 9328c2ecf20Sopenharmony_ci if (error) 9338c2ecf20Sopenharmony_ci return error; 9348c2ecf20Sopenharmony_ci } 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci inode->i_ctime = current_time(inode); 9378c2ecf20Sopenharmony_ci set_cached_acl(inode, type, acl); 9388c2ecf20Sopenharmony_ci return 0; 9398c2ecf20Sopenharmony_ci} 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ciint simple_acl_create(struct inode *dir, struct inode *inode) 9428c2ecf20Sopenharmony_ci{ 9438c2ecf20Sopenharmony_ci struct posix_acl *default_acl, *acl; 9448c2ecf20Sopenharmony_ci int error; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl); 9478c2ecf20Sopenharmony_ci if (error) 9488c2ecf20Sopenharmony_ci return error; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci set_cached_acl(inode, ACL_TYPE_DEFAULT, default_acl); 9518c2ecf20Sopenharmony_ci set_cached_acl(inode, ACL_TYPE_ACCESS, acl); 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci if (default_acl) 9548c2ecf20Sopenharmony_ci posix_acl_release(default_acl); 9558c2ecf20Sopenharmony_ci if (acl) 9568c2ecf20Sopenharmony_ci posix_acl_release(acl); 9578c2ecf20Sopenharmony_ci return 0; 9588c2ecf20Sopenharmony_ci} 959