brintos

brintos / linux-shallow public Read only

0
0
Text · 9.6 KiB · 8bffdec Raw
443 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * fs/f2fs/acl.c4 *5 * Copyright (c) 2012 Samsung Electronics Co., Ltd.6 *             http://www.samsung.com/7 *8 * Portions of this code from linux/fs/ext2/acl.c9 *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	else193		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 mnt_idmap *idmap,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_or_capable(idmap, inode, i_gid_into_vfsgid(idmap, inode)))223		mode &= ~S_ISGID;224	*mode_p = mode;225	return 0;226}227 228static int __f2fs_set_acl(struct mnt_idmap *idmap,229			struct inode *inode, int type,230			struct posix_acl *acl, struct page *ipage)231{232	int name_index;233	void *value = NULL;234	size_t size = 0;235	int error;236	umode_t mode = inode->i_mode;237 238	switch (type) {239	case ACL_TYPE_ACCESS:240		name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;241		if (acl && !ipage) {242			error = f2fs_acl_update_mode(idmap, inode,243								&mode, &acl);244			if (error)245				return error;246			set_acl_inode(inode, mode);247		}248		break;249 250	case ACL_TYPE_DEFAULT:251		name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT;252		if (!S_ISDIR(inode->i_mode))253			return acl ? -EACCES : 0;254		break;255 256	default:257		return -EINVAL;258	}259 260	if (acl) {261		value = f2fs_acl_to_disk(F2FS_I_SB(inode), acl, &size);262		if (IS_ERR(value)) {263			clear_inode_flag(inode, FI_ACL_MODE);264			return PTR_ERR(value);265		}266	}267 268	error = f2fs_setxattr(inode, name_index, "", value, size, ipage, 0);269 270	kfree(value);271	if (!error)272		set_cached_acl(inode, type, acl);273 274	clear_inode_flag(inode, FI_ACL_MODE);275	return error;276}277 278int f2fs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,279		 struct posix_acl *acl, int type)280{281	struct inode *inode = d_inode(dentry);282 283	if (unlikely(f2fs_cp_error(F2FS_I_SB(inode))))284		return -EIO;285 286	return __f2fs_set_acl(idmap, inode, type, acl, NULL);287}288 289/*290 * Most part of f2fs_acl_clone, f2fs_acl_create_masq, f2fs_acl_create291 * are copied from posix_acl.c292 */293static struct posix_acl *f2fs_acl_clone(const struct posix_acl *acl,294							gfp_t flags)295{296	struct posix_acl *clone = NULL;297 298	if (acl) {299		int size = sizeof(struct posix_acl) + acl->a_count *300				sizeof(struct posix_acl_entry);301		clone = kmemdup(acl, size, flags);302		if (clone)303			refcount_set(&clone->a_refcount, 1);304	}305	return clone;306}307 308static int f2fs_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)309{310	struct posix_acl_entry *pa, *pe;311	struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;312	umode_t mode = *mode_p;313	int not_equiv = 0;314 315	/* assert(atomic_read(acl->a_refcount) == 1); */316 317	FOREACH_ACL_ENTRY(pa, acl, pe) {318		switch (pa->e_tag) {319		case ACL_USER_OBJ:320			pa->e_perm &= (mode >> 6) | ~S_IRWXO;321			mode &= (pa->e_perm << 6) | ~S_IRWXU;322			break;323 324		case ACL_USER:325		case ACL_GROUP:326			not_equiv = 1;327			break;328 329		case ACL_GROUP_OBJ:330			group_obj = pa;331			break;332 333		case ACL_OTHER:334			pa->e_perm &= mode | ~S_IRWXO;335			mode &= pa->e_perm | ~S_IRWXO;336			break;337 338		case ACL_MASK:339			mask_obj = pa;340			not_equiv = 1;341			break;342 343		default:344			return -EIO;345		}346	}347 348	if (mask_obj) {349		mask_obj->e_perm &= (mode >> 3) | ~S_IRWXO;350		mode &= (mask_obj->e_perm << 3) | ~S_IRWXG;351	} else {352		if (!group_obj)353			return -EIO;354		group_obj->e_perm &= (mode >> 3) | ~S_IRWXO;355		mode &= (group_obj->e_perm << 3) | ~S_IRWXG;356	}357 358	*mode_p = (*mode_p & ~S_IRWXUGO) | mode;359	return not_equiv;360}361 362static int f2fs_acl_create(struct inode *dir, umode_t *mode,363		struct posix_acl **default_acl, struct posix_acl **acl,364		struct page *dpage)365{366	struct posix_acl *p;367	struct posix_acl *clone;368	int ret;369 370	*acl = NULL;371	*default_acl = NULL;372 373	if (S_ISLNK(*mode) || !IS_POSIXACL(dir))374		return 0;375 376	p = __f2fs_get_acl(dir, ACL_TYPE_DEFAULT, dpage);377	if (!p || p == ERR_PTR(-EOPNOTSUPP)) {378		*mode &= ~current_umask();379		return 0;380	}381	if (IS_ERR(p))382		return PTR_ERR(p);383 384	clone = f2fs_acl_clone(p, GFP_NOFS);385	if (!clone) {386		ret = -ENOMEM;387		goto release_acl;388	}389 390	ret = f2fs_acl_create_masq(clone, mode);391	if (ret < 0)392		goto release_clone;393 394	if (ret == 0)395		posix_acl_release(clone);396	else397		*acl = clone;398 399	if (!S_ISDIR(*mode))400		posix_acl_release(p);401	else402		*default_acl = p;403 404	return 0;405 406release_clone:407	posix_acl_release(clone);408release_acl:409	posix_acl_release(p);410	return ret;411}412 413int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage,414							struct page *dpage)415{416	struct posix_acl *default_acl = NULL, *acl = NULL;417	int error;418 419	error = f2fs_acl_create(dir, &inode->i_mode, &default_acl, &acl, dpage);420	if (error)421		return error;422 423	f2fs_mark_inode_dirty_sync(inode, true);424 425	if (default_acl) {426		error = __f2fs_set_acl(NULL, inode, ACL_TYPE_DEFAULT, default_acl,427				       ipage);428		posix_acl_release(default_acl);429	} else {430		inode->i_default_acl = NULL;431	}432	if (acl) {433		if (!error)434			error = __f2fs_set_acl(NULL, inode, ACL_TYPE_ACCESS, acl,435					       ipage);436		posix_acl_release(acl);437	} else {438		inode->i_acl = NULL;439	}440 441	return error;442}443