18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/fs/ext4/acl.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/quotaops.h> 98c2ecf20Sopenharmony_ci#include "ext4_jbd2.h" 108c2ecf20Sopenharmony_ci#include "ext4.h" 118c2ecf20Sopenharmony_ci#include "xattr.h" 128c2ecf20Sopenharmony_ci#include "acl.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* 158c2ecf20Sopenharmony_ci * Convert from filesystem to in-memory representation. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_cistatic struct posix_acl * 188c2ecf20Sopenharmony_ciext4_acl_from_disk(const void *value, size_t size) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci const char *end = (char *)value + size; 218c2ecf20Sopenharmony_ci int n, count; 228c2ecf20Sopenharmony_ci struct posix_acl *acl; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci if (!value) 258c2ecf20Sopenharmony_ci return NULL; 268c2ecf20Sopenharmony_ci if (size < sizeof(ext4_acl_header)) 278c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 288c2ecf20Sopenharmony_ci if (((ext4_acl_header *)value)->a_version != 298c2ecf20Sopenharmony_ci cpu_to_le32(EXT4_ACL_VERSION)) 308c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 318c2ecf20Sopenharmony_ci value = (char *)value + sizeof(ext4_acl_header); 328c2ecf20Sopenharmony_ci count = ext4_acl_count(size); 338c2ecf20Sopenharmony_ci if (count < 0) 348c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 358c2ecf20Sopenharmony_ci if (count == 0) 368c2ecf20Sopenharmony_ci return NULL; 378c2ecf20Sopenharmony_ci acl = posix_acl_alloc(count, GFP_NOFS); 388c2ecf20Sopenharmony_ci if (!acl) 398c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 408c2ecf20Sopenharmony_ci for (n = 0; n < count; n++) { 418c2ecf20Sopenharmony_ci ext4_acl_entry *entry = 428c2ecf20Sopenharmony_ci (ext4_acl_entry *)value; 438c2ecf20Sopenharmony_ci if ((char *)value + sizeof(ext4_acl_entry_short) > end) 448c2ecf20Sopenharmony_ci goto fail; 458c2ecf20Sopenharmony_ci acl->a_entries[n].e_tag = le16_to_cpu(entry->e_tag); 468c2ecf20Sopenharmony_ci acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci switch (acl->a_entries[n].e_tag) { 498c2ecf20Sopenharmony_ci case ACL_USER_OBJ: 508c2ecf20Sopenharmony_ci case ACL_GROUP_OBJ: 518c2ecf20Sopenharmony_ci case ACL_MASK: 528c2ecf20Sopenharmony_ci case ACL_OTHER: 538c2ecf20Sopenharmony_ci value = (char *)value + 548c2ecf20Sopenharmony_ci sizeof(ext4_acl_entry_short); 558c2ecf20Sopenharmony_ci break; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci case ACL_USER: 588c2ecf20Sopenharmony_ci value = (char *)value + sizeof(ext4_acl_entry); 598c2ecf20Sopenharmony_ci if ((char *)value > end) 608c2ecf20Sopenharmony_ci goto fail; 618c2ecf20Sopenharmony_ci acl->a_entries[n].e_uid = 628c2ecf20Sopenharmony_ci make_kuid(&init_user_ns, 638c2ecf20Sopenharmony_ci le32_to_cpu(entry->e_id)); 648c2ecf20Sopenharmony_ci break; 658c2ecf20Sopenharmony_ci case ACL_GROUP: 668c2ecf20Sopenharmony_ci value = (char *)value + sizeof(ext4_acl_entry); 678c2ecf20Sopenharmony_ci if ((char *)value > end) 688c2ecf20Sopenharmony_ci goto fail; 698c2ecf20Sopenharmony_ci acl->a_entries[n].e_gid = 708c2ecf20Sopenharmony_ci make_kgid(&init_user_ns, 718c2ecf20Sopenharmony_ci le32_to_cpu(entry->e_id)); 728c2ecf20Sopenharmony_ci break; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci default: 758c2ecf20Sopenharmony_ci goto fail; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci if (value != end) 798c2ecf20Sopenharmony_ci goto fail; 808c2ecf20Sopenharmony_ci return acl; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cifail: 838c2ecf20Sopenharmony_ci posix_acl_release(acl); 848c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* 888c2ecf20Sopenharmony_ci * Convert from in-memory to filesystem representation. 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_cistatic void * 918c2ecf20Sopenharmony_ciext4_acl_to_disk(const struct posix_acl *acl, size_t *size) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci ext4_acl_header *ext_acl; 948c2ecf20Sopenharmony_ci char *e; 958c2ecf20Sopenharmony_ci size_t n; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci *size = ext4_acl_size(acl->a_count); 988c2ecf20Sopenharmony_ci ext_acl = kmalloc(sizeof(ext4_acl_header) + acl->a_count * 998c2ecf20Sopenharmony_ci sizeof(ext4_acl_entry), GFP_NOFS); 1008c2ecf20Sopenharmony_ci if (!ext_acl) 1018c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1028c2ecf20Sopenharmony_ci ext_acl->a_version = cpu_to_le32(EXT4_ACL_VERSION); 1038c2ecf20Sopenharmony_ci e = (char *)ext_acl + sizeof(ext4_acl_header); 1048c2ecf20Sopenharmony_ci for (n = 0; n < acl->a_count; n++) { 1058c2ecf20Sopenharmony_ci const struct posix_acl_entry *acl_e = &acl->a_entries[n]; 1068c2ecf20Sopenharmony_ci ext4_acl_entry *entry = (ext4_acl_entry *)e; 1078c2ecf20Sopenharmony_ci entry->e_tag = cpu_to_le16(acl_e->e_tag); 1088c2ecf20Sopenharmony_ci entry->e_perm = cpu_to_le16(acl_e->e_perm); 1098c2ecf20Sopenharmony_ci switch (acl_e->e_tag) { 1108c2ecf20Sopenharmony_ci case ACL_USER: 1118c2ecf20Sopenharmony_ci entry->e_id = cpu_to_le32( 1128c2ecf20Sopenharmony_ci from_kuid(&init_user_ns, acl_e->e_uid)); 1138c2ecf20Sopenharmony_ci e += sizeof(ext4_acl_entry); 1148c2ecf20Sopenharmony_ci break; 1158c2ecf20Sopenharmony_ci case ACL_GROUP: 1168c2ecf20Sopenharmony_ci entry->e_id = cpu_to_le32( 1178c2ecf20Sopenharmony_ci from_kgid(&init_user_ns, acl_e->e_gid)); 1188c2ecf20Sopenharmony_ci e += sizeof(ext4_acl_entry); 1198c2ecf20Sopenharmony_ci break; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci case ACL_USER_OBJ: 1228c2ecf20Sopenharmony_ci case ACL_GROUP_OBJ: 1238c2ecf20Sopenharmony_ci case ACL_MASK: 1248c2ecf20Sopenharmony_ci case ACL_OTHER: 1258c2ecf20Sopenharmony_ci e += sizeof(ext4_acl_entry_short); 1268c2ecf20Sopenharmony_ci break; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci default: 1298c2ecf20Sopenharmony_ci goto fail; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci return (char *)ext_acl; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cifail: 1358c2ecf20Sopenharmony_ci kfree(ext_acl); 1368c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci/* 1408c2ecf20Sopenharmony_ci * Inode operation get_posix_acl(). 1418c2ecf20Sopenharmony_ci * 1428c2ecf20Sopenharmony_ci * inode->i_mutex: don't care 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_cistruct posix_acl * 1458c2ecf20Sopenharmony_ciext4_get_acl(struct inode *inode, int type) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci int name_index; 1488c2ecf20Sopenharmony_ci char *value = NULL; 1498c2ecf20Sopenharmony_ci struct posix_acl *acl; 1508c2ecf20Sopenharmony_ci int retval; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci switch (type) { 1538c2ecf20Sopenharmony_ci case ACL_TYPE_ACCESS: 1548c2ecf20Sopenharmony_ci name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS; 1558c2ecf20Sopenharmony_ci break; 1568c2ecf20Sopenharmony_ci case ACL_TYPE_DEFAULT: 1578c2ecf20Sopenharmony_ci name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT; 1588c2ecf20Sopenharmony_ci break; 1598c2ecf20Sopenharmony_ci default: 1608c2ecf20Sopenharmony_ci BUG(); 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci retval = ext4_xattr_get(inode, name_index, "", NULL, 0); 1638c2ecf20Sopenharmony_ci if (retval > 0) { 1648c2ecf20Sopenharmony_ci value = kmalloc(retval, GFP_NOFS); 1658c2ecf20Sopenharmony_ci if (!value) 1668c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1678c2ecf20Sopenharmony_ci retval = ext4_xattr_get(inode, name_index, "", value, retval); 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci if (retval > 0) 1708c2ecf20Sopenharmony_ci acl = ext4_acl_from_disk(value, retval); 1718c2ecf20Sopenharmony_ci else if (retval == -ENODATA || retval == -ENOSYS) 1728c2ecf20Sopenharmony_ci acl = NULL; 1738c2ecf20Sopenharmony_ci else 1748c2ecf20Sopenharmony_ci acl = ERR_PTR(retval); 1758c2ecf20Sopenharmony_ci kfree(value); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci return acl; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/* 1818c2ecf20Sopenharmony_ci * Set the access or default ACL of an inode. 1828c2ecf20Sopenharmony_ci * 1838c2ecf20Sopenharmony_ci * inode->i_mutex: down unless called from ext4_new_inode 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_cistatic int 1868c2ecf20Sopenharmony_ci__ext4_set_acl(handle_t *handle, struct inode *inode, int type, 1878c2ecf20Sopenharmony_ci struct posix_acl *acl, int xattr_flags) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci int name_index; 1908c2ecf20Sopenharmony_ci void *value = NULL; 1918c2ecf20Sopenharmony_ci size_t size = 0; 1928c2ecf20Sopenharmony_ci int error; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci switch (type) { 1958c2ecf20Sopenharmony_ci case ACL_TYPE_ACCESS: 1968c2ecf20Sopenharmony_ci name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS; 1978c2ecf20Sopenharmony_ci break; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci case ACL_TYPE_DEFAULT: 2008c2ecf20Sopenharmony_ci name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT; 2018c2ecf20Sopenharmony_ci if (!S_ISDIR(inode->i_mode)) 2028c2ecf20Sopenharmony_ci return acl ? -EACCES : 0; 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci default: 2068c2ecf20Sopenharmony_ci return -EINVAL; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci if (acl) { 2098c2ecf20Sopenharmony_ci value = ext4_acl_to_disk(acl, &size); 2108c2ecf20Sopenharmony_ci if (IS_ERR(value)) 2118c2ecf20Sopenharmony_ci return (int)PTR_ERR(value); 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci error = ext4_xattr_set_handle(handle, inode, name_index, "", 2158c2ecf20Sopenharmony_ci value, size, xattr_flags); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci kfree(value); 2188c2ecf20Sopenharmony_ci if (!error) 2198c2ecf20Sopenharmony_ci set_cached_acl(inode, type, acl); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return error; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ciint 2258c2ecf20Sopenharmony_ciext4_set_acl(struct inode *inode, struct posix_acl *acl, int type) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci handle_t *handle; 2288c2ecf20Sopenharmony_ci int error, credits, retries = 0; 2298c2ecf20Sopenharmony_ci size_t acl_size = acl ? ext4_acl_size(acl->a_count) : 0; 2308c2ecf20Sopenharmony_ci umode_t mode = inode->i_mode; 2318c2ecf20Sopenharmony_ci int update_mode = 0; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci error = dquot_initialize(inode); 2348c2ecf20Sopenharmony_ci if (error) 2358c2ecf20Sopenharmony_ci return error; 2368c2ecf20Sopenharmony_ciretry: 2378c2ecf20Sopenharmony_ci error = ext4_xattr_set_credits(inode, acl_size, false /* is_create */, 2388c2ecf20Sopenharmony_ci &credits); 2398c2ecf20Sopenharmony_ci if (error) 2408c2ecf20Sopenharmony_ci return error; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci handle = ext4_journal_start(inode, EXT4_HT_XATTR, credits); 2438c2ecf20Sopenharmony_ci if (IS_ERR(handle)) 2448c2ecf20Sopenharmony_ci return PTR_ERR(handle); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if ((type == ACL_TYPE_ACCESS) && acl) { 2478c2ecf20Sopenharmony_ci error = posix_acl_update_mode(inode, &mode, &acl); 2488c2ecf20Sopenharmony_ci if (error) 2498c2ecf20Sopenharmony_ci goto out_stop; 2508c2ecf20Sopenharmony_ci if (mode != inode->i_mode) 2518c2ecf20Sopenharmony_ci update_mode = 1; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci error = __ext4_set_acl(handle, inode, type, acl, 0 /* xattr_flags */); 2558c2ecf20Sopenharmony_ci if (!error && update_mode) { 2568c2ecf20Sopenharmony_ci inode->i_mode = mode; 2578c2ecf20Sopenharmony_ci inode->i_ctime = current_time(inode); 2588c2ecf20Sopenharmony_ci error = ext4_mark_inode_dirty(handle, inode); 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ciout_stop: 2618c2ecf20Sopenharmony_ci ext4_journal_stop(handle); 2628c2ecf20Sopenharmony_ci if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries)) 2638c2ecf20Sopenharmony_ci goto retry; 2648c2ecf20Sopenharmony_ci return error; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci/* 2688c2ecf20Sopenharmony_ci * Initialize the ACLs of a new inode. Called from ext4_new_inode. 2698c2ecf20Sopenharmony_ci * 2708c2ecf20Sopenharmony_ci * dir->i_mutex: down 2718c2ecf20Sopenharmony_ci * inode->i_mutex: up (access to inode is still exclusive) 2728c2ecf20Sopenharmony_ci */ 2738c2ecf20Sopenharmony_ciint 2748c2ecf20Sopenharmony_ciext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct posix_acl *default_acl, *acl; 2778c2ecf20Sopenharmony_ci int error; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl); 2808c2ecf20Sopenharmony_ci if (error) 2818c2ecf20Sopenharmony_ci return error; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (default_acl) { 2848c2ecf20Sopenharmony_ci error = __ext4_set_acl(handle, inode, ACL_TYPE_DEFAULT, 2858c2ecf20Sopenharmony_ci default_acl, XATTR_CREATE); 2868c2ecf20Sopenharmony_ci posix_acl_release(default_acl); 2878c2ecf20Sopenharmony_ci } else { 2888c2ecf20Sopenharmony_ci inode->i_default_acl = NULL; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci if (acl) { 2918c2ecf20Sopenharmony_ci if (!error) 2928c2ecf20Sopenharmony_ci error = __ext4_set_acl(handle, inode, ACL_TYPE_ACCESS, 2938c2ecf20Sopenharmony_ci acl, XATTR_CREATE); 2948c2ecf20Sopenharmony_ci posix_acl_release(acl); 2958c2ecf20Sopenharmony_ci } else { 2968c2ecf20Sopenharmony_ci inode->i_acl = NULL; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci return error; 2998c2ecf20Sopenharmony_ci} 300