Branch data 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 : 0 : ext4_acl_from_disk(const void *value, size_t size)
19 : : {
20 : 0 : const char *end = (char *)value + size;
21 : : int n, count;
22 : : struct posix_acl *acl;
23 : :
24 [ # # ]: 0 : if (!value)
25 : : return NULL;
26 [ # # ]: 0 : if (size < sizeof(ext4_acl_header))
27 : : return ERR_PTR(-EINVAL);
28 [ # # ]: 0 : if (((ext4_acl_header *)value)->a_version !=
29 : : cpu_to_le32(EXT4_ACL_VERSION))
30 : : return ERR_PTR(-EINVAL);
31 : 0 : value = (char *)value + sizeof(ext4_acl_header);
32 : : count = ext4_acl_count(size);
33 [ # # ]: 0 : if (count < 0)
34 : : return ERR_PTR(-EINVAL);
35 [ # # ]: 0 : if (count == 0)
36 : : return NULL;
37 : 0 : acl = posix_acl_alloc(count, GFP_NOFS);
38 [ # # ]: 0 : if (!acl)
39 : : return ERR_PTR(-ENOMEM);
40 [ # # ]: 0 : for (n = 0; n < count; n++) {
41 : : ext4_acl_entry *entry =
42 : : (ext4_acl_entry *)value;
43 [ # # ]: 0 : if ((char *)value + sizeof(ext4_acl_entry_short) > end)
44 : : goto fail;
45 : 0 : acl->a_entries[n].e_tag = le16_to_cpu(entry->e_tag);
46 : 0 : acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
47 : :
48 [ # # # # ]: 0 : 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 : 0 : value = (char *)value + sizeof(ext4_acl_entry);
59 [ # # ]: 0 : if ((char *)value > end)
60 : : goto fail;
61 : 0 : acl->a_entries[n].e_uid =
62 : 0 : make_kuid(&init_user_ns,
63 : : le32_to_cpu(entry->e_id));
64 : 0 : break;
65 : : case ACL_GROUP:
66 : 0 : value = (char *)value + sizeof(ext4_acl_entry);
67 [ # # ]: 0 : if ((char *)value > end)
68 : : goto fail;
69 : 0 : acl->a_entries[n].e_gid =
70 : 0 : make_kgid(&init_user_ns,
71 : : le32_to_cpu(entry->e_id));
72 : 0 : break;
73 : :
74 : : default:
75 : : goto fail;
76 : : }
77 : : }
78 [ # # ]: 0 : if (value != end)
79 : : goto fail;
80 : : return acl;
81 : :
82 : : 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 : 0 : 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 : 0 : *size = ext4_acl_size(acl->a_count);
98 : 0 : ext_acl = kmalloc(sizeof(ext4_acl_header) + acl->a_count *
99 : : sizeof(ext4_acl_entry), GFP_NOFS);
100 [ # # ]: 0 : if (!ext_acl)
101 : : return ERR_PTR(-ENOMEM);
102 : 0 : ext_acl->a_version = cpu_to_le32(EXT4_ACL_VERSION);
103 : 0 : e = (char *)ext_acl + sizeof(ext4_acl_header);
104 [ # # ]: 0 : for (n = 0; n < acl->a_count; n++) {
105 : 0 : const struct posix_acl_entry *acl_e = &acl->a_entries[n];
106 : : ext4_acl_entry *entry = (ext4_acl_entry *)e;
107 : 0 : entry->e_tag = cpu_to_le16(acl_e->e_tag);
108 : 0 : entry->e_perm = cpu_to_le16(acl_e->e_perm);
109 [ # # # # ]: 0 : switch (acl_e->e_tag) {
110 : : case ACL_USER:
111 : 0 : entry->e_id = cpu_to_le32(
112 : : from_kuid(&init_user_ns, acl_e->e_uid));
113 : 0 : e += sizeof(ext4_acl_entry);
114 : 0 : break;
115 : : case ACL_GROUP:
116 : 0 : entry->e_id = cpu_to_le32(
117 : : from_kgid(&init_user_ns, acl_e->e_gid));
118 : 0 : e += sizeof(ext4_acl_entry);
119 : 0 : break;
120 : :
121 : : case ACL_USER_OBJ:
122 : : case ACL_GROUP_OBJ:
123 : : case ACL_MASK:
124 : : case ACL_OTHER:
125 : 0 : e += sizeof(ext4_acl_entry_short);
126 : 0 : break;
127 : :
128 : : default:
129 : : 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_mutex: don't care
143 : : */
144 : : struct posix_acl *
145 : 673624 : ext4_get_acl(struct inode *inode, int type)
146 : : {
147 : : int name_index;
148 : : char *value = NULL;
149 : : struct posix_acl *acl;
150 : : int retval;
151 : :
152 [ + - + ]: 673624 : switch (type) {
153 : : case ACL_TYPE_ACCESS:
154 : : name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
155 : : break;
156 : : case ACL_TYPE_DEFAULT:
157 : : name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT;
158 : 3488 : break;
159 : : default:
160 : 0 : BUG();
161 : : }
162 : 673624 : retval = ext4_xattr_get(inode, name_index, "", NULL, 0);
163 [ - + ]: 673630 : if (retval > 0) {
164 : 0 : value = kmalloc(retval, GFP_NOFS);
165 [ # # ]: 0 : if (!value)
166 : : return ERR_PTR(-ENOMEM);
167 : 0 : retval = ext4_xattr_get(inode, name_index, "", value, retval);
168 : : }
169 [ - + ]: 673616 : if (retval > 0)
170 : 0 : acl = ext4_acl_from_disk(value, retval);
171 [ - + ]: 673616 : else if (retval == -ENODATA || retval == -ENOSYS)
172 : : acl = NULL;
173 : : else
174 : : acl = ERR_PTR(retval);
175 : 673616 : kfree(value);
176 : :
177 : 673634 : return acl;
178 : : }
179 : :
180 : : /*
181 : : * Set the access or default ACL of an inode.
182 : : *
183 : : * inode->i_mutex: down unless called from ext4_new_inode
184 : : */
185 : : static int
186 : 34 : __ext4_set_acl(handle_t *handle, struct inode *inode, int type,
187 : : struct posix_acl *acl, int xattr_flags)
188 : : {
189 : : int name_index;
190 : : void *value = NULL;
191 : 34 : size_t size = 0;
192 : : int error;
193 : :
194 [ - + - ]: 34 : switch (type) {
195 : : case ACL_TYPE_ACCESS:
196 : : name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
197 : : break;
198 : :
199 : : case ACL_TYPE_DEFAULT:
200 : : name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT;
201 [ # # ]: 0 : if (!S_ISDIR(inode->i_mode))
202 [ # # ]: 0 : return acl ? -EACCES : 0;
203 : : break;
204 : :
205 : : default:
206 : : return -EINVAL;
207 : : }
208 [ - + ]: 34 : if (acl) {
209 : 0 : value = ext4_acl_to_disk(acl, &size);
210 [ # # ]: 0 : if (IS_ERR(value))
211 : 0 : return (int)PTR_ERR(value);
212 : : }
213 : :
214 : 34 : error = ext4_xattr_set_handle(handle, inode, name_index, "",
215 : : value, size, xattr_flags);
216 : :
217 : 34 : kfree(value);
218 [ + - ]: 34 : if (!error) {
219 : 34 : set_cached_acl(inode, type, acl);
220 : : }
221 : :
222 : 34 : return error;
223 : : }
224 : :
225 : : int
226 : 34 : ext4_set_acl(struct inode *inode, struct posix_acl *acl, int type)
227 : : {
228 : : handle_t *handle;
229 : 34 : int error, credits, retries = 0;
230 [ + - ]: 34 : size_t acl_size = acl ? ext4_acl_size(acl->a_count) : 0;
231 : 34 : umode_t mode = inode->i_mode;
232 : : int update_mode = 0;
233 : :
234 : 34 : error = dquot_initialize(inode);
235 [ + - ]: 34 : if (error)
236 : : return error;
237 : : retry:
238 : 34 : error = ext4_xattr_set_credits(inode, acl_size, false /* is_create */,
239 : : &credits);
240 [ - + ]: 34 : if (error)
241 : 0 : return error;
242 : :
243 : 34 : handle = ext4_journal_start(inode, EXT4_HT_XATTR, credits);
244 [ - + ]: 34 : if (IS_ERR(handle))
245 : 0 : return PTR_ERR(handle);
246 : :
247 [ + - ]: 34 : if ((type == ACL_TYPE_ACCESS) && acl) {
248 : 34 : error = posix_acl_update_mode(inode, &mode, &acl);
249 [ + - ]: 34 : if (error)
250 : : goto out_stop;
251 [ + + ]: 34 : if (mode != inode->i_mode)
252 : : update_mode = 1;
253 : : }
254 : :
255 : 34 : error = __ext4_set_acl(handle, inode, type, acl, 0 /* xattr_flags */);
256 [ + + ]: 34 : if (!error && update_mode) {
257 : 32 : inode->i_mode = mode;
258 : 32 : inode->i_ctime = current_time(inode);
259 : 32 : ext4_mark_inode_dirty(handle, inode);
260 : : }
261 : : out_stop:
262 : 34 : ext4_journal_stop(handle);
263 [ - + # # ]: 34 : if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
264 : : goto retry;
265 : 34 : return error;
266 : : }
267 : :
268 : : /*
269 : : * Initialize the ACLs of a new inode. Called from ext4_new_inode.
270 : : *
271 : : * dir->i_mutex: down
272 : : * inode->i_mutex: up (access to inode is still exclusive)
273 : : */
274 : : int
275 : 285052 : ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
276 : : {
277 : : struct posix_acl *default_acl, *acl;
278 : : int error;
279 : :
280 : 285052 : error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
281 [ + - ]: 285052 : if (error)
282 : : return error;
283 : :
284 [ - + ]: 285052 : if (default_acl) {
285 : 0 : error = __ext4_set_acl(handle, inode, ACL_TYPE_DEFAULT,
286 : : default_acl, XATTR_CREATE);
287 : 0 : posix_acl_release(default_acl);
288 : : } else {
289 : 285052 : inode->i_default_acl = NULL;
290 : : }
291 [ - + ]: 285052 : if (acl) {
292 [ # # ]: 0 : if (!error)
293 : 0 : error = __ext4_set_acl(handle, inode, ACL_TYPE_ACCESS,
294 : : acl, XATTR_CREATE);
295 : 0 : posix_acl_release(acl);
296 : : } else {
297 : 285052 : inode->i_acl = NULL;
298 : : }
299 : 285052 : return error;
300 : : }
|