xref: /openbmc/linux/fs/ext2/acl.c (revision 2243acd5)
1  // SPDX-License-Identifier: GPL-2.0
2  /*
3   * linux/fs/ext2/acl.c
4   *
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   */
19  static struct posix_acl *
20  ext2_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  
83  fail:
84  	posix_acl_release(acl);
85  	return ERR_PTR(-EINVAL);
86  }
87  
88  /*
89   * Convert from in-memory to filesystem representation.
90   */
91  static void *
92  ext2_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  
135  fail:
136  	kfree(ext_acl);
137  	return ERR_PTR(-EINVAL);
138  }
139  
140  /*
141   * inode->i_mutex: don't care
142   */
143  struct posix_acl *
144  ext2_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  	else
176  		acl = ERR_PTR(retval);
177  	kfree(value);
178  
179  	return acl;
180  }
181  
182  static int
183  __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: down
220   */
221  int
222  ext2_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->i_ctime = current_time(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: down
250   * inode->i_mutex: up (access to inode is still exclusive)
251   */
252  int
253  ext2_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