xref: /openbmc/linux/fs/ext4/acl.c (revision e330fb14)
1  // SPDX-License-Identifier: GPL-2.0
2  /*
3   * linux/fs/ext4/acl.c
4   *
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   */
17  static struct posix_acl *
18  ext4_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  
82  fail:
83  	posix_acl_release(acl);
84  	return ERR_PTR(-EINVAL);
85  }
86  
87  /*
88   * Convert from in-memory to filesystem representation.
89   */
90  static void *
91  ext4_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  
134  fail:
135  	kfree(ext_acl);
136  	return ERR_PTR(-EINVAL);
137  }
138  
139  /*
140   * Inode operation get_posix_acl().
141   *
142   * inode->i_mutex: don't care
143   */
144  struct posix_acl *
145  ext4_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  	else
177  		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_mutex: down unless called from ext4_new_inode
187   */
188  static int
189  __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  
227  int
228  ext4_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
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  	umode_t mode = inode->i_mode;
235  	int update_mode = 0;
236  
237  	error = dquot_initialize(inode);
238  	if (error)
239  		return error;
240  retry:
241  	error = ext4_xattr_set_credits(inode, acl_size, false /* is_create */,
242  				       &credits);
243  	if (error)
244  		return error;
245  
246  	handle = ext4_journal_start(inode, EXT4_HT_XATTR, credits);
247  	if (IS_ERR(handle))
248  		return PTR_ERR(handle);
249  	ext4_fc_start_update(inode);
250  
251  	if ((type == ACL_TYPE_ACCESS) && acl) {
252  		error = posix_acl_update_mode(mnt_userns, 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->i_ctime = current_time(inode);
263  		error = ext4_mark_inode_dirty(handle, inode);
264  	}
265  out_stop:
266  	ext4_journal_stop(handle);
267  	ext4_fc_stop_update(inode);
268  	if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
269  		goto retry;
270  	return error;
271  }
272  
273  /*
274   * Initialize the ACLs of a new inode. Called from ext4_new_inode.
275   *
276   * dir->i_mutex: down
277   * inode->i_mutex: up (access to inode is still exclusive)
278   */
279  int
280  ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
281  {
282  	struct posix_acl *default_acl, *acl;
283  	int error;
284  
285  	error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
286  	if (error)
287  		return error;
288  
289  	if (default_acl) {
290  		error = __ext4_set_acl(handle, inode, ACL_TYPE_DEFAULT,
291  				       default_acl, XATTR_CREATE);
292  		posix_acl_release(default_acl);
293  	} else {
294  		inode->i_default_acl = NULL;
295  	}
296  	if (acl) {
297  		if (!error)
298  			error = __ext4_set_acl(handle, inode, ACL_TYPE_ACCESS,
299  					       acl, XATTR_CREATE);
300  		posix_acl_release(acl);
301  	} else {
302  		inode->i_acl = NULL;
303  	}
304  	return error;
305  }
306