cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

acl.c (6820B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * linux/fs/ext4/acl.c
      4 *
      5 * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
      6 */
      7
      8#include <linux/quotaops.h>
      9#include "ext4_jbd2.h"
     10#include "ext4.h"
     11#include "xattr.h"
     12#include "acl.h"
     13
     14/*
     15 * Convert from filesystem to in-memory representation.
     16 */
     17static struct posix_acl *
     18ext4_acl_from_disk(const void *value, size_t size)
     19{
     20	const char *end = (char *)value + size;
     21	int n, count;
     22	struct posix_acl *acl;
     23
     24	if (!value)
     25		return NULL;
     26	if (size < sizeof(ext4_acl_header))
     27		 return ERR_PTR(-EINVAL);
     28	if (((ext4_acl_header *)value)->a_version !=
     29	    cpu_to_le32(EXT4_ACL_VERSION))
     30		return ERR_PTR(-EINVAL);
     31	value = (char *)value + sizeof(ext4_acl_header);
     32	count = ext4_acl_count(size);
     33	if (count < 0)
     34		return ERR_PTR(-EINVAL);
     35	if (count == 0)
     36		return NULL;
     37	acl = posix_acl_alloc(count, GFP_NOFS);
     38	if (!acl)
     39		return ERR_PTR(-ENOMEM);
     40	for (n = 0; n < count; n++) {
     41		ext4_acl_entry *entry =
     42			(ext4_acl_entry *)value;
     43		if ((char *)value + sizeof(ext4_acl_entry_short) > end)
     44			goto fail;
     45		acl->a_entries[n].e_tag  = le16_to_cpu(entry->e_tag);
     46		acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
     47
     48		switch (acl->a_entries[n].e_tag) {
     49		case ACL_USER_OBJ:
     50		case ACL_GROUP_OBJ:
     51		case ACL_MASK:
     52		case ACL_OTHER:
     53			value = (char *)value +
     54				sizeof(ext4_acl_entry_short);
     55			break;
     56
     57		case ACL_USER:
     58			value = (char *)value + sizeof(ext4_acl_entry);
     59			if ((char *)value > end)
     60				goto fail;
     61			acl->a_entries[n].e_uid =
     62				make_kuid(&init_user_ns,
     63					  le32_to_cpu(entry->e_id));
     64			break;
     65		case ACL_GROUP:
     66			value = (char *)value + sizeof(ext4_acl_entry);
     67			if ((char *)value > end)
     68				goto fail;
     69			acl->a_entries[n].e_gid =
     70				make_kgid(&init_user_ns,
     71					  le32_to_cpu(entry->e_id));
     72			break;
     73
     74		default:
     75			goto fail;
     76		}
     77	}
     78	if (value != end)
     79		goto fail;
     80	return acl;
     81
     82fail:
     83	posix_acl_release(acl);
     84	return ERR_PTR(-EINVAL);
     85}
     86
     87/*
     88 * Convert from in-memory to filesystem representation.
     89 */
     90static void *
     91ext4_acl_to_disk(const struct posix_acl *acl, size_t *size)
     92{
     93	ext4_acl_header *ext_acl;
     94	char *e;
     95	size_t n;
     96
     97	*size = ext4_acl_size(acl->a_count);
     98	ext_acl = kmalloc(sizeof(ext4_acl_header) + acl->a_count *
     99			sizeof(ext4_acl_entry), GFP_NOFS);
    100	if (!ext_acl)
    101		return ERR_PTR(-ENOMEM);
    102	ext_acl->a_version = cpu_to_le32(EXT4_ACL_VERSION);
    103	e = (char *)ext_acl + sizeof(ext4_acl_header);
    104	for (n = 0; n < acl->a_count; n++) {
    105		const struct posix_acl_entry *acl_e = &acl->a_entries[n];
    106		ext4_acl_entry *entry = (ext4_acl_entry *)e;
    107		entry->e_tag  = cpu_to_le16(acl_e->e_tag);
    108		entry->e_perm = cpu_to_le16(acl_e->e_perm);
    109		switch (acl_e->e_tag) {
    110		case ACL_USER:
    111			entry->e_id = cpu_to_le32(
    112				from_kuid(&init_user_ns, acl_e->e_uid));
    113			e += sizeof(ext4_acl_entry);
    114			break;
    115		case ACL_GROUP:
    116			entry->e_id = cpu_to_le32(
    117				from_kgid(&init_user_ns, acl_e->e_gid));
    118			e += sizeof(ext4_acl_entry);
    119			break;
    120
    121		case ACL_USER_OBJ:
    122		case ACL_GROUP_OBJ:
    123		case ACL_MASK:
    124		case ACL_OTHER:
    125			e += sizeof(ext4_acl_entry_short);
    126			break;
    127
    128		default:
    129			goto fail;
    130		}
    131	}
    132	return (char *)ext_acl;
    133
    134fail:
    135	kfree(ext_acl);
    136	return ERR_PTR(-EINVAL);
    137}
    138
    139/*
    140 * Inode operation get_posix_acl().
    141 *
    142 * inode->i_rwsem: don't care
    143 */
    144struct posix_acl *
    145ext4_get_acl(struct inode *inode, int type, bool rcu)
    146{
    147	int name_index;
    148	char *value = NULL;
    149	struct posix_acl *acl;
    150	int retval;
    151
    152	if (rcu)
    153		return ERR_PTR(-ECHILD);
    154
    155	switch (type) {
    156	case ACL_TYPE_ACCESS:
    157		name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
    158		break;
    159	case ACL_TYPE_DEFAULT:
    160		name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT;
    161		break;
    162	default:
    163		BUG();
    164	}
    165	retval = ext4_xattr_get(inode, name_index, "", NULL, 0);
    166	if (retval > 0) {
    167		value = kmalloc(retval, GFP_NOFS);
    168		if (!value)
    169			return ERR_PTR(-ENOMEM);
    170		retval = ext4_xattr_get(inode, name_index, "", value, retval);
    171	}
    172	if (retval > 0)
    173		acl = ext4_acl_from_disk(value, retval);
    174	else if (retval == -ENODATA || retval == -ENOSYS)
    175		acl = NULL;
    176	else
    177		acl = ERR_PTR(retval);
    178	kfree(value);
    179
    180	return acl;
    181}
    182
    183/*
    184 * Set the access or default ACL of an inode.
    185 *
    186 * inode->i_rwsem: down unless called from ext4_new_inode
    187 */
    188static int
    189__ext4_set_acl(handle_t *handle, struct inode *inode, int type,
    190	     struct posix_acl *acl, int xattr_flags)
    191{
    192	int name_index;
    193	void *value = NULL;
    194	size_t size = 0;
    195	int error;
    196
    197	switch (type) {
    198	case ACL_TYPE_ACCESS:
    199		name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
    200		break;
    201
    202	case ACL_TYPE_DEFAULT:
    203		name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT;
    204		if (!S_ISDIR(inode->i_mode))
    205			return acl ? -EACCES : 0;
    206		break;
    207
    208	default:
    209		return -EINVAL;
    210	}
    211	if (acl) {
    212		value = ext4_acl_to_disk(acl, &size);
    213		if (IS_ERR(value))
    214			return (int)PTR_ERR(value);
    215	}
    216
    217	error = ext4_xattr_set_handle(handle, inode, name_index, "",
    218				      value, size, xattr_flags);
    219
    220	kfree(value);
    221	if (!error)
    222		set_cached_acl(inode, type, acl);
    223
    224	return error;
    225}
    226
    227int
    228ext4_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
    229	     struct posix_acl *acl, int type)
    230{
    231	handle_t *handle;
    232	int error, credits, retries = 0;
    233	size_t acl_size = acl ? ext4_acl_size(acl->a_count) : 0;
    234	umode_t mode = inode->i_mode;
    235	int update_mode = 0;
    236
    237	error = dquot_initialize(inode);
    238	if (error)
    239		return error;
    240retry:
    241	error = ext4_xattr_set_credits(inode, acl_size, false /* is_create */,
    242				       &credits);
    243	if (error)
    244		return error;
    245
    246	handle = ext4_journal_start(inode, EXT4_HT_XATTR, credits);
    247	if (IS_ERR(handle))
    248		return PTR_ERR(handle);
    249
    250	if ((type == ACL_TYPE_ACCESS) && acl) {
    251		error = posix_acl_update_mode(mnt_userns, inode, &mode, &acl);
    252		if (error)
    253			goto out_stop;
    254		if (mode != inode->i_mode)
    255			update_mode = 1;
    256	}
    257
    258	error = __ext4_set_acl(handle, inode, type, acl, 0 /* xattr_flags */);
    259	if (!error && update_mode) {
    260		inode->i_mode = mode;
    261		inode->i_ctime = current_time(inode);
    262		error = ext4_mark_inode_dirty(handle, inode);
    263	}
    264out_stop:
    265	ext4_journal_stop(handle);
    266	if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
    267		goto retry;
    268	return error;
    269}
    270
    271/*
    272 * Initialize the ACLs of a new inode. Called from ext4_new_inode.
    273 *
    274 * dir->i_rwsem: down
    275 * inode->i_rwsem: up (access to inode is still exclusive)
    276 */
    277int
    278ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
    279{
    280	struct posix_acl *default_acl, *acl;
    281	int error;
    282
    283	error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
    284	if (error)
    285		return error;
    286
    287	if (default_acl) {
    288		error = __ext4_set_acl(handle, inode, ACL_TYPE_DEFAULT,
    289				       default_acl, XATTR_CREATE);
    290		posix_acl_release(default_acl);
    291	} else {
    292		inode->i_default_acl = NULL;
    293	}
    294	if (acl) {
    295		if (!error)
    296			error = __ext4_set_acl(handle, inode, ACL_TYPE_ACCESS,
    297					       acl, XATTR_CREATE);
    298		posix_acl_release(acl);
    299	} else {
    300		inode->i_acl = NULL;
    301	}
    302	return error;
    303}