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