brintos

brintos / linux-shallow public Read only

0
0
Text · 6.4 KiB · c7c3dcf Raw
295 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2008, Christoph Hellwig4 * All Rights Reserved.5 */6#include "xfs.h"7#include "xfs_shared.h"8#include "xfs_format.h"9#include "xfs_log_format.h"10#include "xfs_trans_resv.h"11#include "xfs_mount.h"12#include "xfs_inode.h"13#include "xfs_da_format.h"14#include "xfs_da_btree.h"15#include "xfs_attr.h"16#include "xfs_trace.h"17#include "xfs_error.h"18#include "xfs_acl.h"19#include "xfs_trans.h"20#include "xfs_xattr.h"21 22#include <linux/posix_acl_xattr.h>23 24/*25 * Locking scheme:26 *  - all ACL updates are protected by inode->i_mutex, which is taken before27 *    calling into this file.28 */29 30STATIC struct posix_acl *31xfs_acl_from_disk(32	struct xfs_mount	*mp,33	const struct xfs_acl	*aclp,34	int			len,35	int			max_entries)36{37	struct posix_acl_entry *acl_e;38	struct posix_acl *acl;39	const struct xfs_acl_entry *ace;40	unsigned int count, i;41 42	if (len < sizeof(*aclp)) {43		XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, aclp,44				len);45		return ERR_PTR(-EFSCORRUPTED);46	}47 48	count = be32_to_cpu(aclp->acl_cnt);49	if (count > max_entries || XFS_ACL_SIZE(count) != len) {50		XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, aclp,51				len);52		return ERR_PTR(-EFSCORRUPTED);53	}54 55	acl = posix_acl_alloc(count, GFP_KERNEL);56	if (!acl)57		return ERR_PTR(-ENOMEM);58 59	for (i = 0; i < count; i++) {60		acl_e = &acl->a_entries[i];61		ace = &aclp->acl_entry[i];62 63		/*64		 * The tag is 32 bits on disk and 16 bits in core.65		 *66		 * Because every access to it goes through the core67		 * format first this is not a problem.68		 */69		acl_e->e_tag = be32_to_cpu(ace->ae_tag);70		acl_e->e_perm = be16_to_cpu(ace->ae_perm);71 72		switch (acl_e->e_tag) {73		case ACL_USER:74			acl_e->e_uid = make_kuid(&init_user_ns,75						 be32_to_cpu(ace->ae_id));76			break;77		case ACL_GROUP:78			acl_e->e_gid = make_kgid(&init_user_ns,79						 be32_to_cpu(ace->ae_id));80			break;81		case ACL_USER_OBJ:82		case ACL_GROUP_OBJ:83		case ACL_MASK:84		case ACL_OTHER:85			break;86		default:87			goto fail;88		}89	}90	return acl;91 92fail:93	posix_acl_release(acl);94	return ERR_PTR(-EINVAL);95}96 97STATIC void98xfs_acl_to_disk(struct xfs_acl *aclp, const struct posix_acl *acl)99{100	const struct posix_acl_entry *acl_e;101	struct xfs_acl_entry *ace;102	int i;103 104	aclp->acl_cnt = cpu_to_be32(acl->a_count);105	for (i = 0; i < acl->a_count; i++) {106		ace = &aclp->acl_entry[i];107		acl_e = &acl->a_entries[i];108 109		ace->ae_tag = cpu_to_be32(acl_e->e_tag);110		switch (acl_e->e_tag) {111		case ACL_USER:112			ace->ae_id = cpu_to_be32(113					from_kuid(&init_user_ns, acl_e->e_uid));114			break;115		case ACL_GROUP:116			ace->ae_id = cpu_to_be32(117					from_kgid(&init_user_ns, acl_e->e_gid));118			break;119		default:120			ace->ae_id = cpu_to_be32(ACL_UNDEFINED_ID);121			break;122		}123 124		ace->ae_perm = cpu_to_be16(acl_e->e_perm);125	}126}127 128struct posix_acl *129xfs_get_acl(struct inode *inode, int type, bool rcu)130{131	struct xfs_inode	*ip = XFS_I(inode);132	struct xfs_mount	*mp = ip->i_mount;133	struct posix_acl	*acl = NULL;134	struct xfs_da_args	args = {135		.dp		= ip,136		.attr_filter	= XFS_ATTR_ROOT,137		.valuelen	= XFS_ACL_MAX_SIZE(mp),138	};139	int			error;140 141	if (rcu)142		return ERR_PTR(-ECHILD);143 144	trace_xfs_get_acl(ip);145 146	switch (type) {147	case ACL_TYPE_ACCESS:148		args.name = SGI_ACL_FILE;149		break;150	case ACL_TYPE_DEFAULT:151		args.name = SGI_ACL_DEFAULT;152		break;153	default:154		BUG();155	}156	args.namelen = strlen(args.name);157 158	/*159	 * If the attribute doesn't exist make sure we have a negative cache160	 * entry, for any other error assume it is transient.161	 */162	error = xfs_attr_get(&args);163	if (!error) {164		acl = xfs_acl_from_disk(mp, args.value, args.valuelen,165					XFS_ACL_MAX_ENTRIES(mp));166	} else if (error != -ENOATTR) {167		acl = ERR_PTR(error);168	}169 170	kvfree(args.value);171	return acl;172}173 174int175__xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type)176{177	struct xfs_inode	*ip = XFS_I(inode);178	struct xfs_da_args	args = {179		.dp		= ip,180		.attr_filter	= XFS_ATTR_ROOT,181	};182	int			error;183 184	switch (type) {185	case ACL_TYPE_ACCESS:186		args.name = SGI_ACL_FILE;187		break;188	case ACL_TYPE_DEFAULT:189		if (!S_ISDIR(inode->i_mode))190			return acl ? -EACCES : 0;191		args.name = SGI_ACL_DEFAULT;192		break;193	default:194		return -EINVAL;195	}196	args.namelen = strlen(args.name);197 198	if (acl) {199		args.valuelen = XFS_ACL_SIZE(acl->a_count);200		args.value = kvzalloc(args.valuelen, GFP_KERNEL);201		if (!args.value)202			return -ENOMEM;203		xfs_acl_to_disk(args.value, acl);204		error = xfs_attr_change(&args, XFS_ATTRUPDATE_UPSERT);205		kvfree(args.value);206	} else {207		error = xfs_attr_change(&args, XFS_ATTRUPDATE_REMOVE);208		/*209		 * If the attribute didn't exist to start with that's fine.210		 */211		if (error == -ENOATTR)212			error = 0;213	}214 215	if (!error)216		set_cached_acl(inode, type, acl);217	return error;218}219 220static int221xfs_acl_set_mode(222	struct inode		*inode,223	umode_t			mode)224{225	struct xfs_inode	*ip = XFS_I(inode);226	struct xfs_mount	*mp = ip->i_mount;227	struct xfs_trans	*tp;228	int			error;229 230	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);231	if (error)232		return error;233 234	xfs_ilock(ip, XFS_ILOCK_EXCL);235	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);236	inode->i_mode = mode;237	inode_set_ctime_current(inode);238	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);239 240	if (xfs_has_wsync(mp))241		xfs_trans_set_sync(tp);242	return xfs_trans_commit(tp);243}244 245int246xfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,247	    struct posix_acl *acl, int type)248{249	umode_t mode;250	bool set_mode = false;251	int error = 0;252	struct inode *inode = d_inode(dentry);253 254	if (!acl)255		goto set_acl;256 257	error = -E2BIG;258	if (acl->a_count > XFS_ACL_MAX_ENTRIES(XFS_M(inode->i_sb)))259		return error;260 261	if (type == ACL_TYPE_ACCESS) {262		error = posix_acl_update_mode(idmap, inode, &mode, &acl);263		if (error)264			return error;265		set_mode = true;266	}267 268 set_acl:269	/*270	 * We set the mode after successfully updating the ACL xattr because the271	 * xattr update can fail at ENOSPC and we don't want to change the mode272	 * if the ACL update hasn't been applied.273	 */274	error =  __xfs_set_acl(inode, acl, type);275	if (!error && set_mode && mode != inode->i_mode)276		error = xfs_acl_set_mode(inode, mode);277	return error;278}279 280/*281 * Invalidate any cached ACLs if the user has bypassed the ACL interface.282 * We don't validate the content whatsoever so it is caller responsibility to283 * provide data in valid format and ensure i_mode is consistent.284 */285void286xfs_forget_acl(287	struct inode		*inode,288	const char		*name)289{290	if (!strcmp(name, SGI_ACL_FILE))291		forget_cached_acl(inode, ACL_TYPE_ACCESS);292	else if (!strcmp(name, SGI_ACL_DEFAULT))293		forget_cached_acl(inode, ACL_TYPE_DEFAULT);294}295