162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2008, Christoph Hellwig 462306a36Sopenharmony_ci * All Rights Reserved. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include "xfs.h" 762306a36Sopenharmony_ci#include "xfs_shared.h" 862306a36Sopenharmony_ci#include "xfs_format.h" 962306a36Sopenharmony_ci#include "xfs_log_format.h" 1062306a36Sopenharmony_ci#include "xfs_trans_resv.h" 1162306a36Sopenharmony_ci#include "xfs_mount.h" 1262306a36Sopenharmony_ci#include "xfs_inode.h" 1362306a36Sopenharmony_ci#include "xfs_da_format.h" 1462306a36Sopenharmony_ci#include "xfs_da_btree.h" 1562306a36Sopenharmony_ci#include "xfs_attr.h" 1662306a36Sopenharmony_ci#include "xfs_trace.h" 1762306a36Sopenharmony_ci#include "xfs_error.h" 1862306a36Sopenharmony_ci#include "xfs_acl.h" 1962306a36Sopenharmony_ci#include "xfs_trans.h" 2062306a36Sopenharmony_ci#include "xfs_xattr.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <linux/posix_acl_xattr.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * Locking scheme: 2662306a36Sopenharmony_ci * - all ACL updates are protected by inode->i_mutex, which is taken before 2762306a36Sopenharmony_ci * calling into this file. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ciSTATIC struct posix_acl * 3162306a36Sopenharmony_cixfs_acl_from_disk( 3262306a36Sopenharmony_ci struct xfs_mount *mp, 3362306a36Sopenharmony_ci const struct xfs_acl *aclp, 3462306a36Sopenharmony_ci int len, 3562306a36Sopenharmony_ci int max_entries) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci struct posix_acl_entry *acl_e; 3862306a36Sopenharmony_ci struct posix_acl *acl; 3962306a36Sopenharmony_ci const struct xfs_acl_entry *ace; 4062306a36Sopenharmony_ci unsigned int count, i; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci if (len < sizeof(*aclp)) { 4362306a36Sopenharmony_ci XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, aclp, 4462306a36Sopenharmony_ci len); 4562306a36Sopenharmony_ci return ERR_PTR(-EFSCORRUPTED); 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci count = be32_to_cpu(aclp->acl_cnt); 4962306a36Sopenharmony_ci if (count > max_entries || XFS_ACL_SIZE(count) != len) { 5062306a36Sopenharmony_ci XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, aclp, 5162306a36Sopenharmony_ci len); 5262306a36Sopenharmony_ci return ERR_PTR(-EFSCORRUPTED); 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci acl = posix_acl_alloc(count, GFP_KERNEL); 5662306a36Sopenharmony_ci if (!acl) 5762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci for (i = 0; i < count; i++) { 6062306a36Sopenharmony_ci acl_e = &acl->a_entries[i]; 6162306a36Sopenharmony_ci ace = &aclp->acl_entry[i]; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* 6462306a36Sopenharmony_ci * The tag is 32 bits on disk and 16 bits in core. 6562306a36Sopenharmony_ci * 6662306a36Sopenharmony_ci * Because every access to it goes through the core 6762306a36Sopenharmony_ci * format first this is not a problem. 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_ci acl_e->e_tag = be32_to_cpu(ace->ae_tag); 7062306a36Sopenharmony_ci acl_e->e_perm = be16_to_cpu(ace->ae_perm); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci switch (acl_e->e_tag) { 7362306a36Sopenharmony_ci case ACL_USER: 7462306a36Sopenharmony_ci acl_e->e_uid = make_kuid(&init_user_ns, 7562306a36Sopenharmony_ci be32_to_cpu(ace->ae_id)); 7662306a36Sopenharmony_ci break; 7762306a36Sopenharmony_ci case ACL_GROUP: 7862306a36Sopenharmony_ci acl_e->e_gid = make_kgid(&init_user_ns, 7962306a36Sopenharmony_ci be32_to_cpu(ace->ae_id)); 8062306a36Sopenharmony_ci break; 8162306a36Sopenharmony_ci case ACL_USER_OBJ: 8262306a36Sopenharmony_ci case ACL_GROUP_OBJ: 8362306a36Sopenharmony_ci case ACL_MASK: 8462306a36Sopenharmony_ci case ACL_OTHER: 8562306a36Sopenharmony_ci break; 8662306a36Sopenharmony_ci default: 8762306a36Sopenharmony_ci goto fail; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci return acl; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cifail: 9362306a36Sopenharmony_ci posix_acl_release(acl); 9462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ciSTATIC void 9862306a36Sopenharmony_cixfs_acl_to_disk(struct xfs_acl *aclp, const struct posix_acl *acl) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci const struct posix_acl_entry *acl_e; 10162306a36Sopenharmony_ci struct xfs_acl_entry *ace; 10262306a36Sopenharmony_ci int i; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci aclp->acl_cnt = cpu_to_be32(acl->a_count); 10562306a36Sopenharmony_ci for (i = 0; i < acl->a_count; i++) { 10662306a36Sopenharmony_ci ace = &aclp->acl_entry[i]; 10762306a36Sopenharmony_ci acl_e = &acl->a_entries[i]; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci ace->ae_tag = cpu_to_be32(acl_e->e_tag); 11062306a36Sopenharmony_ci switch (acl_e->e_tag) { 11162306a36Sopenharmony_ci case ACL_USER: 11262306a36Sopenharmony_ci ace->ae_id = cpu_to_be32( 11362306a36Sopenharmony_ci from_kuid(&init_user_ns, acl_e->e_uid)); 11462306a36Sopenharmony_ci break; 11562306a36Sopenharmony_ci case ACL_GROUP: 11662306a36Sopenharmony_ci ace->ae_id = cpu_to_be32( 11762306a36Sopenharmony_ci from_kgid(&init_user_ns, acl_e->e_gid)); 11862306a36Sopenharmony_ci break; 11962306a36Sopenharmony_ci default: 12062306a36Sopenharmony_ci ace->ae_id = cpu_to_be32(ACL_UNDEFINED_ID); 12162306a36Sopenharmony_ci break; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci ace->ae_perm = cpu_to_be16(acl_e->e_perm); 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistruct posix_acl * 12962306a36Sopenharmony_cixfs_get_acl(struct inode *inode, int type, bool rcu) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct xfs_inode *ip = XFS_I(inode); 13262306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 13362306a36Sopenharmony_ci struct posix_acl *acl = NULL; 13462306a36Sopenharmony_ci struct xfs_da_args args = { 13562306a36Sopenharmony_ci .dp = ip, 13662306a36Sopenharmony_ci .attr_filter = XFS_ATTR_ROOT, 13762306a36Sopenharmony_ci .valuelen = XFS_ACL_MAX_SIZE(mp), 13862306a36Sopenharmony_ci }; 13962306a36Sopenharmony_ci int error; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (rcu) 14262306a36Sopenharmony_ci return ERR_PTR(-ECHILD); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci trace_xfs_get_acl(ip); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci switch (type) { 14762306a36Sopenharmony_ci case ACL_TYPE_ACCESS: 14862306a36Sopenharmony_ci args.name = SGI_ACL_FILE; 14962306a36Sopenharmony_ci break; 15062306a36Sopenharmony_ci case ACL_TYPE_DEFAULT: 15162306a36Sopenharmony_ci args.name = SGI_ACL_DEFAULT; 15262306a36Sopenharmony_ci break; 15362306a36Sopenharmony_ci default: 15462306a36Sopenharmony_ci BUG(); 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci args.namelen = strlen(args.name); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* 15962306a36Sopenharmony_ci * If the attribute doesn't exist make sure we have a negative cache 16062306a36Sopenharmony_ci * entry, for any other error assume it is transient. 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_ci error = xfs_attr_get(&args); 16362306a36Sopenharmony_ci if (!error) { 16462306a36Sopenharmony_ci acl = xfs_acl_from_disk(mp, args.value, args.valuelen, 16562306a36Sopenharmony_ci XFS_ACL_MAX_ENTRIES(mp)); 16662306a36Sopenharmony_ci } else if (error != -ENOATTR) { 16762306a36Sopenharmony_ci acl = ERR_PTR(error); 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci kmem_free(args.value); 17162306a36Sopenharmony_ci return acl; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ciint 17562306a36Sopenharmony_ci__xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct xfs_inode *ip = XFS_I(inode); 17862306a36Sopenharmony_ci struct xfs_da_args args = { 17962306a36Sopenharmony_ci .dp = ip, 18062306a36Sopenharmony_ci .attr_filter = XFS_ATTR_ROOT, 18162306a36Sopenharmony_ci }; 18262306a36Sopenharmony_ci int error; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci switch (type) { 18562306a36Sopenharmony_ci case ACL_TYPE_ACCESS: 18662306a36Sopenharmony_ci args.name = SGI_ACL_FILE; 18762306a36Sopenharmony_ci break; 18862306a36Sopenharmony_ci case ACL_TYPE_DEFAULT: 18962306a36Sopenharmony_ci if (!S_ISDIR(inode->i_mode)) 19062306a36Sopenharmony_ci return acl ? -EACCES : 0; 19162306a36Sopenharmony_ci args.name = SGI_ACL_DEFAULT; 19262306a36Sopenharmony_ci break; 19362306a36Sopenharmony_ci default: 19462306a36Sopenharmony_ci return -EINVAL; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci args.namelen = strlen(args.name); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (acl) { 19962306a36Sopenharmony_ci args.valuelen = XFS_ACL_SIZE(acl->a_count); 20062306a36Sopenharmony_ci args.value = kvzalloc(args.valuelen, GFP_KERNEL); 20162306a36Sopenharmony_ci if (!args.value) 20262306a36Sopenharmony_ci return -ENOMEM; 20362306a36Sopenharmony_ci xfs_acl_to_disk(args.value, acl); 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci error = xfs_attr_change(&args); 20762306a36Sopenharmony_ci kmem_free(args.value); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* 21062306a36Sopenharmony_ci * If the attribute didn't exist to start with that's fine. 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_ci if (!acl && error == -ENOATTR) 21362306a36Sopenharmony_ci error = 0; 21462306a36Sopenharmony_ci if (!error) 21562306a36Sopenharmony_ci set_cached_acl(inode, type, acl); 21662306a36Sopenharmony_ci return error; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic int 22062306a36Sopenharmony_cixfs_acl_set_mode( 22162306a36Sopenharmony_ci struct inode *inode, 22262306a36Sopenharmony_ci umode_t mode) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct xfs_inode *ip = XFS_I(inode); 22562306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 22662306a36Sopenharmony_ci struct xfs_trans *tp; 22762306a36Sopenharmony_ci int error; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp); 23062306a36Sopenharmony_ci if (error) 23162306a36Sopenharmony_ci return error; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci xfs_ilock(ip, XFS_ILOCK_EXCL); 23462306a36Sopenharmony_ci xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); 23562306a36Sopenharmony_ci inode->i_mode = mode; 23662306a36Sopenharmony_ci inode_set_ctime_current(inode); 23762306a36Sopenharmony_ci xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (xfs_has_wsync(mp)) 24062306a36Sopenharmony_ci xfs_trans_set_sync(tp); 24162306a36Sopenharmony_ci return xfs_trans_commit(tp); 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ciint 24562306a36Sopenharmony_cixfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, 24662306a36Sopenharmony_ci struct posix_acl *acl, int type) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci umode_t mode; 24962306a36Sopenharmony_ci bool set_mode = false; 25062306a36Sopenharmony_ci int error = 0; 25162306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (!acl) 25462306a36Sopenharmony_ci goto set_acl; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci error = -E2BIG; 25762306a36Sopenharmony_ci if (acl->a_count > XFS_ACL_MAX_ENTRIES(XFS_M(inode->i_sb))) 25862306a36Sopenharmony_ci return error; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (type == ACL_TYPE_ACCESS) { 26162306a36Sopenharmony_ci error = posix_acl_update_mode(idmap, inode, &mode, &acl); 26262306a36Sopenharmony_ci if (error) 26362306a36Sopenharmony_ci return error; 26462306a36Sopenharmony_ci set_mode = true; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci set_acl: 26862306a36Sopenharmony_ci /* 26962306a36Sopenharmony_ci * We set the mode after successfully updating the ACL xattr because the 27062306a36Sopenharmony_ci * xattr update can fail at ENOSPC and we don't want to change the mode 27162306a36Sopenharmony_ci * if the ACL update hasn't been applied. 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_ci error = __xfs_set_acl(inode, acl, type); 27462306a36Sopenharmony_ci if (!error && set_mode && mode != inode->i_mode) 27562306a36Sopenharmony_ci error = xfs_acl_set_mode(inode, mode); 27662306a36Sopenharmony_ci return error; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci/* 28062306a36Sopenharmony_ci * Invalidate any cached ACLs if the user has bypassed the ACL interface. 28162306a36Sopenharmony_ci * We don't validate the content whatsoever so it is caller responsibility to 28262306a36Sopenharmony_ci * provide data in valid format and ensure i_mode is consistent. 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_civoid 28562306a36Sopenharmony_cixfs_forget_acl( 28662306a36Sopenharmony_ci struct inode *inode, 28762306a36Sopenharmony_ci const char *name) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci if (!strcmp(name, SGI_ACL_FILE)) 29062306a36Sopenharmony_ci forget_cached_acl(inode, ACL_TYPE_ACCESS); 29162306a36Sopenharmony_ci else if (!strcmp(name, SGI_ACL_DEFAULT)) 29262306a36Sopenharmony_ci forget_cached_acl(inode, ACL_TYPE_DEFAULT); 29362306a36Sopenharmony_ci} 294