Line data Source code
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 3 : ext4_acl_from_disk(const void *value, size_t size)
19 : {
20 3 : const char *end = (char *)value + size;
21 3 : int n, count;
22 3 : struct posix_acl *acl;
23 :
24 3 : if (!value)
25 : return NULL;
26 3 : if (size < sizeof(ext4_acl_header))
27 : return ERR_PTR(-EINVAL);
28 3 : if (((ext4_acl_header *)value)->a_version !=
29 : cpu_to_le32(EXT4_ACL_VERSION))
30 : return ERR_PTR(-EINVAL);
31 3 : value = (char *)value + sizeof(ext4_acl_header);
32 3 : count = ext4_acl_count(size);
33 3 : if (count < 0)
34 : return ERR_PTR(-EINVAL);
35 3 : if (count == 0)
36 : return NULL;
37 3 : acl = posix_acl_alloc(count, GFP_NOFS);
38 3 : if (!acl)
39 : return ERR_PTR(-ENOMEM);
40 20 : for (n = 0; n < count; n++) {
41 17 : ext4_acl_entry *entry =
42 : (ext4_acl_entry *)value;
43 17 : if ((char *)value + sizeof(ext4_acl_entry_short) > end)
44 0 : goto fail;
45 17 : acl->a_entries[n].e_tag = le16_to_cpu(entry->e_tag);
46 17 : acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
47 :
48 17 : 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 3 : case ACL_USER:
58 3 : value = (char *)value + sizeof(ext4_acl_entry);
59 3 : if ((char *)value > end)
60 0 : goto fail;
61 3 : acl->a_entries[n].e_uid =
62 3 : make_kuid(&init_user_ns,
63 3 : le32_to_cpu(entry->e_id));
64 3 : break;
65 2 : case ACL_GROUP:
66 2 : value = (char *)value + sizeof(ext4_acl_entry);
67 2 : if ((char *)value > end)
68 0 : goto fail;
69 2 : acl->a_entries[n].e_gid =
70 2 : make_kgid(&init_user_ns,
71 2 : le32_to_cpu(entry->e_id));
72 2 : break;
73 :
74 0 : default:
75 0 : goto fail;
76 : }
77 : }
78 3 : if (value != end)
79 0 : goto fail;
80 : return acl;
81 :
82 0 : fail:
83 0 : posix_acl_release(acl);
84 0 : return ERR_PTR(-EINVAL);
85 : }
86 :
87 : /*
88 : * Convert from in-memory to filesystem representation.
89 : */
90 : static void *
91 2569 : ext4_acl_to_disk(const struct posix_acl *acl, size_t *size)
92 : {
93 2569 : ext4_acl_header *ext_acl;
94 2569 : char *e;
95 2569 : size_t n;
96 :
97 2569 : *size = ext4_acl_size(acl->a_count);
98 2569 : ext_acl = kmalloc(sizeof(ext4_acl_header) + acl->a_count *
99 : sizeof(ext4_acl_entry), GFP_NOFS);
100 2569 : if (!ext_acl)
101 : return ERR_PTR(-ENOMEM);
102 2569 : ext_acl->a_version = cpu_to_le32(EXT4_ACL_VERSION);
103 2569 : e = (char *)ext_acl + sizeof(ext4_acl_header);
104 15365 : for (n = 0; n < acl->a_count; n++) {
105 12796 : const struct posix_acl_entry *acl_e = &acl->a_entries[n];
106 12796 : ext4_acl_entry *entry = (ext4_acl_entry *)e;
107 12796 : entry->e_tag = cpu_to_le16(acl_e->e_tag);
108 12796 : entry->e_perm = cpu_to_le16(acl_e->e_perm);
109 12796 : switch (acl_e->e_tag) {
110 2519 : case ACL_USER:
111 2519 : entry->e_id = cpu_to_le32(
112 : from_kuid(&init_user_ns, acl_e->e_uid));
113 2519 : e += sizeof(ext4_acl_entry);
114 2519 : break;
115 5 : case ACL_GROUP:
116 5 : entry->e_id = cpu_to_le32(
117 : from_kgid(&init_user_ns, acl_e->e_gid));
118 5 : e += sizeof(ext4_acl_entry);
119 5 : break;
120 :
121 10272 : case ACL_USER_OBJ:
122 : case ACL_GROUP_OBJ:
123 : case ACL_MASK:
124 : case ACL_OTHER:
125 10272 : e += sizeof(ext4_acl_entry_short);
126 10272 : break;
127 :
128 0 : default:
129 0 : goto fail;
130 : }
131 : }
132 : return (char *)ext_acl;
133 :
134 : fail:
135 0 : kfree(ext_acl);
136 0 : return ERR_PTR(-EINVAL);
137 : }
138 :
139 : /*
140 : * Inode operation get_posix_acl().
141 : *
142 : * inode->i_rwsem: don't care
143 : */
144 : struct posix_acl *
145 13669 : ext4_get_acl(struct inode *inode, int type, bool rcu)
146 : {
147 13669 : int name_index;
148 13669 : char *value = NULL;
149 13669 : struct posix_acl *acl;
150 13669 : int retval;
151 :
152 13669 : if (rcu)
153 : return ERR_PTR(-ECHILD);
154 :
155 13669 : switch (type) {
156 : case ACL_TYPE_ACCESS:
157 : name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
158 : break;
159 1148 : case ACL_TYPE_DEFAULT:
160 1148 : name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT;
161 1148 : break;
162 0 : default:
163 0 : BUG();
164 : }
165 13669 : retval = ext4_xattr_get(inode, name_index, "", NULL, 0);
166 13669 : if (retval > 0) {
167 3 : value = kmalloc(retval, GFP_NOFS);
168 3 : if (!value)
169 : return ERR_PTR(-ENOMEM);
170 3 : retval = ext4_xattr_get(inode, name_index, "", value, retval);
171 : }
172 13669 : if (retval > 0)
173 3 : acl = ext4_acl_from_disk(value, retval);
174 13666 : else if (retval == -ENODATA || retval == -ENOSYS)
175 : acl = NULL;
176 : else
177 0 : acl = ERR_PTR(retval);
178 13669 : kfree(value);
179 :
180 13669 : 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_inode
187 : */
188 : static int
189 925578 : __ext4_set_acl(handle_t *handle, struct inode *inode, int type,
190 : struct posix_acl *acl, int xattr_flags)
191 : {
192 925578 : int name_index;
193 925578 : void *value = NULL;
194 925578 : size_t size = 0;
195 925578 : int error;
196 :
197 925578 : switch (type) {
198 : case ACL_TYPE_ACCESS:
199 : name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
200 : break;
201 :
202 10584 : case ACL_TYPE_DEFAULT:
203 10584 : name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT;
204 10584 : if (!S_ISDIR(inode->i_mode))
205 0 : return acl ? -EACCES : 0;
206 : break;
207 :
208 : default:
209 : return -EINVAL;
210 : }
211 925578 : if (acl) {
212 2569 : value = ext4_acl_to_disk(acl, &size);
213 2569 : if (IS_ERR(value))
214 0 : return (int)PTR_ERR(value);
215 : }
216 :
217 925578 : error = ext4_xattr_set_handle(handle, inode, name_index, "",
218 : value, size, xattr_flags);
219 :
220 925590 : kfree(value);
221 925589 : if (!error)
222 925588 : set_cached_acl(inode, type, acl);
223 :
224 : return error;
225 : }
226 :
227 : int
228 923043 : ext4_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
229 : struct posix_acl *acl, int type)
230 : {
231 923043 : handle_t *handle;
232 923043 : int error, credits, retries = 0;
233 923043 : size_t acl_size = acl ? ext4_acl_size(acl->a_count) : 0;
234 923043 : struct inode *inode = d_inode(dentry);
235 923043 : umode_t mode = inode->i_mode;
236 923043 : int update_mode = 0;
237 :
238 923043 : error = dquot_initialize(inode);
239 923044 : if (error)
240 : return error;
241 923044 : retry:
242 923044 : error = ext4_xattr_set_credits(inode, acl_size, false /* is_create */,
243 : &credits);
244 923041 : if (error)
245 0 : return error;
246 :
247 923041 : handle = ext4_journal_start(inode, EXT4_HT_XATTR, credits);
248 923041 : if (IS_ERR(handle))
249 0 : return PTR_ERR(handle);
250 :
251 923041 : if ((type == ACL_TYPE_ACCESS) && acl) {
252 910806 : error = posix_acl_update_mode(idmap, inode, &mode, &acl);
253 910807 : if (error)
254 0 : goto out_stop;
255 910807 : if (mode != inode->i_mode)
256 910793 : update_mode = 1;
257 : }
258 :
259 923042 : error = __ext4_set_acl(handle, inode, type, acl, 0 /* xattr_flags */);
260 923053 : if (!error && update_mode) {
261 910799 : inode->i_mode = mode;
262 910799 : inode->i_ctime = current_time(inode);
263 910795 : error = ext4_mark_inode_dirty(handle, inode);
264 : }
265 12254 : out_stop:
266 923051 : ext4_journal_stop(handle);
267 923058 : if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
268 0 : 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: down
276 : * inode->i_rwsem: up (access to inode is still exclusive)
277 : */
278 : int
279 2782113 : ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
280 : {
281 2782113 : struct posix_acl *default_acl, *acl;
282 2782113 : int error;
283 :
284 2782113 : error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
285 2762641 : if (error)
286 : return error;
287 :
288 2762641 : if (default_acl) {
289 222 : error = __ext4_set_acl(handle, inode, ACL_TYPE_DEFAULT,
290 : default_acl, XATTR_CREATE);
291 222 : posix_acl_release(default_acl);
292 : } else {
293 2762419 : inode->i_default_acl = NULL;
294 : }
295 2763244 : if (acl) {
296 2317 : if (!error)
297 2317 : error = __ext4_set_acl(handle, inode, ACL_TYPE_ACCESS,
298 : acl, XATTR_CREATE);
299 2317 : posix_acl_release(acl);
300 : } else {
301 2760927 : inode->i_acl = NULL;
302 : }
303 : return error;
304 : }
|