162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2002,2003 by Andreas Gruenbacher <a.gruenbacher@computer.org> 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Fixes from William Schumacher incorporated on 15 March 2001. 662306a36Sopenharmony_ci * (Reported by Charles Bertsch, <CBertsch@microtest.com>). 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* 1062306a36Sopenharmony_ci * This file contains generic functions for manipulating 1162306a36Sopenharmony_ci * POSIX 1003.1e draft standard 17 ACLs. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/atomic.h> 1762306a36Sopenharmony_ci#include <linux/fs.h> 1862306a36Sopenharmony_ci#include <linux/sched.h> 1962306a36Sopenharmony_ci#include <linux/cred.h> 2062306a36Sopenharmony_ci#include <linux/posix_acl.h> 2162306a36Sopenharmony_ci#include <linux/posix_acl_xattr.h> 2262306a36Sopenharmony_ci#include <linux/xattr.h> 2362306a36Sopenharmony_ci#include <linux/export.h> 2462306a36Sopenharmony_ci#include <linux/user_namespace.h> 2562306a36Sopenharmony_ci#include <linux/namei.h> 2662306a36Sopenharmony_ci#include <linux/mnt_idmapping.h> 2762306a36Sopenharmony_ci#include <linux/iversion.h> 2862306a36Sopenharmony_ci#include <linux/security.h> 2962306a36Sopenharmony_ci#include <linux/evm.h> 3062306a36Sopenharmony_ci#include <linux/fsnotify.h> 3162306a36Sopenharmony_ci#include <linux/filelock.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include "internal.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic struct posix_acl **acl_by_type(struct inode *inode, int type) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci switch (type) { 3862306a36Sopenharmony_ci case ACL_TYPE_ACCESS: 3962306a36Sopenharmony_ci return &inode->i_acl; 4062306a36Sopenharmony_ci case ACL_TYPE_DEFAULT: 4162306a36Sopenharmony_ci return &inode->i_default_acl; 4262306a36Sopenharmony_ci default: 4362306a36Sopenharmony_ci BUG(); 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistruct posix_acl *get_cached_acl(struct inode *inode, int type) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct posix_acl **p = acl_by_type(inode, type); 5062306a36Sopenharmony_ci struct posix_acl *acl; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci for (;;) { 5362306a36Sopenharmony_ci rcu_read_lock(); 5462306a36Sopenharmony_ci acl = rcu_dereference(*p); 5562306a36Sopenharmony_ci if (!acl || is_uncached_acl(acl) || 5662306a36Sopenharmony_ci refcount_inc_not_zero(&acl->a_refcount)) 5762306a36Sopenharmony_ci break; 5862306a36Sopenharmony_ci rcu_read_unlock(); 5962306a36Sopenharmony_ci cpu_relax(); 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci rcu_read_unlock(); 6262306a36Sopenharmony_ci return acl; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ciEXPORT_SYMBOL(get_cached_acl); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistruct posix_acl *get_cached_acl_rcu(struct inode *inode, int type) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct posix_acl *acl = rcu_dereference(*acl_by_type(inode, type)); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (acl == ACL_DONT_CACHE) { 7162306a36Sopenharmony_ci struct posix_acl *ret; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci ret = inode->i_op->get_inode_acl(inode, type, LOOKUP_RCU); 7462306a36Sopenharmony_ci if (!IS_ERR(ret)) 7562306a36Sopenharmony_ci acl = ret; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return acl; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ciEXPORT_SYMBOL(get_cached_acl_rcu); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_civoid set_cached_acl(struct inode *inode, int type, struct posix_acl *acl) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct posix_acl **p = acl_by_type(inode, type); 8562306a36Sopenharmony_ci struct posix_acl *old; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci old = xchg(p, posix_acl_dup(acl)); 8862306a36Sopenharmony_ci if (!is_uncached_acl(old)) 8962306a36Sopenharmony_ci posix_acl_release(old); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ciEXPORT_SYMBOL(set_cached_acl); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic void __forget_cached_acl(struct posix_acl **p) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci struct posix_acl *old; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci old = xchg(p, ACL_NOT_CACHED); 9862306a36Sopenharmony_ci if (!is_uncached_acl(old)) 9962306a36Sopenharmony_ci posix_acl_release(old); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_civoid forget_cached_acl(struct inode *inode, int type) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci __forget_cached_acl(acl_by_type(inode, type)); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ciEXPORT_SYMBOL(forget_cached_acl); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_civoid forget_all_cached_acls(struct inode *inode) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci __forget_cached_acl(&inode->i_acl); 11162306a36Sopenharmony_ci __forget_cached_acl(&inode->i_default_acl); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ciEXPORT_SYMBOL(forget_all_cached_acls); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic struct posix_acl *__get_acl(struct mnt_idmap *idmap, 11662306a36Sopenharmony_ci struct dentry *dentry, struct inode *inode, 11762306a36Sopenharmony_ci int type) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct posix_acl *sentinel; 12062306a36Sopenharmony_ci struct posix_acl **p; 12162306a36Sopenharmony_ci struct posix_acl *acl; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* 12462306a36Sopenharmony_ci * The sentinel is used to detect when another operation like 12562306a36Sopenharmony_ci * set_cached_acl() or forget_cached_acl() races with get_inode_acl(). 12662306a36Sopenharmony_ci * It is guaranteed that is_uncached_acl(sentinel) is true. 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci acl = get_cached_acl(inode, type); 13062306a36Sopenharmony_ci if (!is_uncached_acl(acl)) 13162306a36Sopenharmony_ci return acl; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (!IS_POSIXACL(inode)) 13462306a36Sopenharmony_ci return NULL; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci sentinel = uncached_acl_sentinel(current); 13762306a36Sopenharmony_ci p = acl_by_type(inode, type); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* 14062306a36Sopenharmony_ci * If the ACL isn't being read yet, set our sentinel. Otherwise, the 14162306a36Sopenharmony_ci * current value of the ACL will not be ACL_NOT_CACHED and so our own 14262306a36Sopenharmony_ci * sentinel will not be set; another task will update the cache. We 14362306a36Sopenharmony_ci * could wait for that other task to complete its job, but it's easier 14462306a36Sopenharmony_ci * to just call ->get_inode_acl to fetch the ACL ourself. (This is 14562306a36Sopenharmony_ci * going to be an unlikely race.) 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ci cmpxchg(p, ACL_NOT_CACHED, sentinel); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* 15062306a36Sopenharmony_ci * Normally, the ACL returned by ->get{_inode}_acl will be cached. 15162306a36Sopenharmony_ci * A filesystem can prevent that by calling 15262306a36Sopenharmony_ci * forget_cached_acl(inode, type) in ->get{_inode}_acl. 15362306a36Sopenharmony_ci * 15462306a36Sopenharmony_ci * If the filesystem doesn't have a get{_inode}_ acl() function at all, 15562306a36Sopenharmony_ci * we'll just create the negative cache entry. 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_ci if (dentry && inode->i_op->get_acl) { 15862306a36Sopenharmony_ci acl = inode->i_op->get_acl(idmap, dentry, type); 15962306a36Sopenharmony_ci } else if (inode->i_op->get_inode_acl) { 16062306a36Sopenharmony_ci acl = inode->i_op->get_inode_acl(inode, type, false); 16162306a36Sopenharmony_ci } else { 16262306a36Sopenharmony_ci set_cached_acl(inode, type, NULL); 16362306a36Sopenharmony_ci return NULL; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci if (IS_ERR(acl)) { 16662306a36Sopenharmony_ci /* 16762306a36Sopenharmony_ci * Remove our sentinel so that we don't block future attempts 16862306a36Sopenharmony_ci * to cache the ACL. 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_ci cmpxchg(p, sentinel, ACL_NOT_CACHED); 17162306a36Sopenharmony_ci return acl; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* 17562306a36Sopenharmony_ci * Cache the result, but only if our sentinel is still in place. 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_ci posix_acl_dup(acl); 17862306a36Sopenharmony_ci if (unlikely(!try_cmpxchg(p, &sentinel, acl))) 17962306a36Sopenharmony_ci posix_acl_release(acl); 18062306a36Sopenharmony_ci return acl; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistruct posix_acl *get_inode_acl(struct inode *inode, int type) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci return __get_acl(&nop_mnt_idmap, NULL, inode, type); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ciEXPORT_SYMBOL(get_inode_acl); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/* 19062306a36Sopenharmony_ci * Init a fresh posix_acl 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_civoid 19362306a36Sopenharmony_ciposix_acl_init(struct posix_acl *acl, int count) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci refcount_set(&acl->a_refcount, 1); 19662306a36Sopenharmony_ci acl->a_count = count; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ciEXPORT_SYMBOL(posix_acl_init); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci/* 20162306a36Sopenharmony_ci * Allocate a new ACL with the specified number of entries. 20262306a36Sopenharmony_ci */ 20362306a36Sopenharmony_cistruct posix_acl * 20462306a36Sopenharmony_ciposix_acl_alloc(int count, gfp_t flags) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci const size_t size = sizeof(struct posix_acl) + 20762306a36Sopenharmony_ci count * sizeof(struct posix_acl_entry); 20862306a36Sopenharmony_ci struct posix_acl *acl = kmalloc(size, flags); 20962306a36Sopenharmony_ci if (acl) 21062306a36Sopenharmony_ci posix_acl_init(acl, count); 21162306a36Sopenharmony_ci return acl; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ciEXPORT_SYMBOL(posix_acl_alloc); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci/* 21662306a36Sopenharmony_ci * Clone an ACL. 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_cistruct posix_acl * 21962306a36Sopenharmony_ciposix_acl_clone(const struct posix_acl *acl, gfp_t flags) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci struct posix_acl *clone = NULL; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (acl) { 22462306a36Sopenharmony_ci int size = sizeof(struct posix_acl) + acl->a_count * 22562306a36Sopenharmony_ci sizeof(struct posix_acl_entry); 22662306a36Sopenharmony_ci clone = kmemdup(acl, size, flags); 22762306a36Sopenharmony_ci if (clone) 22862306a36Sopenharmony_ci refcount_set(&clone->a_refcount, 1); 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci return clone; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(posix_acl_clone); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci/* 23562306a36Sopenharmony_ci * Check if an acl is valid. Returns 0 if it is, or -E... otherwise. 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_ciint 23862306a36Sopenharmony_ciposix_acl_valid(struct user_namespace *user_ns, const struct posix_acl *acl) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci const struct posix_acl_entry *pa, *pe; 24162306a36Sopenharmony_ci int state = ACL_USER_OBJ; 24262306a36Sopenharmony_ci int needs_mask = 0; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci FOREACH_ACL_ENTRY(pa, acl, pe) { 24562306a36Sopenharmony_ci if (pa->e_perm & ~(ACL_READ|ACL_WRITE|ACL_EXECUTE)) 24662306a36Sopenharmony_ci return -EINVAL; 24762306a36Sopenharmony_ci switch (pa->e_tag) { 24862306a36Sopenharmony_ci case ACL_USER_OBJ: 24962306a36Sopenharmony_ci if (state == ACL_USER_OBJ) { 25062306a36Sopenharmony_ci state = ACL_USER; 25162306a36Sopenharmony_ci break; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci return -EINVAL; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci case ACL_USER: 25662306a36Sopenharmony_ci if (state != ACL_USER) 25762306a36Sopenharmony_ci return -EINVAL; 25862306a36Sopenharmony_ci if (!kuid_has_mapping(user_ns, pa->e_uid)) 25962306a36Sopenharmony_ci return -EINVAL; 26062306a36Sopenharmony_ci needs_mask = 1; 26162306a36Sopenharmony_ci break; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci case ACL_GROUP_OBJ: 26462306a36Sopenharmony_ci if (state == ACL_USER) { 26562306a36Sopenharmony_ci state = ACL_GROUP; 26662306a36Sopenharmony_ci break; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci return -EINVAL; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci case ACL_GROUP: 27162306a36Sopenharmony_ci if (state != ACL_GROUP) 27262306a36Sopenharmony_ci return -EINVAL; 27362306a36Sopenharmony_ci if (!kgid_has_mapping(user_ns, pa->e_gid)) 27462306a36Sopenharmony_ci return -EINVAL; 27562306a36Sopenharmony_ci needs_mask = 1; 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci case ACL_MASK: 27962306a36Sopenharmony_ci if (state != ACL_GROUP) 28062306a36Sopenharmony_ci return -EINVAL; 28162306a36Sopenharmony_ci state = ACL_OTHER; 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci case ACL_OTHER: 28562306a36Sopenharmony_ci if (state == ACL_OTHER || 28662306a36Sopenharmony_ci (state == ACL_GROUP && !needs_mask)) { 28762306a36Sopenharmony_ci state = 0; 28862306a36Sopenharmony_ci break; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci return -EINVAL; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci default: 29362306a36Sopenharmony_ci return -EINVAL; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci if (state == 0) 29762306a36Sopenharmony_ci return 0; 29862306a36Sopenharmony_ci return -EINVAL; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ciEXPORT_SYMBOL(posix_acl_valid); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci/* 30362306a36Sopenharmony_ci * Returns 0 if the acl can be exactly represented in the traditional 30462306a36Sopenharmony_ci * file mode permission bits, or else 1. Returns -E... on error. 30562306a36Sopenharmony_ci */ 30662306a36Sopenharmony_ciint 30762306a36Sopenharmony_ciposix_acl_equiv_mode(const struct posix_acl *acl, umode_t *mode_p) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci const struct posix_acl_entry *pa, *pe; 31062306a36Sopenharmony_ci umode_t mode = 0; 31162306a36Sopenharmony_ci int not_equiv = 0; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* 31462306a36Sopenharmony_ci * A null ACL can always be presented as mode bits. 31562306a36Sopenharmony_ci */ 31662306a36Sopenharmony_ci if (!acl) 31762306a36Sopenharmony_ci return 0; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci FOREACH_ACL_ENTRY(pa, acl, pe) { 32062306a36Sopenharmony_ci switch (pa->e_tag) { 32162306a36Sopenharmony_ci case ACL_USER_OBJ: 32262306a36Sopenharmony_ci mode |= (pa->e_perm & S_IRWXO) << 6; 32362306a36Sopenharmony_ci break; 32462306a36Sopenharmony_ci case ACL_GROUP_OBJ: 32562306a36Sopenharmony_ci mode |= (pa->e_perm & S_IRWXO) << 3; 32662306a36Sopenharmony_ci break; 32762306a36Sopenharmony_ci case ACL_OTHER: 32862306a36Sopenharmony_ci mode |= pa->e_perm & S_IRWXO; 32962306a36Sopenharmony_ci break; 33062306a36Sopenharmony_ci case ACL_MASK: 33162306a36Sopenharmony_ci mode = (mode & ~S_IRWXG) | 33262306a36Sopenharmony_ci ((pa->e_perm & S_IRWXO) << 3); 33362306a36Sopenharmony_ci not_equiv = 1; 33462306a36Sopenharmony_ci break; 33562306a36Sopenharmony_ci case ACL_USER: 33662306a36Sopenharmony_ci case ACL_GROUP: 33762306a36Sopenharmony_ci not_equiv = 1; 33862306a36Sopenharmony_ci break; 33962306a36Sopenharmony_ci default: 34062306a36Sopenharmony_ci return -EINVAL; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci if (mode_p) 34462306a36Sopenharmony_ci *mode_p = (*mode_p & ~S_IRWXUGO) | mode; 34562306a36Sopenharmony_ci return not_equiv; 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ciEXPORT_SYMBOL(posix_acl_equiv_mode); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci/* 35062306a36Sopenharmony_ci * Create an ACL representing the file mode permission bits of an inode. 35162306a36Sopenharmony_ci */ 35262306a36Sopenharmony_cistruct posix_acl * 35362306a36Sopenharmony_ciposix_acl_from_mode(umode_t mode, gfp_t flags) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci struct posix_acl *acl = posix_acl_alloc(3, flags); 35662306a36Sopenharmony_ci if (!acl) 35762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci acl->a_entries[0].e_tag = ACL_USER_OBJ; 36062306a36Sopenharmony_ci acl->a_entries[0].e_perm = (mode & S_IRWXU) >> 6; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci acl->a_entries[1].e_tag = ACL_GROUP_OBJ; 36362306a36Sopenharmony_ci acl->a_entries[1].e_perm = (mode & S_IRWXG) >> 3; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci acl->a_entries[2].e_tag = ACL_OTHER; 36662306a36Sopenharmony_ci acl->a_entries[2].e_perm = (mode & S_IRWXO); 36762306a36Sopenharmony_ci return acl; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ciEXPORT_SYMBOL(posix_acl_from_mode); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci/* 37262306a36Sopenharmony_ci * Return 0 if current is granted want access to the inode 37362306a36Sopenharmony_ci * by the acl. Returns -E... otherwise. 37462306a36Sopenharmony_ci */ 37562306a36Sopenharmony_ciint 37662306a36Sopenharmony_ciposix_acl_permission(struct mnt_idmap *idmap, struct inode *inode, 37762306a36Sopenharmony_ci const struct posix_acl *acl, int want) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci const struct posix_acl_entry *pa, *pe, *mask_obj; 38062306a36Sopenharmony_ci struct user_namespace *fs_userns = i_user_ns(inode); 38162306a36Sopenharmony_ci int found = 0; 38262306a36Sopenharmony_ci vfsuid_t vfsuid; 38362306a36Sopenharmony_ci vfsgid_t vfsgid; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci want &= MAY_READ | MAY_WRITE | MAY_EXEC; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci FOREACH_ACL_ENTRY(pa, acl, pe) { 38862306a36Sopenharmony_ci switch(pa->e_tag) { 38962306a36Sopenharmony_ci case ACL_USER_OBJ: 39062306a36Sopenharmony_ci /* (May have been checked already) */ 39162306a36Sopenharmony_ci vfsuid = i_uid_into_vfsuid(idmap, inode); 39262306a36Sopenharmony_ci if (vfsuid_eq_kuid(vfsuid, current_fsuid())) 39362306a36Sopenharmony_ci goto check_perm; 39462306a36Sopenharmony_ci break; 39562306a36Sopenharmony_ci case ACL_USER: 39662306a36Sopenharmony_ci vfsuid = make_vfsuid(idmap, fs_userns, 39762306a36Sopenharmony_ci pa->e_uid); 39862306a36Sopenharmony_ci if (vfsuid_eq_kuid(vfsuid, current_fsuid())) 39962306a36Sopenharmony_ci goto mask; 40062306a36Sopenharmony_ci break; 40162306a36Sopenharmony_ci case ACL_GROUP_OBJ: 40262306a36Sopenharmony_ci vfsgid = i_gid_into_vfsgid(idmap, inode); 40362306a36Sopenharmony_ci if (vfsgid_in_group_p(vfsgid)) { 40462306a36Sopenharmony_ci found = 1; 40562306a36Sopenharmony_ci if ((pa->e_perm & want) == want) 40662306a36Sopenharmony_ci goto mask; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci break; 40962306a36Sopenharmony_ci case ACL_GROUP: 41062306a36Sopenharmony_ci vfsgid = make_vfsgid(idmap, fs_userns, 41162306a36Sopenharmony_ci pa->e_gid); 41262306a36Sopenharmony_ci if (vfsgid_in_group_p(vfsgid)) { 41362306a36Sopenharmony_ci found = 1; 41462306a36Sopenharmony_ci if ((pa->e_perm & want) == want) 41562306a36Sopenharmony_ci goto mask; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci break; 41862306a36Sopenharmony_ci case ACL_MASK: 41962306a36Sopenharmony_ci break; 42062306a36Sopenharmony_ci case ACL_OTHER: 42162306a36Sopenharmony_ci if (found) 42262306a36Sopenharmony_ci return -EACCES; 42362306a36Sopenharmony_ci else 42462306a36Sopenharmony_ci goto check_perm; 42562306a36Sopenharmony_ci default: 42662306a36Sopenharmony_ci return -EIO; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci return -EIO; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cimask: 43262306a36Sopenharmony_ci for (mask_obj = pa+1; mask_obj != pe; mask_obj++) { 43362306a36Sopenharmony_ci if (mask_obj->e_tag == ACL_MASK) { 43462306a36Sopenharmony_ci if ((pa->e_perm & mask_obj->e_perm & want) == want) 43562306a36Sopenharmony_ci return 0; 43662306a36Sopenharmony_ci return -EACCES; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cicheck_perm: 44162306a36Sopenharmony_ci if ((pa->e_perm & want) == want) 44262306a36Sopenharmony_ci return 0; 44362306a36Sopenharmony_ci return -EACCES; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci/* 44762306a36Sopenharmony_ci * Modify acl when creating a new inode. The caller must ensure the acl is 44862306a36Sopenharmony_ci * only referenced once. 44962306a36Sopenharmony_ci * 45062306a36Sopenharmony_ci * mode_p initially must contain the mode parameter to the open() / creat() 45162306a36Sopenharmony_ci * system calls. All permissions that are not granted by the acl are removed. 45262306a36Sopenharmony_ci * The permissions in the acl are changed to reflect the mode_p parameter. 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_cistatic int posix_acl_create_masq(struct posix_acl *acl, umode_t *mode_p) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct posix_acl_entry *pa, *pe; 45762306a36Sopenharmony_ci struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL; 45862306a36Sopenharmony_ci umode_t mode = *mode_p; 45962306a36Sopenharmony_ci int not_equiv = 0; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* assert(atomic_read(acl->a_refcount) == 1); */ 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci FOREACH_ACL_ENTRY(pa, acl, pe) { 46462306a36Sopenharmony_ci switch(pa->e_tag) { 46562306a36Sopenharmony_ci case ACL_USER_OBJ: 46662306a36Sopenharmony_ci pa->e_perm &= (mode >> 6) | ~S_IRWXO; 46762306a36Sopenharmony_ci mode &= (pa->e_perm << 6) | ~S_IRWXU; 46862306a36Sopenharmony_ci break; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci case ACL_USER: 47162306a36Sopenharmony_ci case ACL_GROUP: 47262306a36Sopenharmony_ci not_equiv = 1; 47362306a36Sopenharmony_ci break; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci case ACL_GROUP_OBJ: 47662306a36Sopenharmony_ci group_obj = pa; 47762306a36Sopenharmony_ci break; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci case ACL_OTHER: 48062306a36Sopenharmony_ci pa->e_perm &= mode | ~S_IRWXO; 48162306a36Sopenharmony_ci mode &= pa->e_perm | ~S_IRWXO; 48262306a36Sopenharmony_ci break; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci case ACL_MASK: 48562306a36Sopenharmony_ci mask_obj = pa; 48662306a36Sopenharmony_ci not_equiv = 1; 48762306a36Sopenharmony_ci break; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci default: 49062306a36Sopenharmony_ci return -EIO; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (mask_obj) { 49562306a36Sopenharmony_ci mask_obj->e_perm &= (mode >> 3) | ~S_IRWXO; 49662306a36Sopenharmony_ci mode &= (mask_obj->e_perm << 3) | ~S_IRWXG; 49762306a36Sopenharmony_ci } else { 49862306a36Sopenharmony_ci if (!group_obj) 49962306a36Sopenharmony_ci return -EIO; 50062306a36Sopenharmony_ci group_obj->e_perm &= (mode >> 3) | ~S_IRWXO; 50162306a36Sopenharmony_ci mode &= (group_obj->e_perm << 3) | ~S_IRWXG; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci *mode_p = (*mode_p & ~S_IRWXUGO) | mode; 50562306a36Sopenharmony_ci return not_equiv; 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci/* 50962306a36Sopenharmony_ci * Modify the ACL for the chmod syscall. 51062306a36Sopenharmony_ci */ 51162306a36Sopenharmony_cistatic int __posix_acl_chmod_masq(struct posix_acl *acl, umode_t mode) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL; 51462306a36Sopenharmony_ci struct posix_acl_entry *pa, *pe; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* assert(atomic_read(acl->a_refcount) == 1); */ 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci FOREACH_ACL_ENTRY(pa, acl, pe) { 51962306a36Sopenharmony_ci switch(pa->e_tag) { 52062306a36Sopenharmony_ci case ACL_USER_OBJ: 52162306a36Sopenharmony_ci pa->e_perm = (mode & S_IRWXU) >> 6; 52262306a36Sopenharmony_ci break; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci case ACL_USER: 52562306a36Sopenharmony_ci case ACL_GROUP: 52662306a36Sopenharmony_ci break; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci case ACL_GROUP_OBJ: 52962306a36Sopenharmony_ci group_obj = pa; 53062306a36Sopenharmony_ci break; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci case ACL_MASK: 53362306a36Sopenharmony_ci mask_obj = pa; 53462306a36Sopenharmony_ci break; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci case ACL_OTHER: 53762306a36Sopenharmony_ci pa->e_perm = (mode & S_IRWXO); 53862306a36Sopenharmony_ci break; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci default: 54162306a36Sopenharmony_ci return -EIO; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci if (mask_obj) { 54662306a36Sopenharmony_ci mask_obj->e_perm = (mode & S_IRWXG) >> 3; 54762306a36Sopenharmony_ci } else { 54862306a36Sopenharmony_ci if (!group_obj) 54962306a36Sopenharmony_ci return -EIO; 55062306a36Sopenharmony_ci group_obj->e_perm = (mode & S_IRWXG) >> 3; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci return 0; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ciint 55762306a36Sopenharmony_ci__posix_acl_create(struct posix_acl **acl, gfp_t gfp, umode_t *mode_p) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci struct posix_acl *clone = posix_acl_clone(*acl, gfp); 56062306a36Sopenharmony_ci int err = -ENOMEM; 56162306a36Sopenharmony_ci if (clone) { 56262306a36Sopenharmony_ci err = posix_acl_create_masq(clone, mode_p); 56362306a36Sopenharmony_ci if (err < 0) { 56462306a36Sopenharmony_ci posix_acl_release(clone); 56562306a36Sopenharmony_ci clone = NULL; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci posix_acl_release(*acl); 56962306a36Sopenharmony_ci *acl = clone; 57062306a36Sopenharmony_ci return err; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ciEXPORT_SYMBOL(__posix_acl_create); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ciint 57562306a36Sopenharmony_ci__posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci struct posix_acl *clone = posix_acl_clone(*acl, gfp); 57862306a36Sopenharmony_ci int err = -ENOMEM; 57962306a36Sopenharmony_ci if (clone) { 58062306a36Sopenharmony_ci err = __posix_acl_chmod_masq(clone, mode); 58162306a36Sopenharmony_ci if (err) { 58262306a36Sopenharmony_ci posix_acl_release(clone); 58362306a36Sopenharmony_ci clone = NULL; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci posix_acl_release(*acl); 58762306a36Sopenharmony_ci *acl = clone; 58862306a36Sopenharmony_ci return err; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ciEXPORT_SYMBOL(__posix_acl_chmod); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci/** 59362306a36Sopenharmony_ci * posix_acl_chmod - chmod a posix acl 59462306a36Sopenharmony_ci * 59562306a36Sopenharmony_ci * @idmap: idmap of the mount @inode was found from 59662306a36Sopenharmony_ci * @dentry: dentry to check permissions on 59762306a36Sopenharmony_ci * @mode: the new mode of @inode 59862306a36Sopenharmony_ci * 59962306a36Sopenharmony_ci * If the dentry has been found through an idmapped mount the idmap of 60062306a36Sopenharmony_ci * the vfsmount must be passed through @idmap. This function will then 60162306a36Sopenharmony_ci * take care to map the inode according to @idmap before checking 60262306a36Sopenharmony_ci * permissions. On non-idmapped mounts or if permission checking is to be 60362306a36Sopenharmony_ci * performed on the raw inode simply passs @nop_mnt_idmap. 60462306a36Sopenharmony_ci */ 60562306a36Sopenharmony_ciint 60662306a36Sopenharmony_ci posix_acl_chmod(struct mnt_idmap *idmap, struct dentry *dentry, 60762306a36Sopenharmony_ci umode_t mode) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 61062306a36Sopenharmony_ci struct posix_acl *acl; 61162306a36Sopenharmony_ci int ret = 0; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (!IS_POSIXACL(inode)) 61462306a36Sopenharmony_ci return 0; 61562306a36Sopenharmony_ci if (!inode->i_op->set_acl) 61662306a36Sopenharmony_ci return -EOPNOTSUPP; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci acl = get_inode_acl(inode, ACL_TYPE_ACCESS); 61962306a36Sopenharmony_ci if (IS_ERR_OR_NULL(acl)) { 62062306a36Sopenharmony_ci if (acl == ERR_PTR(-EOPNOTSUPP)) 62162306a36Sopenharmony_ci return 0; 62262306a36Sopenharmony_ci return PTR_ERR(acl); 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci ret = __posix_acl_chmod(&acl, GFP_KERNEL, mode); 62662306a36Sopenharmony_ci if (ret) 62762306a36Sopenharmony_ci return ret; 62862306a36Sopenharmony_ci ret = inode->i_op->set_acl(idmap, dentry, acl, ACL_TYPE_ACCESS); 62962306a36Sopenharmony_ci posix_acl_release(acl); 63062306a36Sopenharmony_ci return ret; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ciEXPORT_SYMBOL(posix_acl_chmod); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ciint 63562306a36Sopenharmony_ciposix_acl_create(struct inode *dir, umode_t *mode, 63662306a36Sopenharmony_ci struct posix_acl **default_acl, struct posix_acl **acl) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci struct posix_acl *p; 63962306a36Sopenharmony_ci struct posix_acl *clone; 64062306a36Sopenharmony_ci int ret; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci *acl = NULL; 64362306a36Sopenharmony_ci *default_acl = NULL; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (S_ISLNK(*mode) || !IS_POSIXACL(dir)) 64662306a36Sopenharmony_ci return 0; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci p = get_inode_acl(dir, ACL_TYPE_DEFAULT); 64962306a36Sopenharmony_ci if (!p || p == ERR_PTR(-EOPNOTSUPP)) { 65062306a36Sopenharmony_ci *mode &= ~current_umask(); 65162306a36Sopenharmony_ci return 0; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci if (IS_ERR(p)) 65462306a36Sopenharmony_ci return PTR_ERR(p); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci ret = -ENOMEM; 65762306a36Sopenharmony_ci clone = posix_acl_clone(p, GFP_NOFS); 65862306a36Sopenharmony_ci if (!clone) 65962306a36Sopenharmony_ci goto err_release; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci ret = posix_acl_create_masq(clone, mode); 66262306a36Sopenharmony_ci if (ret < 0) 66362306a36Sopenharmony_ci goto err_release_clone; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if (ret == 0) 66662306a36Sopenharmony_ci posix_acl_release(clone); 66762306a36Sopenharmony_ci else 66862306a36Sopenharmony_ci *acl = clone; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci if (!S_ISDIR(*mode)) 67162306a36Sopenharmony_ci posix_acl_release(p); 67262306a36Sopenharmony_ci else 67362306a36Sopenharmony_ci *default_acl = p; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci return 0; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cierr_release_clone: 67862306a36Sopenharmony_ci posix_acl_release(clone); 67962306a36Sopenharmony_cierr_release: 68062306a36Sopenharmony_ci posix_acl_release(p); 68162306a36Sopenharmony_ci return ret; 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(posix_acl_create); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci/** 68662306a36Sopenharmony_ci * posix_acl_update_mode - update mode in set_acl 68762306a36Sopenharmony_ci * @idmap: idmap of the mount @inode was found from 68862306a36Sopenharmony_ci * @inode: target inode 68962306a36Sopenharmony_ci * @mode_p: mode (pointer) for update 69062306a36Sopenharmony_ci * @acl: acl pointer 69162306a36Sopenharmony_ci * 69262306a36Sopenharmony_ci * Update the file mode when setting an ACL: compute the new file permission 69362306a36Sopenharmony_ci * bits based on the ACL. In addition, if the ACL is equivalent to the new 69462306a36Sopenharmony_ci * file mode, set *@acl to NULL to indicate that no ACL should be set. 69562306a36Sopenharmony_ci * 69662306a36Sopenharmony_ci * As with chmod, clear the setgid bit if the caller is not in the owning group 69762306a36Sopenharmony_ci * or capable of CAP_FSETID (see inode_change_ok). 69862306a36Sopenharmony_ci * 69962306a36Sopenharmony_ci * If the inode has been found through an idmapped mount the idmap of 70062306a36Sopenharmony_ci * the vfsmount must be passed through @idmap. This function will then 70162306a36Sopenharmony_ci * take care to map the inode according to @idmap before checking 70262306a36Sopenharmony_ci * permissions. On non-idmapped mounts or if permission checking is to be 70362306a36Sopenharmony_ci * performed on the raw inode simply passs @nop_mnt_idmap. 70462306a36Sopenharmony_ci * 70562306a36Sopenharmony_ci * Called from set_acl inode operations. 70662306a36Sopenharmony_ci */ 70762306a36Sopenharmony_ciint posix_acl_update_mode(struct mnt_idmap *idmap, 70862306a36Sopenharmony_ci struct inode *inode, umode_t *mode_p, 70962306a36Sopenharmony_ci struct posix_acl **acl) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci umode_t mode = inode->i_mode; 71262306a36Sopenharmony_ci int error; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci error = posix_acl_equiv_mode(*acl, &mode); 71562306a36Sopenharmony_ci if (error < 0) 71662306a36Sopenharmony_ci return error; 71762306a36Sopenharmony_ci if (error == 0) 71862306a36Sopenharmony_ci *acl = NULL; 71962306a36Sopenharmony_ci if (!vfsgid_in_group_p(i_gid_into_vfsgid(idmap, inode)) && 72062306a36Sopenharmony_ci !capable_wrt_inode_uidgid(idmap, inode, CAP_FSETID)) 72162306a36Sopenharmony_ci mode &= ~S_ISGID; 72262306a36Sopenharmony_ci *mode_p = mode; 72362306a36Sopenharmony_ci return 0; 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ciEXPORT_SYMBOL(posix_acl_update_mode); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci/* 72862306a36Sopenharmony_ci * Fix up the uids and gids in posix acl extended attributes in place. 72962306a36Sopenharmony_ci */ 73062306a36Sopenharmony_cistatic int posix_acl_fix_xattr_common(const void *value, size_t size) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci const struct posix_acl_xattr_header *header = value; 73362306a36Sopenharmony_ci int count; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if (!header) 73662306a36Sopenharmony_ci return -EINVAL; 73762306a36Sopenharmony_ci if (size < sizeof(struct posix_acl_xattr_header)) 73862306a36Sopenharmony_ci return -EINVAL; 73962306a36Sopenharmony_ci if (header->a_version != cpu_to_le32(POSIX_ACL_XATTR_VERSION)) 74062306a36Sopenharmony_ci return -EOPNOTSUPP; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci count = posix_acl_xattr_count(size); 74362306a36Sopenharmony_ci if (count < 0) 74462306a36Sopenharmony_ci return -EINVAL; 74562306a36Sopenharmony_ci if (count == 0) 74662306a36Sopenharmony_ci return 0; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci return count; 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci/** 75262306a36Sopenharmony_ci * posix_acl_from_xattr - convert POSIX ACLs from backing store to VFS format 75362306a36Sopenharmony_ci * @userns: the filesystem's idmapping 75462306a36Sopenharmony_ci * @value: the uapi representation of POSIX ACLs 75562306a36Sopenharmony_ci * @size: the size of @void 75662306a36Sopenharmony_ci * 75762306a36Sopenharmony_ci * Filesystems that store POSIX ACLs in the unaltered uapi format should use 75862306a36Sopenharmony_ci * posix_acl_from_xattr() when reading them from the backing store and 75962306a36Sopenharmony_ci * converting them into the struct posix_acl VFS format. The helper is 76062306a36Sopenharmony_ci * specifically intended to be called from the acl inode operation. 76162306a36Sopenharmony_ci * 76262306a36Sopenharmony_ci * The posix_acl_from_xattr() function will map the raw {g,u}id values stored 76362306a36Sopenharmony_ci * in ACL_{GROUP,USER} entries into idmapping in @userns. 76462306a36Sopenharmony_ci * 76562306a36Sopenharmony_ci * Note that posix_acl_from_xattr() does not take idmapped mounts into account. 76662306a36Sopenharmony_ci * If it did it calling it from the get acl inode operation would return POSIX 76762306a36Sopenharmony_ci * ACLs mapped according to an idmapped mount which would mean that the value 76862306a36Sopenharmony_ci * couldn't be cached for the filesystem. Idmapped mounts are taken into 76962306a36Sopenharmony_ci * account on the fly during permission checking or right at the VFS - 77062306a36Sopenharmony_ci * userspace boundary before reporting them to the user. 77162306a36Sopenharmony_ci * 77262306a36Sopenharmony_ci * Return: Allocated struct posix_acl on success, NULL for a valid header but 77362306a36Sopenharmony_ci * without actual POSIX ACL entries, or ERR_PTR() encoded error code. 77462306a36Sopenharmony_ci */ 77562306a36Sopenharmony_cistruct posix_acl *posix_acl_from_xattr(struct user_namespace *userns, 77662306a36Sopenharmony_ci const void *value, size_t size) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci const struct posix_acl_xattr_header *header = value; 77962306a36Sopenharmony_ci const struct posix_acl_xattr_entry *entry = (const void *)(header + 1), *end; 78062306a36Sopenharmony_ci int count; 78162306a36Sopenharmony_ci struct posix_acl *acl; 78262306a36Sopenharmony_ci struct posix_acl_entry *acl_e; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci count = posix_acl_fix_xattr_common(value, size); 78562306a36Sopenharmony_ci if (count < 0) 78662306a36Sopenharmony_ci return ERR_PTR(count); 78762306a36Sopenharmony_ci if (count == 0) 78862306a36Sopenharmony_ci return NULL; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci acl = posix_acl_alloc(count, GFP_NOFS); 79162306a36Sopenharmony_ci if (!acl) 79262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 79362306a36Sopenharmony_ci acl_e = acl->a_entries; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci for (end = entry + count; entry != end; acl_e++, entry++) { 79662306a36Sopenharmony_ci acl_e->e_tag = le16_to_cpu(entry->e_tag); 79762306a36Sopenharmony_ci acl_e->e_perm = le16_to_cpu(entry->e_perm); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci switch(acl_e->e_tag) { 80062306a36Sopenharmony_ci case ACL_USER_OBJ: 80162306a36Sopenharmony_ci case ACL_GROUP_OBJ: 80262306a36Sopenharmony_ci case ACL_MASK: 80362306a36Sopenharmony_ci case ACL_OTHER: 80462306a36Sopenharmony_ci break; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci case ACL_USER: 80762306a36Sopenharmony_ci acl_e->e_uid = make_kuid(userns, 80862306a36Sopenharmony_ci le32_to_cpu(entry->e_id)); 80962306a36Sopenharmony_ci if (!uid_valid(acl_e->e_uid)) 81062306a36Sopenharmony_ci goto fail; 81162306a36Sopenharmony_ci break; 81262306a36Sopenharmony_ci case ACL_GROUP: 81362306a36Sopenharmony_ci acl_e->e_gid = make_kgid(userns, 81462306a36Sopenharmony_ci le32_to_cpu(entry->e_id)); 81562306a36Sopenharmony_ci if (!gid_valid(acl_e->e_gid)) 81662306a36Sopenharmony_ci goto fail; 81762306a36Sopenharmony_ci break; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci default: 82062306a36Sopenharmony_ci goto fail; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci return acl; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cifail: 82662306a36Sopenharmony_ci posix_acl_release(acl); 82762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 82862306a36Sopenharmony_ci} 82962306a36Sopenharmony_ciEXPORT_SYMBOL (posix_acl_from_xattr); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci/* 83262306a36Sopenharmony_ci * Convert from in-memory to extended attribute representation. 83362306a36Sopenharmony_ci */ 83462306a36Sopenharmony_ciint 83562306a36Sopenharmony_ciposix_acl_to_xattr(struct user_namespace *user_ns, const struct posix_acl *acl, 83662306a36Sopenharmony_ci void *buffer, size_t size) 83762306a36Sopenharmony_ci{ 83862306a36Sopenharmony_ci struct posix_acl_xattr_header *ext_acl = buffer; 83962306a36Sopenharmony_ci struct posix_acl_xattr_entry *ext_entry; 84062306a36Sopenharmony_ci int real_size, n; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci real_size = posix_acl_xattr_size(acl->a_count); 84362306a36Sopenharmony_ci if (!buffer) 84462306a36Sopenharmony_ci return real_size; 84562306a36Sopenharmony_ci if (real_size > size) 84662306a36Sopenharmony_ci return -ERANGE; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci ext_entry = (void *)(ext_acl + 1); 84962306a36Sopenharmony_ci ext_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci for (n=0; n < acl->a_count; n++, ext_entry++) { 85262306a36Sopenharmony_ci const struct posix_acl_entry *acl_e = &acl->a_entries[n]; 85362306a36Sopenharmony_ci ext_entry->e_tag = cpu_to_le16(acl_e->e_tag); 85462306a36Sopenharmony_ci ext_entry->e_perm = cpu_to_le16(acl_e->e_perm); 85562306a36Sopenharmony_ci switch(acl_e->e_tag) { 85662306a36Sopenharmony_ci case ACL_USER: 85762306a36Sopenharmony_ci ext_entry->e_id = 85862306a36Sopenharmony_ci cpu_to_le32(from_kuid(user_ns, acl_e->e_uid)); 85962306a36Sopenharmony_ci break; 86062306a36Sopenharmony_ci case ACL_GROUP: 86162306a36Sopenharmony_ci ext_entry->e_id = 86262306a36Sopenharmony_ci cpu_to_le32(from_kgid(user_ns, acl_e->e_gid)); 86362306a36Sopenharmony_ci break; 86462306a36Sopenharmony_ci default: 86562306a36Sopenharmony_ci ext_entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID); 86662306a36Sopenharmony_ci break; 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci return real_size; 87062306a36Sopenharmony_ci} 87162306a36Sopenharmony_ciEXPORT_SYMBOL (posix_acl_to_xattr); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci/** 87462306a36Sopenharmony_ci * vfs_posix_acl_to_xattr - convert from kernel to userspace representation 87562306a36Sopenharmony_ci * @idmap: idmap of the mount 87662306a36Sopenharmony_ci * @inode: inode the posix acls are set on 87762306a36Sopenharmony_ci * @acl: the posix acls as represented by the vfs 87862306a36Sopenharmony_ci * @buffer: the buffer into which to convert @acl 87962306a36Sopenharmony_ci * @size: size of @buffer 88062306a36Sopenharmony_ci * 88162306a36Sopenharmony_ci * This converts @acl from the VFS representation in the filesystem idmapping 88262306a36Sopenharmony_ci * to the uapi form reportable to userspace. And mount and caller idmappings 88362306a36Sopenharmony_ci * are handled appropriately. 88462306a36Sopenharmony_ci * 88562306a36Sopenharmony_ci * Return: On success, the size of the stored uapi posix acls, on error a 88662306a36Sopenharmony_ci * negative errno. 88762306a36Sopenharmony_ci */ 88862306a36Sopenharmony_cistatic ssize_t vfs_posix_acl_to_xattr(struct mnt_idmap *idmap, 88962306a36Sopenharmony_ci struct inode *inode, 89062306a36Sopenharmony_ci const struct posix_acl *acl, void *buffer, 89162306a36Sopenharmony_ci size_t size) 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci{ 89462306a36Sopenharmony_ci struct posix_acl_xattr_header *ext_acl = buffer; 89562306a36Sopenharmony_ci struct posix_acl_xattr_entry *ext_entry; 89662306a36Sopenharmony_ci struct user_namespace *fs_userns, *caller_userns; 89762306a36Sopenharmony_ci ssize_t real_size, n; 89862306a36Sopenharmony_ci vfsuid_t vfsuid; 89962306a36Sopenharmony_ci vfsgid_t vfsgid; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci real_size = posix_acl_xattr_size(acl->a_count); 90262306a36Sopenharmony_ci if (!buffer) 90362306a36Sopenharmony_ci return real_size; 90462306a36Sopenharmony_ci if (real_size > size) 90562306a36Sopenharmony_ci return -ERANGE; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci ext_entry = (void *)(ext_acl + 1); 90862306a36Sopenharmony_ci ext_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci fs_userns = i_user_ns(inode); 91162306a36Sopenharmony_ci caller_userns = current_user_ns(); 91262306a36Sopenharmony_ci for (n=0; n < acl->a_count; n++, ext_entry++) { 91362306a36Sopenharmony_ci const struct posix_acl_entry *acl_e = &acl->a_entries[n]; 91462306a36Sopenharmony_ci ext_entry->e_tag = cpu_to_le16(acl_e->e_tag); 91562306a36Sopenharmony_ci ext_entry->e_perm = cpu_to_le16(acl_e->e_perm); 91662306a36Sopenharmony_ci switch(acl_e->e_tag) { 91762306a36Sopenharmony_ci case ACL_USER: 91862306a36Sopenharmony_ci vfsuid = make_vfsuid(idmap, fs_userns, acl_e->e_uid); 91962306a36Sopenharmony_ci ext_entry->e_id = cpu_to_le32(from_kuid( 92062306a36Sopenharmony_ci caller_userns, vfsuid_into_kuid(vfsuid))); 92162306a36Sopenharmony_ci break; 92262306a36Sopenharmony_ci case ACL_GROUP: 92362306a36Sopenharmony_ci vfsgid = make_vfsgid(idmap, fs_userns, acl_e->e_gid); 92462306a36Sopenharmony_ci ext_entry->e_id = cpu_to_le32(from_kgid( 92562306a36Sopenharmony_ci caller_userns, vfsgid_into_kgid(vfsgid))); 92662306a36Sopenharmony_ci break; 92762306a36Sopenharmony_ci default: 92862306a36Sopenharmony_ci ext_entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID); 92962306a36Sopenharmony_ci break; 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci } 93262306a36Sopenharmony_ci return real_size; 93362306a36Sopenharmony_ci} 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ciint 93662306a36Sopenharmony_ciset_posix_acl(struct mnt_idmap *idmap, struct dentry *dentry, 93762306a36Sopenharmony_ci int type, struct posix_acl *acl) 93862306a36Sopenharmony_ci{ 93962306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci if (!IS_POSIXACL(inode)) 94262306a36Sopenharmony_ci return -EOPNOTSUPP; 94362306a36Sopenharmony_ci if (!inode->i_op->set_acl) 94462306a36Sopenharmony_ci return -EOPNOTSUPP; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci if (type == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode)) 94762306a36Sopenharmony_ci return acl ? -EACCES : 0; 94862306a36Sopenharmony_ci if (!inode_owner_or_capable(idmap, inode)) 94962306a36Sopenharmony_ci return -EPERM; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci if (acl) { 95262306a36Sopenharmony_ci int ret = posix_acl_valid(inode->i_sb->s_user_ns, acl); 95362306a36Sopenharmony_ci if (ret) 95462306a36Sopenharmony_ci return ret; 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci return inode->i_op->set_acl(idmap, dentry, acl, type); 95762306a36Sopenharmony_ci} 95862306a36Sopenharmony_ciEXPORT_SYMBOL(set_posix_acl); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ciint posix_acl_listxattr(struct inode *inode, char **buffer, 96162306a36Sopenharmony_ci ssize_t *remaining_size) 96262306a36Sopenharmony_ci{ 96362306a36Sopenharmony_ci int err; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci if (!IS_POSIXACL(inode)) 96662306a36Sopenharmony_ci return 0; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci if (inode->i_acl) { 96962306a36Sopenharmony_ci err = xattr_list_one(buffer, remaining_size, 97062306a36Sopenharmony_ci XATTR_NAME_POSIX_ACL_ACCESS); 97162306a36Sopenharmony_ci if (err) 97262306a36Sopenharmony_ci return err; 97362306a36Sopenharmony_ci } 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci if (inode->i_default_acl) { 97662306a36Sopenharmony_ci err = xattr_list_one(buffer, remaining_size, 97762306a36Sopenharmony_ci XATTR_NAME_POSIX_ACL_DEFAULT); 97862306a36Sopenharmony_ci if (err) 97962306a36Sopenharmony_ci return err; 98062306a36Sopenharmony_ci } 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci return 0; 98362306a36Sopenharmony_ci} 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_cistatic bool 98662306a36Sopenharmony_ciposix_acl_xattr_list(struct dentry *dentry) 98762306a36Sopenharmony_ci{ 98862306a36Sopenharmony_ci return IS_POSIXACL(d_backing_inode(dentry)); 98962306a36Sopenharmony_ci} 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci/* 99262306a36Sopenharmony_ci * nop_posix_acl_access - legacy xattr handler for access POSIX ACLs 99362306a36Sopenharmony_ci * 99462306a36Sopenharmony_ci * This is the legacy POSIX ACL access xattr handler. It is used by some 99562306a36Sopenharmony_ci * filesystems to implement their ->listxattr() inode operation. New code 99662306a36Sopenharmony_ci * should never use them. 99762306a36Sopenharmony_ci */ 99862306a36Sopenharmony_ciconst struct xattr_handler nop_posix_acl_access = { 99962306a36Sopenharmony_ci .name = XATTR_NAME_POSIX_ACL_ACCESS, 100062306a36Sopenharmony_ci .list = posix_acl_xattr_list, 100162306a36Sopenharmony_ci}; 100262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nop_posix_acl_access); 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci/* 100562306a36Sopenharmony_ci * nop_posix_acl_default - legacy xattr handler for default POSIX ACLs 100662306a36Sopenharmony_ci * 100762306a36Sopenharmony_ci * This is the legacy POSIX ACL default xattr handler. It is used by some 100862306a36Sopenharmony_ci * filesystems to implement their ->listxattr() inode operation. New code 100962306a36Sopenharmony_ci * should never use them. 101062306a36Sopenharmony_ci */ 101162306a36Sopenharmony_ciconst struct xattr_handler nop_posix_acl_default = { 101262306a36Sopenharmony_ci .name = XATTR_NAME_POSIX_ACL_DEFAULT, 101362306a36Sopenharmony_ci .list = posix_acl_xattr_list, 101462306a36Sopenharmony_ci}; 101562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nop_posix_acl_default); 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ciint simple_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, 101862306a36Sopenharmony_ci struct posix_acl *acl, int type) 101962306a36Sopenharmony_ci{ 102062306a36Sopenharmony_ci int error; 102162306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci if (type == ACL_TYPE_ACCESS) { 102462306a36Sopenharmony_ci error = posix_acl_update_mode(idmap, inode, 102562306a36Sopenharmony_ci &inode->i_mode, &acl); 102662306a36Sopenharmony_ci if (error) 102762306a36Sopenharmony_ci return error; 102862306a36Sopenharmony_ci } 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci inode_set_ctime_current(inode); 103162306a36Sopenharmony_ci if (IS_I_VERSION(inode)) 103262306a36Sopenharmony_ci inode_inc_iversion(inode); 103362306a36Sopenharmony_ci set_cached_acl(inode, type, acl); 103462306a36Sopenharmony_ci return 0; 103562306a36Sopenharmony_ci} 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ciint simple_acl_create(struct inode *dir, struct inode *inode) 103862306a36Sopenharmony_ci{ 103962306a36Sopenharmony_ci struct posix_acl *default_acl, *acl; 104062306a36Sopenharmony_ci int error; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl); 104362306a36Sopenharmony_ci if (error) 104462306a36Sopenharmony_ci return error; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci set_cached_acl(inode, ACL_TYPE_DEFAULT, default_acl); 104762306a36Sopenharmony_ci set_cached_acl(inode, ACL_TYPE_ACCESS, acl); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci if (default_acl) 105062306a36Sopenharmony_ci posix_acl_release(default_acl); 105162306a36Sopenharmony_ci if (acl) 105262306a36Sopenharmony_ci posix_acl_release(acl); 105362306a36Sopenharmony_ci return 0; 105462306a36Sopenharmony_ci} 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_cistatic int vfs_set_acl_idmapped_mnt(struct mnt_idmap *idmap, 105762306a36Sopenharmony_ci struct user_namespace *fs_userns, 105862306a36Sopenharmony_ci struct posix_acl *acl) 105962306a36Sopenharmony_ci{ 106062306a36Sopenharmony_ci for (int n = 0; n < acl->a_count; n++) { 106162306a36Sopenharmony_ci struct posix_acl_entry *acl_e = &acl->a_entries[n]; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci switch (acl_e->e_tag) { 106462306a36Sopenharmony_ci case ACL_USER: 106562306a36Sopenharmony_ci acl_e->e_uid = from_vfsuid(idmap, fs_userns, 106662306a36Sopenharmony_ci VFSUIDT_INIT(acl_e->e_uid)); 106762306a36Sopenharmony_ci break; 106862306a36Sopenharmony_ci case ACL_GROUP: 106962306a36Sopenharmony_ci acl_e->e_gid = from_vfsgid(idmap, fs_userns, 107062306a36Sopenharmony_ci VFSGIDT_INIT(acl_e->e_gid)); 107162306a36Sopenharmony_ci break; 107262306a36Sopenharmony_ci } 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci return 0; 107662306a36Sopenharmony_ci} 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci/** 107962306a36Sopenharmony_ci * vfs_set_acl - set posix acls 108062306a36Sopenharmony_ci * @idmap: idmap of the mount 108162306a36Sopenharmony_ci * @dentry: the dentry based on which to set the posix acls 108262306a36Sopenharmony_ci * @acl_name: the name of the posix acl 108362306a36Sopenharmony_ci * @kacl: the posix acls in the appropriate VFS format 108462306a36Sopenharmony_ci * 108562306a36Sopenharmony_ci * This function sets @kacl. The caller must all posix_acl_release() on @kacl 108662306a36Sopenharmony_ci * afterwards. 108762306a36Sopenharmony_ci * 108862306a36Sopenharmony_ci * Return: On success 0, on error negative errno. 108962306a36Sopenharmony_ci */ 109062306a36Sopenharmony_ciint vfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, 109162306a36Sopenharmony_ci const char *acl_name, struct posix_acl *kacl) 109262306a36Sopenharmony_ci{ 109362306a36Sopenharmony_ci int acl_type; 109462306a36Sopenharmony_ci int error; 109562306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 109662306a36Sopenharmony_ci struct inode *delegated_inode = NULL; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci acl_type = posix_acl_type(acl_name); 109962306a36Sopenharmony_ci if (acl_type < 0) 110062306a36Sopenharmony_ci return -EINVAL; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci if (kacl) { 110362306a36Sopenharmony_ci /* 110462306a36Sopenharmony_ci * If we're on an idmapped mount translate from mount specific 110562306a36Sopenharmony_ci * vfs{g,u}id_t into global filesystem k{g,u}id_t. 110662306a36Sopenharmony_ci * Afterwards we can cache the POSIX ACLs filesystem wide and - 110762306a36Sopenharmony_ci * if this is a filesystem with a backing store - ultimately 110862306a36Sopenharmony_ci * translate them to backing store values. 110962306a36Sopenharmony_ci */ 111062306a36Sopenharmony_ci error = vfs_set_acl_idmapped_mnt(idmap, i_user_ns(inode), kacl); 111162306a36Sopenharmony_ci if (error) 111262306a36Sopenharmony_ci return error; 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ciretry_deleg: 111662306a36Sopenharmony_ci inode_lock(inode); 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci /* 111962306a36Sopenharmony_ci * We only care about restrictions the inode struct itself places upon 112062306a36Sopenharmony_ci * us otherwise POSIX ACLs aren't subject to any VFS restrictions. 112162306a36Sopenharmony_ci */ 112262306a36Sopenharmony_ci error = may_write_xattr(idmap, inode); 112362306a36Sopenharmony_ci if (error) 112462306a36Sopenharmony_ci goto out_inode_unlock; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci error = security_inode_set_acl(idmap, dentry, acl_name, kacl); 112762306a36Sopenharmony_ci if (error) 112862306a36Sopenharmony_ci goto out_inode_unlock; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci error = try_break_deleg(inode, &delegated_inode); 113162306a36Sopenharmony_ci if (error) 113262306a36Sopenharmony_ci goto out_inode_unlock; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci if (likely(!is_bad_inode(inode))) 113562306a36Sopenharmony_ci error = set_posix_acl(idmap, dentry, acl_type, kacl); 113662306a36Sopenharmony_ci else 113762306a36Sopenharmony_ci error = -EIO; 113862306a36Sopenharmony_ci if (!error) { 113962306a36Sopenharmony_ci fsnotify_xattr(dentry); 114062306a36Sopenharmony_ci evm_inode_post_set_acl(dentry, acl_name, kacl); 114162306a36Sopenharmony_ci } 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ciout_inode_unlock: 114462306a36Sopenharmony_ci inode_unlock(inode); 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci if (delegated_inode) { 114762306a36Sopenharmony_ci error = break_deleg_wait(&delegated_inode); 114862306a36Sopenharmony_ci if (!error) 114962306a36Sopenharmony_ci goto retry_deleg; 115062306a36Sopenharmony_ci } 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci return error; 115362306a36Sopenharmony_ci} 115462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vfs_set_acl); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci/** 115762306a36Sopenharmony_ci * vfs_get_acl - get posix acls 115862306a36Sopenharmony_ci * @idmap: idmap of the mount 115962306a36Sopenharmony_ci * @dentry: the dentry based on which to retrieve the posix acls 116062306a36Sopenharmony_ci * @acl_name: the name of the posix acl 116162306a36Sopenharmony_ci * 116262306a36Sopenharmony_ci * This function retrieves @kacl from the filesystem. The caller must all 116362306a36Sopenharmony_ci * posix_acl_release() on @kacl. 116462306a36Sopenharmony_ci * 116562306a36Sopenharmony_ci * Return: On success POSIX ACLs in VFS format, on error negative errno. 116662306a36Sopenharmony_ci */ 116762306a36Sopenharmony_cistruct posix_acl *vfs_get_acl(struct mnt_idmap *idmap, 116862306a36Sopenharmony_ci struct dentry *dentry, const char *acl_name) 116962306a36Sopenharmony_ci{ 117062306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 117162306a36Sopenharmony_ci struct posix_acl *acl; 117262306a36Sopenharmony_ci int acl_type, error; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci acl_type = posix_acl_type(acl_name); 117562306a36Sopenharmony_ci if (acl_type < 0) 117662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci /* 117962306a36Sopenharmony_ci * The VFS has no restrictions on reading POSIX ACLs so calling 118062306a36Sopenharmony_ci * something like xattr_permission() isn't needed. Only LSMs get a say. 118162306a36Sopenharmony_ci */ 118262306a36Sopenharmony_ci error = security_inode_get_acl(idmap, dentry, acl_name); 118362306a36Sopenharmony_ci if (error) 118462306a36Sopenharmony_ci return ERR_PTR(error); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci if (!IS_POSIXACL(inode)) 118762306a36Sopenharmony_ci return ERR_PTR(-EOPNOTSUPP); 118862306a36Sopenharmony_ci if (S_ISLNK(inode->i_mode)) 118962306a36Sopenharmony_ci return ERR_PTR(-EOPNOTSUPP); 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci acl = __get_acl(idmap, dentry, inode, acl_type); 119262306a36Sopenharmony_ci if (IS_ERR(acl)) 119362306a36Sopenharmony_ci return acl; 119462306a36Sopenharmony_ci if (!acl) 119562306a36Sopenharmony_ci return ERR_PTR(-ENODATA); 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci return acl; 119862306a36Sopenharmony_ci} 119962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vfs_get_acl); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci/** 120262306a36Sopenharmony_ci * vfs_remove_acl - remove posix acls 120362306a36Sopenharmony_ci * @idmap: idmap of the mount 120462306a36Sopenharmony_ci * @dentry: the dentry based on which to retrieve the posix acls 120562306a36Sopenharmony_ci * @acl_name: the name of the posix acl 120662306a36Sopenharmony_ci * 120762306a36Sopenharmony_ci * This function removes posix acls. 120862306a36Sopenharmony_ci * 120962306a36Sopenharmony_ci * Return: On success 0, on error negative errno. 121062306a36Sopenharmony_ci */ 121162306a36Sopenharmony_ciint vfs_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry, 121262306a36Sopenharmony_ci const char *acl_name) 121362306a36Sopenharmony_ci{ 121462306a36Sopenharmony_ci int acl_type; 121562306a36Sopenharmony_ci int error; 121662306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 121762306a36Sopenharmony_ci struct inode *delegated_inode = NULL; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci acl_type = posix_acl_type(acl_name); 122062306a36Sopenharmony_ci if (acl_type < 0) 122162306a36Sopenharmony_ci return -EINVAL; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ciretry_deleg: 122462306a36Sopenharmony_ci inode_lock(inode); 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci /* 122762306a36Sopenharmony_ci * We only care about restrictions the inode struct itself places upon 122862306a36Sopenharmony_ci * us otherwise POSIX ACLs aren't subject to any VFS restrictions. 122962306a36Sopenharmony_ci */ 123062306a36Sopenharmony_ci error = may_write_xattr(idmap, inode); 123162306a36Sopenharmony_ci if (error) 123262306a36Sopenharmony_ci goto out_inode_unlock; 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci error = security_inode_remove_acl(idmap, dentry, acl_name); 123562306a36Sopenharmony_ci if (error) 123662306a36Sopenharmony_ci goto out_inode_unlock; 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci error = try_break_deleg(inode, &delegated_inode); 123962306a36Sopenharmony_ci if (error) 124062306a36Sopenharmony_ci goto out_inode_unlock; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci if (likely(!is_bad_inode(inode))) 124362306a36Sopenharmony_ci error = set_posix_acl(idmap, dentry, acl_type, NULL); 124462306a36Sopenharmony_ci else 124562306a36Sopenharmony_ci error = -EIO; 124662306a36Sopenharmony_ci if (!error) { 124762306a36Sopenharmony_ci fsnotify_xattr(dentry); 124862306a36Sopenharmony_ci evm_inode_post_remove_acl(idmap, dentry, acl_name); 124962306a36Sopenharmony_ci } 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ciout_inode_unlock: 125262306a36Sopenharmony_ci inode_unlock(inode); 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci if (delegated_inode) { 125562306a36Sopenharmony_ci error = break_deleg_wait(&delegated_inode); 125662306a36Sopenharmony_ci if (!error) 125762306a36Sopenharmony_ci goto retry_deleg; 125862306a36Sopenharmony_ci } 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci return error; 126162306a36Sopenharmony_ci} 126262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vfs_remove_acl); 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ciint do_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, 126562306a36Sopenharmony_ci const char *acl_name, const void *kvalue, size_t size) 126662306a36Sopenharmony_ci{ 126762306a36Sopenharmony_ci int error; 126862306a36Sopenharmony_ci struct posix_acl *acl = NULL; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci if (size) { 127162306a36Sopenharmony_ci /* 127262306a36Sopenharmony_ci * Note that posix_acl_from_xattr() uses GFP_NOFS when it 127362306a36Sopenharmony_ci * probably doesn't need to here. 127462306a36Sopenharmony_ci */ 127562306a36Sopenharmony_ci acl = posix_acl_from_xattr(current_user_ns(), kvalue, size); 127662306a36Sopenharmony_ci if (IS_ERR(acl)) 127762306a36Sopenharmony_ci return PTR_ERR(acl); 127862306a36Sopenharmony_ci } 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci error = vfs_set_acl(idmap, dentry, acl_name, acl); 128162306a36Sopenharmony_ci posix_acl_release(acl); 128262306a36Sopenharmony_ci return error; 128362306a36Sopenharmony_ci} 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_cissize_t do_get_acl(struct mnt_idmap *idmap, struct dentry *dentry, 128662306a36Sopenharmony_ci const char *acl_name, void *kvalue, size_t size) 128762306a36Sopenharmony_ci{ 128862306a36Sopenharmony_ci ssize_t error; 128962306a36Sopenharmony_ci struct posix_acl *acl; 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci acl = vfs_get_acl(idmap, dentry, acl_name); 129262306a36Sopenharmony_ci if (IS_ERR(acl)) 129362306a36Sopenharmony_ci return PTR_ERR(acl); 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci error = vfs_posix_acl_to_xattr(idmap, d_inode(dentry), 129662306a36Sopenharmony_ci acl, kvalue, size); 129762306a36Sopenharmony_ci posix_acl_release(acl); 129862306a36Sopenharmony_ci return error; 129962306a36Sopenharmony_ci} 1300