xref: /openbmc/linux/fs/ext4/acl.c (revision 1bc33893)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2ac27a0ecSDave Kleikamp /*
3617ba13bSMingming Cao  * linux/fs/ext4/acl.c
4ac27a0ecSDave Kleikamp  *
5ac27a0ecSDave Kleikamp  * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
6ac27a0ecSDave Kleikamp  */
7ac27a0ecSDave Kleikamp 
8b8cb5a54STahsin Erdogan #include <linux/quotaops.h>
93dcf5451SChristoph Hellwig #include "ext4_jbd2.h"
103dcf5451SChristoph Hellwig #include "ext4.h"
11ac27a0ecSDave Kleikamp #include "xattr.h"
12ac27a0ecSDave Kleikamp #include "acl.h"
13ac27a0ecSDave Kleikamp 
14ac27a0ecSDave Kleikamp /*
15ac27a0ecSDave Kleikamp  * Convert from filesystem to in-memory representation.
16ac27a0ecSDave Kleikamp  */
17ac27a0ecSDave Kleikamp static struct posix_acl *
ext4_acl_from_disk(const void * value,size_t size)18617ba13bSMingming Cao ext4_acl_from_disk(const void *value, size_t size)
19ac27a0ecSDave Kleikamp {
20ac27a0ecSDave Kleikamp 	const char *end = (char *)value + size;
21ac27a0ecSDave Kleikamp 	int n, count;
22ac27a0ecSDave Kleikamp 	struct posix_acl *acl;
23ac27a0ecSDave Kleikamp 
24ac27a0ecSDave Kleikamp 	if (!value)
25ac27a0ecSDave Kleikamp 		return NULL;
26617ba13bSMingming Cao 	if (size < sizeof(ext4_acl_header))
27ac27a0ecSDave Kleikamp 		 return ERR_PTR(-EINVAL);
28617ba13bSMingming Cao 	if (((ext4_acl_header *)value)->a_version !=
29617ba13bSMingming Cao 	    cpu_to_le32(EXT4_ACL_VERSION))
30ac27a0ecSDave Kleikamp 		return ERR_PTR(-EINVAL);
31617ba13bSMingming Cao 	value = (char *)value + sizeof(ext4_acl_header);
32617ba13bSMingming Cao 	count = ext4_acl_count(size);
33ac27a0ecSDave Kleikamp 	if (count < 0)
34ac27a0ecSDave Kleikamp 		return ERR_PTR(-EINVAL);
35ac27a0ecSDave Kleikamp 	if (count == 0)
36ac27a0ecSDave Kleikamp 		return NULL;
37216553c4SJosef Bacik 	acl = posix_acl_alloc(count, GFP_NOFS);
38ac27a0ecSDave Kleikamp 	if (!acl)
39ac27a0ecSDave Kleikamp 		return ERR_PTR(-ENOMEM);
40ac27a0ecSDave Kleikamp 	for (n = 0; n < count; n++) {
41617ba13bSMingming Cao 		ext4_acl_entry *entry =
42617ba13bSMingming Cao 			(ext4_acl_entry *)value;
43617ba13bSMingming Cao 		if ((char *)value + sizeof(ext4_acl_entry_short) > end)
44ac27a0ecSDave Kleikamp 			goto fail;
45ac27a0ecSDave Kleikamp 		acl->a_entries[n].e_tag  = le16_to_cpu(entry->e_tag);
46ac27a0ecSDave Kleikamp 		acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
472b2d6d01STheodore Ts'o 
48ac27a0ecSDave Kleikamp 		switch (acl->a_entries[n].e_tag) {
49ac27a0ecSDave Kleikamp 		case ACL_USER_OBJ:
50ac27a0ecSDave Kleikamp 		case ACL_GROUP_OBJ:
51ac27a0ecSDave Kleikamp 		case ACL_MASK:
52ac27a0ecSDave Kleikamp 		case ACL_OTHER:
53ac27a0ecSDave Kleikamp 			value = (char *)value +
54617ba13bSMingming Cao 				sizeof(ext4_acl_entry_short);
55ac27a0ecSDave Kleikamp 			break;
56ac27a0ecSDave Kleikamp 
57ac27a0ecSDave Kleikamp 		case ACL_USER:
58af84df93SEric W. Biederman 			value = (char *)value + sizeof(ext4_acl_entry);
59af84df93SEric W. Biederman 			if ((char *)value > end)
60af84df93SEric W. Biederman 				goto fail;
61af84df93SEric W. Biederman 			acl->a_entries[n].e_uid =
62af84df93SEric W. Biederman 				make_kuid(&init_user_ns,
63af84df93SEric W. Biederman 					  le32_to_cpu(entry->e_id));
64af84df93SEric W. Biederman 			break;
65ac27a0ecSDave Kleikamp 		case ACL_GROUP:
66617ba13bSMingming Cao 			value = (char *)value + sizeof(ext4_acl_entry);
67ac27a0ecSDave Kleikamp 			if ((char *)value > end)
68ac27a0ecSDave Kleikamp 				goto fail;
69af84df93SEric W. Biederman 			acl->a_entries[n].e_gid =
70af84df93SEric W. Biederman 				make_kgid(&init_user_ns,
71af84df93SEric W. Biederman 					  le32_to_cpu(entry->e_id));
72ac27a0ecSDave Kleikamp 			break;
73ac27a0ecSDave Kleikamp 
74ac27a0ecSDave Kleikamp 		default:
75ac27a0ecSDave Kleikamp 			goto fail;
76ac27a0ecSDave Kleikamp 		}
77ac27a0ecSDave Kleikamp 	}
78ac27a0ecSDave Kleikamp 	if (value != end)
79ac27a0ecSDave Kleikamp 		goto fail;
80ac27a0ecSDave Kleikamp 	return acl;
81ac27a0ecSDave Kleikamp 
82ac27a0ecSDave Kleikamp fail:
83ac27a0ecSDave Kleikamp 	posix_acl_release(acl);
84ac27a0ecSDave Kleikamp 	return ERR_PTR(-EINVAL);
85ac27a0ecSDave Kleikamp }
86ac27a0ecSDave Kleikamp 
87ac27a0ecSDave Kleikamp /*
88ac27a0ecSDave Kleikamp  * Convert from in-memory to filesystem representation.
89ac27a0ecSDave Kleikamp  */
90ac27a0ecSDave Kleikamp static void *
ext4_acl_to_disk(const struct posix_acl * acl,size_t * size)91617ba13bSMingming Cao ext4_acl_to_disk(const struct posix_acl *acl, size_t *size)
92ac27a0ecSDave Kleikamp {
93617ba13bSMingming Cao 	ext4_acl_header *ext_acl;
94ac27a0ecSDave Kleikamp 	char *e;
95ac27a0ecSDave Kleikamp 	size_t n;
96ac27a0ecSDave Kleikamp 
97617ba13bSMingming Cao 	*size = ext4_acl_size(acl->a_count);
98617ba13bSMingming Cao 	ext_acl = kmalloc(sizeof(ext4_acl_header) + acl->a_count *
99216553c4SJosef Bacik 			sizeof(ext4_acl_entry), GFP_NOFS);
100ac27a0ecSDave Kleikamp 	if (!ext_acl)
101ac27a0ecSDave Kleikamp 		return ERR_PTR(-ENOMEM);
102617ba13bSMingming Cao 	ext_acl->a_version = cpu_to_le32(EXT4_ACL_VERSION);
103617ba13bSMingming Cao 	e = (char *)ext_acl + sizeof(ext4_acl_header);
104ac27a0ecSDave Kleikamp 	for (n = 0; n < acl->a_count; n++) {
105af84df93SEric W. Biederman 		const struct posix_acl_entry *acl_e = &acl->a_entries[n];
106617ba13bSMingming Cao 		ext4_acl_entry *entry = (ext4_acl_entry *)e;
107af84df93SEric W. Biederman 		entry->e_tag  = cpu_to_le16(acl_e->e_tag);
108af84df93SEric W. Biederman 		entry->e_perm = cpu_to_le16(acl_e->e_perm);
109af84df93SEric W. Biederman 		switch (acl_e->e_tag) {
110ac27a0ecSDave Kleikamp 		case ACL_USER:
111af84df93SEric W. Biederman 			entry->e_id = cpu_to_le32(
112af84df93SEric W. Biederman 				from_kuid(&init_user_ns, acl_e->e_uid));
113af84df93SEric W. Biederman 			e += sizeof(ext4_acl_entry);
114af84df93SEric W. Biederman 			break;
115ac27a0ecSDave Kleikamp 		case ACL_GROUP:
116af84df93SEric W. Biederman 			entry->e_id = cpu_to_le32(
117af84df93SEric W. Biederman 				from_kgid(&init_user_ns, acl_e->e_gid));
118617ba13bSMingming Cao 			e += sizeof(ext4_acl_entry);
119ac27a0ecSDave Kleikamp 			break;
120ac27a0ecSDave Kleikamp 
121ac27a0ecSDave Kleikamp 		case ACL_USER_OBJ:
122ac27a0ecSDave Kleikamp 		case ACL_GROUP_OBJ:
123ac27a0ecSDave Kleikamp 		case ACL_MASK:
124ac27a0ecSDave Kleikamp 		case ACL_OTHER:
125617ba13bSMingming Cao 			e += sizeof(ext4_acl_entry_short);
126ac27a0ecSDave Kleikamp 			break;
127ac27a0ecSDave Kleikamp 
128ac27a0ecSDave Kleikamp 		default:
129ac27a0ecSDave Kleikamp 			goto fail;
130ac27a0ecSDave Kleikamp 		}
131ac27a0ecSDave Kleikamp 	}
132ac27a0ecSDave Kleikamp 	return (char *)ext_acl;
133ac27a0ecSDave Kleikamp 
134ac27a0ecSDave Kleikamp fail:
135ac27a0ecSDave Kleikamp 	kfree(ext_acl);
136ac27a0ecSDave Kleikamp 	return ERR_PTR(-EINVAL);
137ac27a0ecSDave Kleikamp }
138ac27a0ecSDave Kleikamp 
139ac27a0ecSDave Kleikamp /*
140ac27a0ecSDave Kleikamp  * Inode operation get_posix_acl().
141ac27a0ecSDave Kleikamp  *
142f340b3d9Shongnanli  * inode->i_rwsem: don't care
143ac27a0ecSDave Kleikamp  */
1444e34e719SChristoph Hellwig struct posix_acl *
ext4_get_acl(struct inode * inode,int type,bool rcu)1450cad6246SMiklos Szeredi ext4_get_acl(struct inode *inode, int type, bool rcu)
146ac27a0ecSDave Kleikamp {
147ac27a0ecSDave Kleikamp 	int name_index;
148ac27a0ecSDave Kleikamp 	char *value = NULL;
149ac27a0ecSDave Kleikamp 	struct posix_acl *acl;
150ac27a0ecSDave Kleikamp 	int retval;
151ac27a0ecSDave Kleikamp 
1520cad6246SMiklos Szeredi 	if (rcu)
1530cad6246SMiklos Szeredi 		return ERR_PTR(-ECHILD);
1540cad6246SMiklos Szeredi 
155ac27a0ecSDave Kleikamp 	switch (type) {
156ac27a0ecSDave Kleikamp 	case ACL_TYPE_ACCESS:
157617ba13bSMingming Cao 		name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
158ac27a0ecSDave Kleikamp 		break;
159ac27a0ecSDave Kleikamp 	case ACL_TYPE_DEFAULT:
160617ba13bSMingming Cao 		name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT;
161ac27a0ecSDave Kleikamp 		break;
162ac27a0ecSDave Kleikamp 	default:
163073aaa1bSAl Viro 		BUG();
164ac27a0ecSDave Kleikamp 	}
165617ba13bSMingming Cao 	retval = ext4_xattr_get(inode, name_index, "", NULL, 0);
166ac27a0ecSDave Kleikamp 	if (retval > 0) {
167216553c4SJosef Bacik 		value = kmalloc(retval, GFP_NOFS);
168ac27a0ecSDave Kleikamp 		if (!value)
169ac27a0ecSDave Kleikamp 			return ERR_PTR(-ENOMEM);
170617ba13bSMingming Cao 		retval = ext4_xattr_get(inode, name_index, "", value, retval);
171ac27a0ecSDave Kleikamp 	}
172ac27a0ecSDave Kleikamp 	if (retval > 0)
173617ba13bSMingming Cao 		acl = ext4_acl_from_disk(value, retval);
174ac27a0ecSDave Kleikamp 	else if (retval == -ENODATA || retval == -ENOSYS)
175ac27a0ecSDave Kleikamp 		acl = NULL;
176ac27a0ecSDave Kleikamp 	else
177ac27a0ecSDave Kleikamp 		acl = ERR_PTR(retval);
178ac27a0ecSDave Kleikamp 	kfree(value);
179ac27a0ecSDave Kleikamp 
180ac27a0ecSDave Kleikamp 	return acl;
181ac27a0ecSDave Kleikamp }
182ac27a0ecSDave Kleikamp 
183ac27a0ecSDave Kleikamp /*
184ac27a0ecSDave Kleikamp  * Set the access or default ACL of an inode.
185ac27a0ecSDave Kleikamp  *
186f340b3d9Shongnanli  * inode->i_rwsem: down unless called from ext4_new_inode
187ac27a0ecSDave Kleikamp  */
188ac27a0ecSDave Kleikamp static int
__ext4_set_acl(handle_t * handle,struct inode * inode,int type,struct posix_acl * acl,int xattr_flags)18964e178a7SChristoph Hellwig __ext4_set_acl(handle_t *handle, struct inode *inode, int type,
190af65207cSTahsin Erdogan 	     struct posix_acl *acl, int xattr_flags)
191ac27a0ecSDave Kleikamp {
192ac27a0ecSDave Kleikamp 	int name_index;
193ac27a0ecSDave Kleikamp 	void *value = NULL;
194ac27a0ecSDave Kleikamp 	size_t size = 0;
195ac27a0ecSDave Kleikamp 	int error;
196ac27a0ecSDave Kleikamp 
197ac27a0ecSDave Kleikamp 	switch (type) {
198ac27a0ecSDave Kleikamp 	case ACL_TYPE_ACCESS:
199617ba13bSMingming Cao 		name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
200ac27a0ecSDave Kleikamp 		break;
201ac27a0ecSDave Kleikamp 
202ac27a0ecSDave Kleikamp 	case ACL_TYPE_DEFAULT:
203617ba13bSMingming Cao 		name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT;
204ac27a0ecSDave Kleikamp 		if (!S_ISDIR(inode->i_mode))
205ac27a0ecSDave Kleikamp 			return acl ? -EACCES : 0;
206ac27a0ecSDave Kleikamp 		break;
207ac27a0ecSDave Kleikamp 
208ac27a0ecSDave Kleikamp 	default:
209ac27a0ecSDave Kleikamp 		return -EINVAL;
210ac27a0ecSDave Kleikamp 	}
211ac27a0ecSDave Kleikamp 	if (acl) {
212617ba13bSMingming Cao 		value = ext4_acl_to_disk(acl, &size);
213ac27a0ecSDave Kleikamp 		if (IS_ERR(value))
214ac27a0ecSDave Kleikamp 			return (int)PTR_ERR(value);
215ac27a0ecSDave Kleikamp 	}
216ac27a0ecSDave Kleikamp 
217617ba13bSMingming Cao 	error = ext4_xattr_set_handle(handle, inode, name_index, "",
218af65207cSTahsin Erdogan 				      value, size, xattr_flags);
219ac27a0ecSDave Kleikamp 
220ac27a0ecSDave Kleikamp 	kfree(value);
2216b6aeffcSCarlos Guerrero Álvarez 	if (!error)
222073aaa1bSAl Viro 		set_cached_acl(inode, type, acl);
223ac27a0ecSDave Kleikamp 
224ac27a0ecSDave Kleikamp 	return error;
225ac27a0ecSDave Kleikamp }
226ac27a0ecSDave Kleikamp 
22764e178a7SChristoph Hellwig int
ext4_set_acl(struct mnt_idmap * idmap,struct dentry * dentry,struct posix_acl * acl,int type)22813e83a49SChristian Brauner ext4_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
229549c7297SChristian Brauner 	     struct posix_acl *acl, int type)
23064e178a7SChristoph Hellwig {
23164e178a7SChristoph Hellwig 	handle_t *handle;
232c1a5d5f6STahsin Erdogan 	int error, credits, retries = 0;
233c1a5d5f6STahsin Erdogan 	size_t acl_size = acl ? ext4_acl_size(acl->a_count) : 0;
234138060baSChristian Brauner 	struct inode *inode = d_inode(dentry);
235a3bb2d55SJan Kara 	umode_t mode = inode->i_mode;
236a3bb2d55SJan Kara 	int update_mode = 0;
23764e178a7SChristoph Hellwig 
238b8cb5a54STahsin Erdogan 	error = dquot_initialize(inode);
239b8cb5a54STahsin Erdogan 	if (error)
240b8cb5a54STahsin Erdogan 		return error;
24164e178a7SChristoph Hellwig retry:
242af65207cSTahsin Erdogan 	error = ext4_xattr_set_credits(inode, acl_size, false /* is_create */,
243af65207cSTahsin Erdogan 				       &credits);
244dec214d0STahsin Erdogan 	if (error)
245dec214d0STahsin Erdogan 		return error;
246dec214d0STahsin Erdogan 
247c1a5d5f6STahsin Erdogan 	handle = ext4_journal_start(inode, EXT4_HT_XATTR, credits);
24864e178a7SChristoph Hellwig 	if (IS_ERR(handle))
24964e178a7SChristoph Hellwig 		return PTR_ERR(handle);
25064e178a7SChristoph Hellwig 
251a3bb2d55SJan Kara 	if ((type == ACL_TYPE_ACCESS) && acl) {
252700b7940SChristian Brauner 		error = posix_acl_update_mode(idmap, inode, &mode, &acl);
253a3bb2d55SJan Kara 		if (error)
254a3bb2d55SJan Kara 			goto out_stop;
2550a1e8258SChengguang Xu 		if (mode != inode->i_mode)
256a3bb2d55SJan Kara 			update_mode = 1;
257a3bb2d55SJan Kara 	}
258a3bb2d55SJan Kara 
259af65207cSTahsin Erdogan 	error = __ext4_set_acl(handle, inode, type, acl, 0 /* xattr_flags */);
260a3bb2d55SJan Kara 	if (!error && update_mode) {
261a3bb2d55SJan Kara 		inode->i_mode = mode;
262*1bc33893SJeff Layton 		inode_set_ctime_current(inode);
2634209ae12SHarshad Shirwadkar 		error = ext4_mark_inode_dirty(handle, inode);
264a3bb2d55SJan Kara 	}
265a3bb2d55SJan Kara out_stop:
26664e178a7SChristoph Hellwig 	ext4_journal_stop(handle);
26764e178a7SChristoph Hellwig 	if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
26864e178a7SChristoph Hellwig 		goto retry;
26964e178a7SChristoph Hellwig 	return error;
27064e178a7SChristoph Hellwig }
27164e178a7SChristoph Hellwig 
272ac27a0ecSDave Kleikamp /*
273617ba13bSMingming Cao  * Initialize the ACLs of a new inode. Called from ext4_new_inode.
274ac27a0ecSDave Kleikamp  *
275f340b3d9Shongnanli  * dir->i_rwsem: down
276f340b3d9Shongnanli  * inode->i_rwsem: up (access to inode is still exclusive)
277ac27a0ecSDave Kleikamp  */
278ac27a0ecSDave Kleikamp int
ext4_init_acl(handle_t * handle,struct inode * inode,struct inode * dir)279617ba13bSMingming Cao ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
280ac27a0ecSDave Kleikamp {
28164e178a7SChristoph Hellwig 	struct posix_acl *default_acl, *acl;
282ac27a0ecSDave Kleikamp 	int error;
283ac27a0ecSDave Kleikamp 
28464e178a7SChristoph Hellwig 	error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
285bc26ab5fSAl Viro 	if (error)
286bc26ab5fSAl Viro 		return error;
28764e178a7SChristoph Hellwig 
28864e178a7SChristoph Hellwig 	if (default_acl) {
28964e178a7SChristoph Hellwig 		error = __ext4_set_acl(handle, inode, ACL_TYPE_DEFAULT,
290af65207cSTahsin Erdogan 				       default_acl, XATTR_CREATE);
29164e178a7SChristoph Hellwig 		posix_acl_release(default_acl);
2926fd94178SChengguang Xu 	} else {
2936fd94178SChengguang Xu 		inode->i_default_acl = NULL;
294ac27a0ecSDave Kleikamp 	}
29564e178a7SChristoph Hellwig 	if (acl) {
29664e178a7SChristoph Hellwig 		if (!error)
29764e178a7SChristoph Hellwig 			error = __ext4_set_acl(handle, inode, ACL_TYPE_ACCESS,
298af65207cSTahsin Erdogan 					       acl, XATTR_CREATE);
299bc26ab5fSAl Viro 		posix_acl_release(acl);
3006fd94178SChengguang Xu 	} else {
3016fd94178SChengguang Xu 		inode->i_acl = NULL;
30264e178a7SChristoph Hellwig 	}
303ac27a0ecSDave Kleikamp 	return error;
304ac27a0ecSDave Kleikamp }
305