brintos

brintos / linux-shallow public Read only

0
0
Text · 6.7 KiB · 3bffe86 Raw
305 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * linux/fs/ext4/acl.c4 *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 care143 */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	else177		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_inode187 */188static int189__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 227int228ext4_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,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	struct inode *inode = d_inode(dentry);235	umode_t mode = inode->i_mode;236	int update_mode = 0;237 238	error = dquot_initialize(inode);239	if (error)240		return error;241retry:242	error = ext4_xattr_set_credits(inode, acl_size, false /* is_create */,243				       &credits);244	if (error)245		return error;246 247	handle = ext4_journal_start(inode, EXT4_HT_XATTR, credits);248	if (IS_ERR(handle))249		return PTR_ERR(handle);250 251	if ((type == ACL_TYPE_ACCESS) && acl) {252		error = posix_acl_update_mode(idmap, inode, &mode, &acl);253		if (error)254			goto out_stop;255		if (mode != inode->i_mode)256			update_mode = 1;257	}258 259	error = __ext4_set_acl(handle, inode, type, acl, 0 /* xattr_flags */);260	if (!error && update_mode) {261		inode->i_mode = mode;262		inode_set_ctime_current(inode);263		error = ext4_mark_inode_dirty(handle, inode);264	}265out_stop:266	ext4_journal_stop(handle);267	if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))268		goto retry;269	return error;270}271 272/*273 * Initialize the ACLs of a new inode. Called from ext4_new_inode.274 *275 * dir->i_rwsem: down276 * inode->i_rwsem: up (access to inode is still exclusive)277 */278int279ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)280{281	struct posix_acl *default_acl, *acl;282	int error;283 284	error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);285	if (error)286		return error;287 288	if (default_acl) {289		error = __ext4_set_acl(handle, inode, ACL_TYPE_DEFAULT,290				       default_acl, XATTR_CREATE);291		posix_acl_release(default_acl);292	} else {293		inode->i_default_acl = NULL;294	}295	if (acl) {296		if (!error)297			error = __ext4_set_acl(handle, inode, ACL_TYPE_ACCESS,298					       acl, XATTR_CREATE);299		posix_acl_release(acl);300	} else {301		inode->i_acl = NULL;302	}303	return error;304}305