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