Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0
2 : : /*
3 : : * fs/f2fs/acl.c
4 : : *
5 : : * Copyright (c) 2012 Samsung Electronics Co., Ltd.
6 : : * http://www.samsung.com/
7 : : *
8 : : * Portions of this code from linux/fs/ext2/acl.c
9 : : *
10 : : * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
11 : : */
12 : : #include <linux/f2fs_fs.h>
13 : : #include "f2fs.h"
14 : : #include "xattr.h"
15 : : #include "acl.h"
16 : :
17 : : static inline size_t f2fs_acl_size(int count)
18 : : {
19 [ # # ]: 0 : if (count <= 4) {
20 : 0 : return sizeof(struct f2fs_acl_header) +
21 : : count * sizeof(struct f2fs_acl_entry_short);
22 : : } else {
23 : 0 : return sizeof(struct f2fs_acl_header) +
24 : : 4 * sizeof(struct f2fs_acl_entry_short) +
25 : 0 : (count - 4) * sizeof(struct f2fs_acl_entry);
26 : : }
27 : : }
28 : :
29 : : static inline int f2fs_acl_count(size_t size)
30 : : {
31 : : ssize_t s;
32 : 0 : size -= sizeof(struct f2fs_acl_header);
33 : 0 : s = size - 4 * sizeof(struct f2fs_acl_entry_short);
34 [ # # ]: 0 : if (s < 0) {
35 [ # # ]: 0 : if (size % sizeof(struct f2fs_acl_entry_short))
36 : : return -1;
37 : 0 : return size / sizeof(struct f2fs_acl_entry_short);
38 : : } else {
39 [ # # ]: 0 : if (s % sizeof(struct f2fs_acl_entry))
40 : : return -1;
41 : 0 : return s / sizeof(struct f2fs_acl_entry) + 4;
42 : : }
43 : : }
44 : :
45 : 0 : static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size)
46 : : {
47 : : int i, count;
48 : : struct posix_acl *acl;
49 : : struct f2fs_acl_header *hdr = (struct f2fs_acl_header *)value;
50 : 0 : struct f2fs_acl_entry *entry = (struct f2fs_acl_entry *)(hdr + 1);
51 : 0 : const char *end = value + size;
52 : :
53 [ # # ]: 0 : if (size < sizeof(struct f2fs_acl_header))
54 : : return ERR_PTR(-EINVAL);
55 : :
56 [ # # ]: 0 : if (hdr->a_version != cpu_to_le32(F2FS_ACL_VERSION))
57 : : return ERR_PTR(-EINVAL);
58 : :
59 : : count = f2fs_acl_count(size);
60 [ # # ]: 0 : if (count < 0)
61 : : return ERR_PTR(-EINVAL);
62 [ # # ]: 0 : if (count == 0)
63 : : return NULL;
64 : :
65 : 0 : acl = posix_acl_alloc(count, GFP_NOFS);
66 [ # # ]: 0 : if (!acl)
67 : : return ERR_PTR(-ENOMEM);
68 : :
69 [ # # ]: 0 : for (i = 0; i < count; i++) {
70 : :
71 [ # # ]: 0 : if ((char *)entry > end)
72 : : goto fail;
73 : :
74 : 0 : acl->a_entries[i].e_tag = le16_to_cpu(entry->e_tag);
75 : 0 : acl->a_entries[i].e_perm = le16_to_cpu(entry->e_perm);
76 : :
77 [ # # # # ]: 0 : switch (acl->a_entries[i].e_tag) {
78 : : case ACL_USER_OBJ:
79 : : case ACL_GROUP_OBJ:
80 : : case ACL_MASK:
81 : : case ACL_OTHER:
82 : 0 : entry = (struct f2fs_acl_entry *)((char *)entry +
83 : : sizeof(struct f2fs_acl_entry_short));
84 : 0 : break;
85 : :
86 : : case ACL_USER:
87 : 0 : acl->a_entries[i].e_uid =
88 : 0 : make_kuid(&init_user_ns,
89 : : le32_to_cpu(entry->e_id));
90 : 0 : entry = (struct f2fs_acl_entry *)((char *)entry +
91 : : sizeof(struct f2fs_acl_entry));
92 : 0 : break;
93 : : case ACL_GROUP:
94 : 0 : acl->a_entries[i].e_gid =
95 : 0 : make_kgid(&init_user_ns,
96 : : le32_to_cpu(entry->e_id));
97 : 0 : entry = (struct f2fs_acl_entry *)((char *)entry +
98 : : sizeof(struct f2fs_acl_entry));
99 : 0 : break;
100 : : default:
101 : : goto fail;
102 : : }
103 : : }
104 [ # # ]: 0 : if ((char *)entry != end)
105 : : goto fail;
106 : : return acl;
107 : : fail:
108 : 0 : posix_acl_release(acl);
109 : 0 : return ERR_PTR(-EINVAL);
110 : : }
111 : :
112 : 0 : static void *f2fs_acl_to_disk(struct f2fs_sb_info *sbi,
113 : : const struct posix_acl *acl, size_t *size)
114 : : {
115 : : struct f2fs_acl_header *f2fs_acl;
116 : : struct f2fs_acl_entry *entry;
117 : : int i;
118 : :
119 : 0 : f2fs_acl = f2fs_kmalloc(sbi, sizeof(struct f2fs_acl_header) +
120 : 0 : acl->a_count * sizeof(struct f2fs_acl_entry),
121 : : GFP_NOFS);
122 [ # # ]: 0 : if (!f2fs_acl)
123 : : return ERR_PTR(-ENOMEM);
124 : :
125 : 0 : f2fs_acl->a_version = cpu_to_le32(F2FS_ACL_VERSION);
126 : 0 : entry = (struct f2fs_acl_entry *)(f2fs_acl + 1);
127 : :
128 [ # # ]: 0 : for (i = 0; i < acl->a_count; i++) {
129 : :
130 : 0 : entry->e_tag = cpu_to_le16(acl->a_entries[i].e_tag);
131 : 0 : entry->e_perm = cpu_to_le16(acl->a_entries[i].e_perm);
132 : :
133 [ # # # # ]: 0 : switch (acl->a_entries[i].e_tag) {
134 : : case ACL_USER:
135 : 0 : entry->e_id = cpu_to_le32(
136 : : from_kuid(&init_user_ns,
137 : : acl->a_entries[i].e_uid));
138 : 0 : entry = (struct f2fs_acl_entry *)((char *)entry +
139 : : sizeof(struct f2fs_acl_entry));
140 : 0 : break;
141 : : case ACL_GROUP:
142 : 0 : entry->e_id = cpu_to_le32(
143 : : from_kgid(&init_user_ns,
144 : : acl->a_entries[i].e_gid));
145 : 0 : entry = (struct f2fs_acl_entry *)((char *)entry +
146 : : sizeof(struct f2fs_acl_entry));
147 : 0 : break;
148 : : case ACL_USER_OBJ:
149 : : case ACL_GROUP_OBJ:
150 : : case ACL_MASK:
151 : : case ACL_OTHER:
152 : 0 : entry = (struct f2fs_acl_entry *)((char *)entry +
153 : : sizeof(struct f2fs_acl_entry_short));
154 : 0 : break;
155 : : default:
156 : : goto fail;
157 : : }
158 : : }
159 : 0 : *size = f2fs_acl_size(acl->a_count);
160 : 0 : return (void *)f2fs_acl;
161 : :
162 : : fail:
163 : 0 : kvfree(f2fs_acl);
164 : 0 : return ERR_PTR(-EINVAL);
165 : : }
166 : :
167 : 0 : static struct posix_acl *__f2fs_get_acl(struct inode *inode, int type,
168 : : struct page *dpage)
169 : : {
170 : : int name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT;
171 : : void *value = NULL;
172 : : struct posix_acl *acl;
173 : : int retval;
174 : :
175 [ # # ]: 0 : if (type == ACL_TYPE_ACCESS)
176 : : name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
177 : :
178 : 0 : retval = f2fs_getxattr(inode, name_index, "", NULL, 0, dpage);
179 [ # # ]: 0 : if (retval > 0) {
180 : 0 : value = f2fs_kmalloc(F2FS_I_SB(inode), retval, GFP_F2FS_ZERO);
181 [ # # ]: 0 : if (!value)
182 : : return ERR_PTR(-ENOMEM);
183 : 0 : retval = f2fs_getxattr(inode, name_index, "", value,
184 : : retval, dpage);
185 : : }
186 : :
187 [ # # ]: 0 : if (retval > 0)
188 : 0 : acl = f2fs_acl_from_disk(value, retval);
189 [ # # ]: 0 : else if (retval == -ENODATA)
190 : : acl = NULL;
191 : : else
192 : : acl = ERR_PTR(retval);
193 : 0 : kvfree(value);
194 : :
195 : 0 : return acl;
196 : : }
197 : :
198 : 0 : struct posix_acl *f2fs_get_acl(struct inode *inode, int type)
199 : : {
200 : 0 : return __f2fs_get_acl(inode, type, NULL);
201 : : }
202 : :
203 : 0 : static int __f2fs_set_acl(struct inode *inode, int type,
204 : : struct posix_acl *acl, struct page *ipage)
205 : : {
206 : : int name_index;
207 : : void *value = NULL;
208 : 0 : size_t size = 0;
209 : : int error;
210 : 0 : umode_t mode = inode->i_mode;
211 : :
212 [ # # # ]: 0 : switch (type) {
213 : : case ACL_TYPE_ACCESS:
214 : : name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
215 [ # # ]: 0 : if (acl && !ipage) {
216 : 0 : error = posix_acl_update_mode(inode, &mode, &acl);
217 [ # # ]: 0 : if (error)
218 : : return error;
219 : 0 : set_acl_inode(inode, mode);
220 : : }
221 : : break;
222 : :
223 : : case ACL_TYPE_DEFAULT:
224 : : name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT;
225 [ # # ]: 0 : if (!S_ISDIR(inode->i_mode))
226 [ # # ]: 0 : return acl ? -EACCES : 0;
227 : : break;
228 : :
229 : : default:
230 : : return -EINVAL;
231 : : }
232 : :
233 [ # # ]: 0 : if (acl) {
234 : 0 : value = f2fs_acl_to_disk(F2FS_I_SB(inode), acl, &size);
235 [ # # ]: 0 : if (IS_ERR(value)) {
236 : 0 : clear_inode_flag(inode, FI_ACL_MODE);
237 : 0 : return PTR_ERR(value);
238 : : }
239 : : }
240 : :
241 : 0 : error = f2fs_setxattr(inode, name_index, "", value, size, ipage, 0);
242 : :
243 : 0 : kvfree(value);
244 [ # # ]: 0 : if (!error)
245 : 0 : set_cached_acl(inode, type, acl);
246 : :
247 : 0 : clear_inode_flag(inode, FI_ACL_MODE);
248 : 0 : return error;
249 : : }
250 : :
251 : 0 : int f2fs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
252 : : {
253 [ # # ]: 0 : if (unlikely(f2fs_cp_error(F2FS_I_SB(inode))))
254 : : return -EIO;
255 : :
256 : 0 : return __f2fs_set_acl(inode, type, acl, NULL);
257 : : }
258 : :
259 : : /*
260 : : * Most part of f2fs_acl_clone, f2fs_acl_create_masq, f2fs_acl_create
261 : : * are copied from posix_acl.c
262 : : */
263 : 0 : static struct posix_acl *f2fs_acl_clone(const struct posix_acl *acl,
264 : : gfp_t flags)
265 : : {
266 : : struct posix_acl *clone = NULL;
267 : :
268 [ # # ]: 0 : if (acl) {
269 : 0 : int size = sizeof(struct posix_acl) + acl->a_count *
270 : : sizeof(struct posix_acl_entry);
271 : 0 : clone = kmemdup(acl, size, flags);
272 [ # # ]: 0 : if (clone)
273 : : refcount_set(&clone->a_refcount, 1);
274 : : }
275 : 0 : return clone;
276 : : }
277 : :
278 : 0 : static int f2fs_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
279 : : {
280 : : struct posix_acl_entry *pa, *pe;
281 : : struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
282 : 0 : umode_t mode = *mode_p;
283 : : int not_equiv = 0;
284 : :
285 : : /* assert(atomic_read(acl->a_refcount) == 1); */
286 : :
287 [ # # ]: 0 : FOREACH_ACL_ENTRY(pa, acl, pe) {
288 [ # # # # : 0 : switch (pa->e_tag) {
# # ]
289 : : case ACL_USER_OBJ:
290 : 0 : pa->e_perm &= (mode >> 6) | ~S_IRWXO;
291 : 0 : mode &= (pa->e_perm << 6) | ~S_IRWXU;
292 : 0 : break;
293 : :
294 : : case ACL_USER:
295 : : case ACL_GROUP:
296 : : not_equiv = 1;
297 : : break;
298 : :
299 : : case ACL_GROUP_OBJ:
300 : : group_obj = pa;
301 : 0 : break;
302 : :
303 : : case ACL_OTHER:
304 : 0 : pa->e_perm &= mode | ~S_IRWXO;
305 : 0 : mode &= pa->e_perm | ~S_IRWXO;
306 : 0 : break;
307 : :
308 : : case ACL_MASK:
309 : : mask_obj = pa;
310 : : not_equiv = 1;
311 : 0 : break;
312 : :
313 : : default:
314 : : return -EIO;
315 : : }
316 : : }
317 : :
318 [ # # ]: 0 : if (mask_obj) {
319 : 0 : mask_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
320 : 0 : mode &= (mask_obj->e_perm << 3) | ~S_IRWXG;
321 : : } else {
322 [ # # ]: 0 : if (!group_obj)
323 : : return -EIO;
324 : 0 : group_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
325 : 0 : mode &= (group_obj->e_perm << 3) | ~S_IRWXG;
326 : : }
327 : :
328 : 0 : *mode_p = (*mode_p & ~S_IRWXUGO) | mode;
329 : 0 : return not_equiv;
330 : : }
331 : :
332 : 0 : static int f2fs_acl_create(struct inode *dir, umode_t *mode,
333 : : struct posix_acl **default_acl, struct posix_acl **acl,
334 : : struct page *dpage)
335 : : {
336 : : struct posix_acl *p;
337 : : struct posix_acl *clone;
338 : : int ret;
339 : :
340 : 0 : *acl = NULL;
341 : 0 : *default_acl = NULL;
342 : :
343 [ # # # # ]: 0 : if (S_ISLNK(*mode) || !IS_POSIXACL(dir))
344 : : return 0;
345 : :
346 : 0 : p = __f2fs_get_acl(dir, ACL_TYPE_DEFAULT, dpage);
347 [ # # # # ]: 0 : if (!p || p == ERR_PTR(-EOPNOTSUPP)) {
348 : 0 : *mode &= ~current_umask();
349 : 0 : return 0;
350 : : }
351 [ # # ]: 0 : if (IS_ERR(p))
352 : 0 : return PTR_ERR(p);
353 : :
354 : 0 : clone = f2fs_acl_clone(p, GFP_NOFS);
355 [ # # ]: 0 : if (!clone) {
356 : : ret = -ENOMEM;
357 : : goto release_acl;
358 : : }
359 : :
360 : 0 : ret = f2fs_acl_create_masq(clone, mode);
361 [ # # ]: 0 : if (ret < 0)
362 : : goto release_clone;
363 : :
364 [ # # ]: 0 : if (ret == 0)
365 : 0 : posix_acl_release(clone);
366 : : else
367 : 0 : *acl = clone;
368 : :
369 [ # # ]: 0 : if (!S_ISDIR(*mode))
370 : 0 : posix_acl_release(p);
371 : : else
372 : 0 : *default_acl = p;
373 : :
374 : : return 0;
375 : :
376 : : release_clone:
377 : 0 : posix_acl_release(clone);
378 : : release_acl:
379 : 0 : posix_acl_release(p);
380 : 0 : return ret;
381 : : }
382 : :
383 : 0 : int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage,
384 : : struct page *dpage)
385 : : {
386 : 0 : struct posix_acl *default_acl = NULL, *acl = NULL;
387 : : int error = 0;
388 : :
389 : 0 : error = f2fs_acl_create(dir, &inode->i_mode, &default_acl, &acl, dpage);
390 [ # # ]: 0 : if (error)
391 : : return error;
392 : :
393 : 0 : f2fs_mark_inode_dirty_sync(inode, true);
394 : :
395 [ # # ]: 0 : if (default_acl) {
396 : 0 : error = __f2fs_set_acl(inode, ACL_TYPE_DEFAULT, default_acl,
397 : : ipage);
398 : 0 : posix_acl_release(default_acl);
399 : : } else {
400 : 0 : inode->i_default_acl = NULL;
401 : : }
402 [ # # ]: 0 : if (acl) {
403 [ # # ]: 0 : if (!error)
404 : 0 : error = __f2fs_set_acl(inode, ACL_TYPE_ACCESS, acl,
405 : : ipage);
406 : 0 : posix_acl_release(acl);
407 : : } else {
408 : 0 : inode->i_acl = NULL;
409 : : }
410 : :
411 : 0 : return error;
412 : : }
|