brintos

brintos / linux-shallow public Read only

0
0
Text · 5.9 KiB · 7e54c31 Raw
277 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * linux/fs/ext2/acl.c4 *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 care142 */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	else176		acl = ERR_PTR(retval);177	kfree(value);178 179	return acl;180}181 182static int183__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: down220 */221int222ext2_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,223	     struct posix_acl *acl, int type)224{225	int error;226	int update_mode = 0;227	struct inode *inode = d_inode(dentry);228	umode_t mode = inode->i_mode;229 230	if (type == ACL_TYPE_ACCESS && acl) {231		error = posix_acl_update_mode(&nop_mnt_idmap, inode, &mode,232					      &acl);233		if (error)234			return error;235		update_mode = 1;236	}237	error = __ext2_set_acl(inode, acl, type);238	if (!error && update_mode) {239		inode->i_mode = mode;240		inode_set_ctime_current(inode);241		mark_inode_dirty(inode);242	}243	return error;244}245 246/*247 * Initialize the ACLs of a new inode. Called from ext2_new_inode.248 *249 * dir->i_mutex: down250 * inode->i_mutex: up (access to inode is still exclusive)251 */252int253ext2_init_acl(struct inode *inode, struct inode *dir)254{255	struct posix_acl *default_acl, *acl;256	int error;257 258	error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);259	if (error)260		return error;261 262	if (default_acl) {263		error = __ext2_set_acl(inode, default_acl, ACL_TYPE_DEFAULT);264		posix_acl_release(default_acl);265	} else {266		inode->i_default_acl = NULL;267	}268	if (acl) {269		if (!error)270			error = __ext2_set_acl(inode, acl, ACL_TYPE_ACCESS);271		posix_acl_release(acl);272	} else {273		inode->i_acl = NULL;274	}275	return error;276}277