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


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * fs/f2fs/acl.c
      4 *
      5 * Copyright (c) 2012 Samsung Electronics Co., Ltd.
      6 *             http://www.samsung.com/
      7 *
      8 * Portions of this code from linux/fs/ext2/acl.c
      9 *
     10 * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
     11 */
     12#include <linux/f2fs_fs.h>
     13#include "f2fs.h"
     14#include "xattr.h"
     15#include "acl.h"
     16
     17static inline size_t f2fs_acl_size(int count)
     18{
     19	if (count <= 4) {
     20		return sizeof(struct f2fs_acl_header) +
     21			count * sizeof(struct f2fs_acl_entry_short);
     22	} else {
     23		return sizeof(struct f2fs_acl_header) +
     24			4 * sizeof(struct f2fs_acl_entry_short) +
     25			(count - 4) * sizeof(struct f2fs_acl_entry);
     26	}
     27}
     28
     29static inline int f2fs_acl_count(size_t size)
     30{
     31	ssize_t s;
     32
     33	size -= sizeof(struct f2fs_acl_header);
     34	s = size - 4 * sizeof(struct f2fs_acl_entry_short);
     35	if (s < 0) {
     36		if (size % sizeof(struct f2fs_acl_entry_short))
     37			return -1;
     38		return size / sizeof(struct f2fs_acl_entry_short);
     39	} else {
     40		if (s % sizeof(struct f2fs_acl_entry))
     41			return -1;
     42		return s / sizeof(struct f2fs_acl_entry) + 4;
     43	}
     44}
     45
     46static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size)
     47{
     48	int i, count;
     49	struct posix_acl *acl;
     50	struct f2fs_acl_header *hdr = (struct f2fs_acl_header *)value;
     51	struct f2fs_acl_entry *entry = (struct f2fs_acl_entry *)(hdr + 1);
     52	const char *end = value + size;
     53
     54	if (size < sizeof(struct f2fs_acl_header))
     55		return ERR_PTR(-EINVAL);
     56
     57	if (hdr->a_version != cpu_to_le32(F2FS_ACL_VERSION))
     58		return ERR_PTR(-EINVAL);
     59
     60	count = f2fs_acl_count(size);
     61	if (count < 0)
     62		return ERR_PTR(-EINVAL);
     63	if (count == 0)
     64		return NULL;
     65
     66	acl = posix_acl_alloc(count, GFP_NOFS);
     67	if (!acl)
     68		return ERR_PTR(-ENOMEM);
     69
     70	for (i = 0; i < count; i++) {
     71
     72		if ((char *)entry > end)
     73			goto fail;
     74
     75		acl->a_entries[i].e_tag  = le16_to_cpu(entry->e_tag);
     76		acl->a_entries[i].e_perm = le16_to_cpu(entry->e_perm);
     77
     78		switch (acl->a_entries[i].e_tag) {
     79		case ACL_USER_OBJ:
     80		case ACL_GROUP_OBJ:
     81		case ACL_MASK:
     82		case ACL_OTHER:
     83			entry = (struct f2fs_acl_entry *)((char *)entry +
     84					sizeof(struct f2fs_acl_entry_short));
     85			break;
     86
     87		case ACL_USER:
     88			acl->a_entries[i].e_uid =
     89				make_kuid(&init_user_ns,
     90						le32_to_cpu(entry->e_id));
     91			entry = (struct f2fs_acl_entry *)((char *)entry +
     92					sizeof(struct f2fs_acl_entry));
     93			break;
     94		case ACL_GROUP:
     95			acl->a_entries[i].e_gid =
     96				make_kgid(&init_user_ns,
     97						le32_to_cpu(entry->e_id));
     98			entry = (struct f2fs_acl_entry *)((char *)entry +
     99					sizeof(struct f2fs_acl_entry));
    100			break;
    101		default:
    102			goto fail;
    103		}
    104	}
    105	if ((char *)entry != end)
    106		goto fail;
    107	return acl;
    108fail:
    109	posix_acl_release(acl);
    110	return ERR_PTR(-EINVAL);
    111}
    112
    113static void *f2fs_acl_to_disk(struct f2fs_sb_info *sbi,
    114				const struct posix_acl *acl, size_t *size)
    115{
    116	struct f2fs_acl_header *f2fs_acl;
    117	struct f2fs_acl_entry *entry;
    118	int i;
    119
    120	f2fs_acl = f2fs_kmalloc(sbi, sizeof(struct f2fs_acl_header) +
    121			acl->a_count * sizeof(struct f2fs_acl_entry),
    122			GFP_NOFS);
    123	if (!f2fs_acl)
    124		return ERR_PTR(-ENOMEM);
    125
    126	f2fs_acl->a_version = cpu_to_le32(F2FS_ACL_VERSION);
    127	entry = (struct f2fs_acl_entry *)(f2fs_acl + 1);
    128
    129	for (i = 0; i < acl->a_count; i++) {
    130
    131		entry->e_tag  = cpu_to_le16(acl->a_entries[i].e_tag);
    132		entry->e_perm = cpu_to_le16(acl->a_entries[i].e_perm);
    133
    134		switch (acl->a_entries[i].e_tag) {
    135		case ACL_USER:
    136			entry->e_id = cpu_to_le32(
    137					from_kuid(&init_user_ns,
    138						acl->a_entries[i].e_uid));
    139			entry = (struct f2fs_acl_entry *)((char *)entry +
    140					sizeof(struct f2fs_acl_entry));
    141			break;
    142		case ACL_GROUP:
    143			entry->e_id = cpu_to_le32(
    144					from_kgid(&init_user_ns,
    145						acl->a_entries[i].e_gid));
    146			entry = (struct f2fs_acl_entry *)((char *)entry +
    147					sizeof(struct f2fs_acl_entry));
    148			break;
    149		case ACL_USER_OBJ:
    150		case ACL_GROUP_OBJ:
    151		case ACL_MASK:
    152		case ACL_OTHER:
    153			entry = (struct f2fs_acl_entry *)((char *)entry +
    154					sizeof(struct f2fs_acl_entry_short));
    155			break;
    156		default:
    157			goto fail;
    158		}
    159	}
    160	*size = f2fs_acl_size(acl->a_count);
    161	return (void *)f2fs_acl;
    162
    163fail:
    164	kfree(f2fs_acl);
    165	return ERR_PTR(-EINVAL);
    166}
    167
    168static struct posix_acl *__f2fs_get_acl(struct inode *inode, int type,
    169						struct page *dpage)
    170{
    171	int name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT;
    172	void *value = NULL;
    173	struct posix_acl *acl;
    174	int retval;
    175
    176	if (type == ACL_TYPE_ACCESS)
    177		name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
    178
    179	retval = f2fs_getxattr(inode, name_index, "", NULL, 0, dpage);
    180	if (retval > 0) {
    181		value = f2fs_kmalloc(F2FS_I_SB(inode), retval, GFP_F2FS_ZERO);
    182		if (!value)
    183			return ERR_PTR(-ENOMEM);
    184		retval = f2fs_getxattr(inode, name_index, "", value,
    185							retval, dpage);
    186	}
    187
    188	if (retval > 0)
    189		acl = f2fs_acl_from_disk(value, retval);
    190	else if (retval == -ENODATA)
    191		acl = NULL;
    192	else
    193		acl = ERR_PTR(retval);
    194	kfree(value);
    195
    196	return acl;
    197}
    198
    199struct posix_acl *f2fs_get_acl(struct inode *inode, int type, bool rcu)
    200{
    201	if (rcu)
    202		return ERR_PTR(-ECHILD);
    203
    204	return __f2fs_get_acl(inode, type, NULL);
    205}
    206
    207static int f2fs_acl_update_mode(struct user_namespace *mnt_userns,
    208				struct inode *inode, umode_t *mode_p,
    209				struct posix_acl **acl)
    210{
    211	umode_t mode = inode->i_mode;
    212	int error;
    213
    214	if (is_inode_flag_set(inode, FI_ACL_MODE))
    215		mode = F2FS_I(inode)->i_acl_mode;
    216
    217	error = posix_acl_equiv_mode(*acl, &mode);
    218	if (error < 0)
    219		return error;
    220	if (error == 0)
    221		*acl = NULL;
    222	if (!in_group_p(i_gid_into_mnt(mnt_userns, inode)) &&
    223	    !capable_wrt_inode_uidgid(mnt_userns, inode, CAP_FSETID))
    224		mode &= ~S_ISGID;
    225	*mode_p = mode;
    226	return 0;
    227}
    228
    229static int __f2fs_set_acl(struct user_namespace *mnt_userns,
    230			struct inode *inode, int type,
    231			struct posix_acl *acl, struct page *ipage)
    232{
    233	int name_index;
    234	void *value = NULL;
    235	size_t size = 0;
    236	int error;
    237	umode_t mode = inode->i_mode;
    238
    239	switch (type) {
    240	case ACL_TYPE_ACCESS:
    241		name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
    242		if (acl && !ipage) {
    243			error = f2fs_acl_update_mode(mnt_userns, inode,
    244								&mode, &acl);
    245			if (error)
    246				return error;
    247			set_acl_inode(inode, mode);
    248		}
    249		break;
    250
    251	case ACL_TYPE_DEFAULT:
    252		name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT;
    253		if (!S_ISDIR(inode->i_mode))
    254			return acl ? -EACCES : 0;
    255		break;
    256
    257	default:
    258		return -EINVAL;
    259	}
    260
    261	if (acl) {
    262		value = f2fs_acl_to_disk(F2FS_I_SB(inode), acl, &size);
    263		if (IS_ERR(value)) {
    264			clear_inode_flag(inode, FI_ACL_MODE);
    265			return PTR_ERR(value);
    266		}
    267	}
    268
    269	error = f2fs_setxattr(inode, name_index, "", value, size, ipage, 0);
    270
    271	kfree(value);
    272	if (!error)
    273		set_cached_acl(inode, type, acl);
    274
    275	clear_inode_flag(inode, FI_ACL_MODE);
    276	return error;
    277}
    278
    279int f2fs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
    280		 struct posix_acl *acl, int type)
    281{
    282	if (unlikely(f2fs_cp_error(F2FS_I_SB(inode))))
    283		return -EIO;
    284
    285	return __f2fs_set_acl(mnt_userns, inode, type, acl, NULL);
    286}
    287
    288/*
    289 * Most part of f2fs_acl_clone, f2fs_acl_create_masq, f2fs_acl_create
    290 * are copied from posix_acl.c
    291 */
    292static struct posix_acl *f2fs_acl_clone(const struct posix_acl *acl,
    293							gfp_t flags)
    294{
    295	struct posix_acl *clone = NULL;
    296
    297	if (acl) {
    298		int size = sizeof(struct posix_acl) + acl->a_count *
    299				sizeof(struct posix_acl_entry);
    300		clone = kmemdup(acl, size, flags);
    301		if (clone)
    302			refcount_set(&clone->a_refcount, 1);
    303	}
    304	return clone;
    305}
    306
    307static int f2fs_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
    308{
    309	struct posix_acl_entry *pa, *pe;
    310	struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
    311	umode_t mode = *mode_p;
    312	int not_equiv = 0;
    313
    314	/* assert(atomic_read(acl->a_refcount) == 1); */
    315
    316	FOREACH_ACL_ENTRY(pa, acl, pe) {
    317		switch (pa->e_tag) {
    318		case ACL_USER_OBJ:
    319			pa->e_perm &= (mode >> 6) | ~S_IRWXO;
    320			mode &= (pa->e_perm << 6) | ~S_IRWXU;
    321			break;
    322
    323		case ACL_USER:
    324		case ACL_GROUP:
    325			not_equiv = 1;
    326			break;
    327
    328		case ACL_GROUP_OBJ:
    329			group_obj = pa;
    330			break;
    331
    332		case ACL_OTHER:
    333			pa->e_perm &= mode | ~S_IRWXO;
    334			mode &= pa->e_perm | ~S_IRWXO;
    335			break;
    336
    337		case ACL_MASK:
    338			mask_obj = pa;
    339			not_equiv = 1;
    340			break;
    341
    342		default:
    343			return -EIO;
    344		}
    345	}
    346
    347	if (mask_obj) {
    348		mask_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
    349		mode &= (mask_obj->e_perm << 3) | ~S_IRWXG;
    350	} else {
    351		if (!group_obj)
    352			return -EIO;
    353		group_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
    354		mode &= (group_obj->e_perm << 3) | ~S_IRWXG;
    355	}
    356
    357	*mode_p = (*mode_p & ~S_IRWXUGO) | mode;
    358	return not_equiv;
    359}
    360
    361static int f2fs_acl_create(struct inode *dir, umode_t *mode,
    362		struct posix_acl **default_acl, struct posix_acl **acl,
    363		struct page *dpage)
    364{
    365	struct posix_acl *p;
    366	struct posix_acl *clone;
    367	int ret;
    368
    369	*acl = NULL;
    370	*default_acl = NULL;
    371
    372	if (S_ISLNK(*mode) || !IS_POSIXACL(dir))
    373		return 0;
    374
    375	p = __f2fs_get_acl(dir, ACL_TYPE_DEFAULT, dpage);
    376	if (!p || p == ERR_PTR(-EOPNOTSUPP)) {
    377		*mode &= ~current_umask();
    378		return 0;
    379	}
    380	if (IS_ERR(p))
    381		return PTR_ERR(p);
    382
    383	clone = f2fs_acl_clone(p, GFP_NOFS);
    384	if (!clone) {
    385		ret = -ENOMEM;
    386		goto release_acl;
    387	}
    388
    389	ret = f2fs_acl_create_masq(clone, mode);
    390	if (ret < 0)
    391		goto release_clone;
    392
    393	if (ret == 0)
    394		posix_acl_release(clone);
    395	else
    396		*acl = clone;
    397
    398	if (!S_ISDIR(*mode))
    399		posix_acl_release(p);
    400	else
    401		*default_acl = p;
    402
    403	return 0;
    404
    405release_clone:
    406	posix_acl_release(clone);
    407release_acl:
    408	posix_acl_release(p);
    409	return ret;
    410}
    411
    412int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage,
    413							struct page *dpage)
    414{
    415	struct posix_acl *default_acl = NULL, *acl = NULL;
    416	int error;
    417
    418	error = f2fs_acl_create(dir, &inode->i_mode, &default_acl, &acl, dpage);
    419	if (error)
    420		return error;
    421
    422	f2fs_mark_inode_dirty_sync(inode, true);
    423
    424	if (default_acl) {
    425		error = __f2fs_set_acl(NULL, inode, ACL_TYPE_DEFAULT, default_acl,
    426				       ipage);
    427		posix_acl_release(default_acl);
    428	} else {
    429		inode->i_default_acl = NULL;
    430	}
    431	if (acl) {
    432		if (!error)
    433			error = __f2fs_set_acl(NULL, inode, ACL_TYPE_ACCESS, acl,
    434					       ipage);
    435		posix_acl_release(acl);
    436	} else {
    437		inode->i_acl = NULL;
    438	}
    439
    440	return error;
    441}