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 (6037B)


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