1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2017-2018 HUAWEI, Inc. |
4 | * https://www.huawei.com/ |
5 | * Copyright (C) 2021-2022, Alibaba Cloud |
6 | */ |
7 | #include <linux/security.h> |
8 | #include <linux/xxhash.h> |
9 | #include "xattr.h" |
10 | |
11 | struct erofs_xattr_iter { |
12 | struct super_block *sb; |
13 | struct erofs_buf buf; |
14 | erofs_off_t pos; |
15 | void *kaddr; |
16 | |
17 | char *buffer; |
18 | int buffer_size, buffer_ofs; |
19 | |
20 | /* getxattr */ |
21 | int index, infix_len; |
22 | struct qstr name; |
23 | |
24 | /* listxattr */ |
25 | struct dentry *dentry; |
26 | }; |
27 | |
28 | static int erofs_init_inode_xattrs(struct inode *inode) |
29 | { |
30 | struct erofs_inode *const vi = EROFS_I(inode); |
31 | struct erofs_xattr_iter it; |
32 | unsigned int i; |
33 | struct erofs_xattr_ibody_header *ih; |
34 | struct super_block *sb = inode->i_sb; |
35 | int ret = 0; |
36 | |
37 | /* the most case is that xattrs of this inode are initialized. */ |
38 | if (test_bit(EROFS_I_EA_INITED_BIT, &vi->flags)) { |
39 | /* |
40 | * paired with smp_mb() at the end of the function to ensure |
41 | * fields will only be observed after the bit is set. |
42 | */ |
43 | smp_mb(); |
44 | return 0; |
45 | } |
46 | |
47 | if (wait_on_bit_lock(word: &vi->flags, EROFS_I_BL_XATTR_BIT, TASK_KILLABLE)) |
48 | return -ERESTARTSYS; |
49 | |
50 | /* someone has initialized xattrs for us? */ |
51 | if (test_bit(EROFS_I_EA_INITED_BIT, &vi->flags)) |
52 | goto out_unlock; |
53 | |
54 | /* |
55 | * bypass all xattr operations if ->xattr_isize is not greater than |
56 | * sizeof(struct erofs_xattr_ibody_header), in detail: |
57 | * 1) it is not enough to contain erofs_xattr_ibody_header then |
58 | * ->xattr_isize should be 0 (it means no xattr); |
59 | * 2) it is just to contain erofs_xattr_ibody_header, which is on-disk |
60 | * undefined right now (maybe use later with some new sb feature). |
61 | */ |
62 | if (vi->xattr_isize == sizeof(struct erofs_xattr_ibody_header)) { |
63 | erofs_err(sb, |
64 | "xattr_isize %d of nid %llu is not supported yet" , |
65 | vi->xattr_isize, vi->nid); |
66 | ret = -EOPNOTSUPP; |
67 | goto out_unlock; |
68 | } else if (vi->xattr_isize < sizeof(struct erofs_xattr_ibody_header)) { |
69 | if (vi->xattr_isize) { |
70 | erofs_err(sb, "bogus xattr ibody @ nid %llu" , vi->nid); |
71 | DBG_BUGON(1); |
72 | ret = -EFSCORRUPTED; |
73 | goto out_unlock; /* xattr ondisk layout error */ |
74 | } |
75 | ret = -ENOATTR; |
76 | goto out_unlock; |
77 | } |
78 | |
79 | it.buf = __EROFS_BUF_INITIALIZER; |
80 | erofs_init_metabuf(buf: &it.buf, sb); |
81 | it.pos = erofs_iloc(inode) + vi->inode_isize; |
82 | |
83 | /* read in shared xattr array (non-atomic, see kmalloc below) */ |
84 | it.kaddr = erofs_bread(buf: &it.buf, erofs_blknr(sb, it.pos), type: EROFS_KMAP); |
85 | if (IS_ERR(ptr: it.kaddr)) { |
86 | ret = PTR_ERR(ptr: it.kaddr); |
87 | goto out_unlock; |
88 | } |
89 | |
90 | ih = it.kaddr + erofs_blkoff(sb, it.pos); |
91 | vi->xattr_name_filter = le32_to_cpu(ih->h_name_filter); |
92 | vi->xattr_shared_count = ih->h_shared_count; |
93 | vi->xattr_shared_xattrs = kmalloc_array(n: vi->xattr_shared_count, |
94 | size: sizeof(uint), GFP_KERNEL); |
95 | if (!vi->xattr_shared_xattrs) { |
96 | erofs_put_metabuf(buf: &it.buf); |
97 | ret = -ENOMEM; |
98 | goto out_unlock; |
99 | } |
100 | |
101 | /* let's skip ibody header */ |
102 | it.pos += sizeof(struct erofs_xattr_ibody_header); |
103 | |
104 | for (i = 0; i < vi->xattr_shared_count; ++i) { |
105 | it.kaddr = erofs_bread(buf: &it.buf, erofs_blknr(sb, it.pos), |
106 | type: EROFS_KMAP); |
107 | if (IS_ERR(ptr: it.kaddr)) { |
108 | kfree(objp: vi->xattr_shared_xattrs); |
109 | vi->xattr_shared_xattrs = NULL; |
110 | ret = PTR_ERR(ptr: it.kaddr); |
111 | goto out_unlock; |
112 | } |
113 | vi->xattr_shared_xattrs[i] = le32_to_cpu(*(__le32 *) |
114 | (it.kaddr + erofs_blkoff(sb, it.pos))); |
115 | it.pos += sizeof(__le32); |
116 | } |
117 | erofs_put_metabuf(buf: &it.buf); |
118 | |
119 | /* paired with smp_mb() at the beginning of the function. */ |
120 | smp_mb(); |
121 | set_bit(EROFS_I_EA_INITED_BIT, addr: &vi->flags); |
122 | |
123 | out_unlock: |
124 | clear_and_wake_up_bit(EROFS_I_BL_XATTR_BIT, word: &vi->flags); |
125 | return ret; |
126 | } |
127 | |
128 | static bool erofs_xattr_user_list(struct dentry *dentry) |
129 | { |
130 | return test_opt(&EROFS_SB(dentry->d_sb)->opt, XATTR_USER); |
131 | } |
132 | |
133 | static bool erofs_xattr_trusted_list(struct dentry *dentry) |
134 | { |
135 | return capable(CAP_SYS_ADMIN); |
136 | } |
137 | |
138 | static int erofs_xattr_generic_get(const struct xattr_handler *handler, |
139 | struct dentry *unused, struct inode *inode, |
140 | const char *name, void *buffer, size_t size) |
141 | { |
142 | if (handler->flags == EROFS_XATTR_INDEX_USER && |
143 | !test_opt(&EROFS_I_SB(inode)->opt, XATTR_USER)) |
144 | return -EOPNOTSUPP; |
145 | |
146 | return erofs_getxattr(inode, handler->flags, name, buffer, size); |
147 | } |
148 | |
149 | const struct xattr_handler erofs_xattr_user_handler = { |
150 | .prefix = XATTR_USER_PREFIX, |
151 | .flags = EROFS_XATTR_INDEX_USER, |
152 | .list = erofs_xattr_user_list, |
153 | .get = erofs_xattr_generic_get, |
154 | }; |
155 | |
156 | const struct xattr_handler erofs_xattr_trusted_handler = { |
157 | .prefix = XATTR_TRUSTED_PREFIX, |
158 | .flags = EROFS_XATTR_INDEX_TRUSTED, |
159 | .list = erofs_xattr_trusted_list, |
160 | .get = erofs_xattr_generic_get, |
161 | }; |
162 | |
163 | #ifdef CONFIG_EROFS_FS_SECURITY |
164 | const struct xattr_handler __maybe_unused erofs_xattr_security_handler = { |
165 | .prefix = XATTR_SECURITY_PREFIX, |
166 | .flags = EROFS_XATTR_INDEX_SECURITY, |
167 | .get = erofs_xattr_generic_get, |
168 | }; |
169 | #endif |
170 | |
171 | const struct xattr_handler * const erofs_xattr_handlers[] = { |
172 | &erofs_xattr_user_handler, |
173 | &erofs_xattr_trusted_handler, |
174 | #ifdef CONFIG_EROFS_FS_SECURITY |
175 | &erofs_xattr_security_handler, |
176 | #endif |
177 | NULL, |
178 | }; |
179 | |
180 | static int erofs_xattr_copy_to_buffer(struct erofs_xattr_iter *it, |
181 | unsigned int len) |
182 | { |
183 | unsigned int slice, processed; |
184 | struct super_block *sb = it->sb; |
185 | void *src; |
186 | |
187 | for (processed = 0; processed < len; processed += slice) { |
188 | it->kaddr = erofs_bread(buf: &it->buf, erofs_blknr(sb, it->pos), |
189 | type: EROFS_KMAP); |
190 | if (IS_ERR(ptr: it->kaddr)) |
191 | return PTR_ERR(ptr: it->kaddr); |
192 | |
193 | src = it->kaddr + erofs_blkoff(sb, it->pos); |
194 | slice = min_t(unsigned int, sb->s_blocksize - |
195 | erofs_blkoff(sb, it->pos), len - processed); |
196 | memcpy(it->buffer + it->buffer_ofs, src, slice); |
197 | it->buffer_ofs += slice; |
198 | it->pos += slice; |
199 | } |
200 | return 0; |
201 | } |
202 | |
203 | static int erofs_listxattr_foreach(struct erofs_xattr_iter *it) |
204 | { |
205 | struct erofs_xattr_entry entry; |
206 | unsigned int base_index, name_total, prefix_len, infix_len = 0; |
207 | const char *prefix, *infix = NULL; |
208 | int err; |
209 | |
210 | /* 1. handle xattr entry */ |
211 | entry = *(struct erofs_xattr_entry *) |
212 | (it->kaddr + erofs_blkoff(it->sb, it->pos)); |
213 | it->pos += sizeof(struct erofs_xattr_entry); |
214 | |
215 | base_index = entry.e_name_index; |
216 | if (entry.e_name_index & EROFS_XATTR_LONG_PREFIX) { |
217 | struct erofs_sb_info *sbi = EROFS_SB(it->sb); |
218 | struct erofs_xattr_prefix_item *pf = sbi->xattr_prefixes + |
219 | (entry.e_name_index & EROFS_XATTR_LONG_PREFIX_MASK); |
220 | |
221 | if (pf >= sbi->xattr_prefixes + sbi->xattr_prefix_count) |
222 | return 0; |
223 | infix = pf->prefix->infix; |
224 | infix_len = pf->infix_len; |
225 | base_index = pf->prefix->base_index; |
226 | } |
227 | |
228 | prefix = erofs_xattr_prefix(idx: base_index, dentry: it->dentry); |
229 | if (!prefix) |
230 | return 0; |
231 | prefix_len = strlen(prefix); |
232 | name_total = prefix_len + infix_len + entry.e_name_len + 1; |
233 | |
234 | if (!it->buffer) { |
235 | it->buffer_ofs += name_total; |
236 | return 0; |
237 | } |
238 | |
239 | if (it->buffer_ofs + name_total > it->buffer_size) |
240 | return -ERANGE; |
241 | |
242 | memcpy(it->buffer + it->buffer_ofs, prefix, prefix_len); |
243 | memcpy(it->buffer + it->buffer_ofs + prefix_len, infix, infix_len); |
244 | it->buffer_ofs += prefix_len + infix_len; |
245 | |
246 | /* 2. handle xattr name */ |
247 | err = erofs_xattr_copy_to_buffer(it, len: entry.e_name_len); |
248 | if (err) |
249 | return err; |
250 | |
251 | it->buffer[it->buffer_ofs++] = '\0'; |
252 | return 0; |
253 | } |
254 | |
255 | static int erofs_getxattr_foreach(struct erofs_xattr_iter *it) |
256 | { |
257 | struct super_block *sb = it->sb; |
258 | struct erofs_xattr_entry entry; |
259 | unsigned int slice, processed, value_sz; |
260 | |
261 | /* 1. handle xattr entry */ |
262 | entry = *(struct erofs_xattr_entry *) |
263 | (it->kaddr + erofs_blkoff(sb, it->pos)); |
264 | it->pos += sizeof(struct erofs_xattr_entry); |
265 | value_sz = le16_to_cpu(entry.e_value_size); |
266 | |
267 | /* should also match the infix for long name prefixes */ |
268 | if (entry.e_name_index & EROFS_XATTR_LONG_PREFIX) { |
269 | struct erofs_sb_info *sbi = EROFS_SB(sb); |
270 | struct erofs_xattr_prefix_item *pf = sbi->xattr_prefixes + |
271 | (entry.e_name_index & EROFS_XATTR_LONG_PREFIX_MASK); |
272 | |
273 | if (pf >= sbi->xattr_prefixes + sbi->xattr_prefix_count) |
274 | return -ENOATTR; |
275 | |
276 | if (it->index != pf->prefix->base_index || |
277 | it->name.len != entry.e_name_len + pf->infix_len) |
278 | return -ENOATTR; |
279 | |
280 | if (memcmp(p: it->name.name, q: pf->prefix->infix, size: pf->infix_len)) |
281 | return -ENOATTR; |
282 | |
283 | it->infix_len = pf->infix_len; |
284 | } else { |
285 | if (it->index != entry.e_name_index || |
286 | it->name.len != entry.e_name_len) |
287 | return -ENOATTR; |
288 | |
289 | it->infix_len = 0; |
290 | } |
291 | |
292 | /* 2. handle xattr name */ |
293 | for (processed = 0; processed < entry.e_name_len; processed += slice) { |
294 | it->kaddr = erofs_bread(buf: &it->buf, erofs_blknr(sb, it->pos), |
295 | type: EROFS_KMAP); |
296 | if (IS_ERR(ptr: it->kaddr)) |
297 | return PTR_ERR(ptr: it->kaddr); |
298 | |
299 | slice = min_t(unsigned int, |
300 | sb->s_blocksize - erofs_blkoff(sb, it->pos), |
301 | entry.e_name_len - processed); |
302 | if (memcmp(p: it->name.name + it->infix_len + processed, |
303 | q: it->kaddr + erofs_blkoff(sb, it->pos), size: slice)) |
304 | return -ENOATTR; |
305 | it->pos += slice; |
306 | } |
307 | |
308 | /* 3. handle xattr value */ |
309 | if (!it->buffer) { |
310 | it->buffer_ofs = value_sz; |
311 | return 0; |
312 | } |
313 | |
314 | if (it->buffer_size < value_sz) |
315 | return -ERANGE; |
316 | |
317 | return erofs_xattr_copy_to_buffer(it, len: value_sz); |
318 | } |
319 | |
320 | static int erofs_xattr_iter_inline(struct erofs_xattr_iter *it, |
321 | struct inode *inode, bool getxattr) |
322 | { |
323 | struct erofs_inode *const vi = EROFS_I(inode); |
324 | unsigned int , remaining, entry_sz; |
325 | erofs_off_t next_pos; |
326 | int ret; |
327 | |
328 | xattr_header_sz = sizeof(struct erofs_xattr_ibody_header) + |
329 | sizeof(u32) * vi->xattr_shared_count; |
330 | if (xattr_header_sz >= vi->xattr_isize) { |
331 | DBG_BUGON(xattr_header_sz > vi->xattr_isize); |
332 | return -ENOATTR; |
333 | } |
334 | |
335 | remaining = vi->xattr_isize - xattr_header_sz; |
336 | it->pos = erofs_iloc(inode) + vi->inode_isize + xattr_header_sz; |
337 | |
338 | while (remaining) { |
339 | it->kaddr = erofs_bread(buf: &it->buf, erofs_blknr(it->sb, it->pos), |
340 | type: EROFS_KMAP); |
341 | if (IS_ERR(ptr: it->kaddr)) |
342 | return PTR_ERR(ptr: it->kaddr); |
343 | |
344 | entry_sz = erofs_xattr_entry_size(e: it->kaddr + |
345 | erofs_blkoff(it->sb, it->pos)); |
346 | /* xattr on-disk corruption: xattr entry beyond xattr_isize */ |
347 | if (remaining < entry_sz) { |
348 | DBG_BUGON(1); |
349 | return -EFSCORRUPTED; |
350 | } |
351 | remaining -= entry_sz; |
352 | next_pos = it->pos + entry_sz; |
353 | |
354 | if (getxattr) |
355 | ret = erofs_getxattr_foreach(it); |
356 | else |
357 | ret = erofs_listxattr_foreach(it); |
358 | if ((getxattr && ret != -ENOATTR) || (!getxattr && ret)) |
359 | break; |
360 | |
361 | it->pos = next_pos; |
362 | } |
363 | return ret; |
364 | } |
365 | |
366 | static int erofs_xattr_iter_shared(struct erofs_xattr_iter *it, |
367 | struct inode *inode, bool getxattr) |
368 | { |
369 | struct erofs_inode *const vi = EROFS_I(inode); |
370 | struct super_block *const sb = it->sb; |
371 | struct erofs_sb_info *sbi = EROFS_SB(sb); |
372 | unsigned int i; |
373 | int ret = -ENOATTR; |
374 | |
375 | for (i = 0; i < vi->xattr_shared_count; ++i) { |
376 | it->pos = erofs_pos(sb, sbi->xattr_blkaddr) + |
377 | vi->xattr_shared_xattrs[i] * sizeof(__le32); |
378 | it->kaddr = erofs_bread(buf: &it->buf, erofs_blknr(sb, it->pos), |
379 | type: EROFS_KMAP); |
380 | if (IS_ERR(ptr: it->kaddr)) |
381 | return PTR_ERR(ptr: it->kaddr); |
382 | |
383 | if (getxattr) |
384 | ret = erofs_getxattr_foreach(it); |
385 | else |
386 | ret = erofs_listxattr_foreach(it); |
387 | if ((getxattr && ret != -ENOATTR) || (!getxattr && ret)) |
388 | break; |
389 | } |
390 | return ret; |
391 | } |
392 | |
393 | int erofs_getxattr(struct inode *inode, int index, const char *name, |
394 | void *buffer, size_t buffer_size) |
395 | { |
396 | int ret; |
397 | unsigned int hashbit; |
398 | struct erofs_xattr_iter it; |
399 | struct erofs_inode *vi = EROFS_I(inode); |
400 | struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb); |
401 | |
402 | if (!name) |
403 | return -EINVAL; |
404 | |
405 | ret = erofs_init_inode_xattrs(inode); |
406 | if (ret) |
407 | return ret; |
408 | |
409 | /* reserved flag is non-zero if there's any change of on-disk format */ |
410 | if (erofs_sb_has_xattr_filter(sbi) && !sbi->xattr_filter_reserved) { |
411 | hashbit = xxh32(input: name, strlen(name), |
412 | EROFS_XATTR_FILTER_SEED + index); |
413 | hashbit &= EROFS_XATTR_FILTER_BITS - 1; |
414 | if (vi->xattr_name_filter & (1U << hashbit)) |
415 | return -ENOATTR; |
416 | } |
417 | |
418 | it.index = index; |
419 | it.name = (struct qstr)QSTR_INIT(name, strlen(name)); |
420 | if (it.name.len > EROFS_NAME_LEN) |
421 | return -ERANGE; |
422 | |
423 | it.sb = inode->i_sb; |
424 | it.buf = __EROFS_BUF_INITIALIZER; |
425 | erofs_init_metabuf(buf: &it.buf, sb: it.sb); |
426 | it.buffer = buffer; |
427 | it.buffer_size = buffer_size; |
428 | it.buffer_ofs = 0; |
429 | |
430 | ret = erofs_xattr_iter_inline(it: &it, inode, getxattr: true); |
431 | if (ret == -ENOATTR) |
432 | ret = erofs_xattr_iter_shared(it: &it, inode, getxattr: true); |
433 | erofs_put_metabuf(buf: &it.buf); |
434 | return ret ? ret : it.buffer_ofs; |
435 | } |
436 | |
437 | ssize_t erofs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) |
438 | { |
439 | int ret; |
440 | struct erofs_xattr_iter it; |
441 | struct inode *inode = d_inode(dentry); |
442 | |
443 | ret = erofs_init_inode_xattrs(inode); |
444 | if (ret == -ENOATTR) |
445 | return 0; |
446 | if (ret) |
447 | return ret; |
448 | |
449 | it.sb = dentry->d_sb; |
450 | it.buf = __EROFS_BUF_INITIALIZER; |
451 | erofs_init_metabuf(buf: &it.buf, sb: it.sb); |
452 | it.dentry = dentry; |
453 | it.buffer = buffer; |
454 | it.buffer_size = buffer_size; |
455 | it.buffer_ofs = 0; |
456 | |
457 | ret = erofs_xattr_iter_inline(it: &it, inode, getxattr: false); |
458 | if (!ret || ret == -ENOATTR) |
459 | ret = erofs_xattr_iter_shared(it: &it, inode, getxattr: false); |
460 | if (ret == -ENOATTR) |
461 | ret = 0; |
462 | erofs_put_metabuf(buf: &it.buf); |
463 | return ret ? ret : it.buffer_ofs; |
464 | } |
465 | |
466 | void erofs_xattr_prefixes_cleanup(struct super_block *sb) |
467 | { |
468 | struct erofs_sb_info *sbi = EROFS_SB(sb); |
469 | int i; |
470 | |
471 | if (sbi->xattr_prefixes) { |
472 | for (i = 0; i < sbi->xattr_prefix_count; i++) |
473 | kfree(objp: sbi->xattr_prefixes[i].prefix); |
474 | kfree(objp: sbi->xattr_prefixes); |
475 | sbi->xattr_prefixes = NULL; |
476 | } |
477 | } |
478 | |
479 | int erofs_xattr_prefixes_init(struct super_block *sb) |
480 | { |
481 | struct erofs_sb_info *sbi = EROFS_SB(sb); |
482 | struct erofs_buf buf = __EROFS_BUF_INITIALIZER; |
483 | erofs_off_t pos = (erofs_off_t)sbi->xattr_prefix_start << 2; |
484 | struct erofs_xattr_prefix_item *pfs; |
485 | int ret = 0, i, len; |
486 | |
487 | if (!sbi->xattr_prefix_count) |
488 | return 0; |
489 | |
490 | pfs = kzalloc(size: sbi->xattr_prefix_count * sizeof(*pfs), GFP_KERNEL); |
491 | if (!pfs) |
492 | return -ENOMEM; |
493 | |
494 | if (sbi->packed_inode) |
495 | buf.inode = sbi->packed_inode; |
496 | else |
497 | erofs_init_metabuf(buf: &buf, sb); |
498 | |
499 | for (i = 0; i < sbi->xattr_prefix_count; i++) { |
500 | void *ptr = erofs_read_metadata(sb, buf: &buf, offset: &pos, lengthp: &len); |
501 | |
502 | if (IS_ERR(ptr)) { |
503 | ret = PTR_ERR(ptr); |
504 | break; |
505 | } else if (len < sizeof(*pfs->prefix) || |
506 | len > EROFS_NAME_LEN + sizeof(*pfs->prefix)) { |
507 | kfree(objp: ptr); |
508 | ret = -EFSCORRUPTED; |
509 | break; |
510 | } |
511 | pfs[i].prefix = ptr; |
512 | pfs[i].infix_len = len - sizeof(struct erofs_xattr_long_prefix); |
513 | } |
514 | |
515 | erofs_put_metabuf(buf: &buf); |
516 | sbi->xattr_prefixes = pfs; |
517 | if (ret) |
518 | erofs_xattr_prefixes_cleanup(sb); |
519 | return ret; |
520 | } |
521 | |
522 | #ifdef CONFIG_EROFS_FS_POSIX_ACL |
523 | struct posix_acl *erofs_get_acl(struct inode *inode, int type, bool rcu) |
524 | { |
525 | struct posix_acl *acl; |
526 | int prefix, rc; |
527 | char *value = NULL; |
528 | |
529 | if (rcu) |
530 | return ERR_PTR(error: -ECHILD); |
531 | |
532 | switch (type) { |
533 | case ACL_TYPE_ACCESS: |
534 | prefix = EROFS_XATTR_INDEX_POSIX_ACL_ACCESS; |
535 | break; |
536 | case ACL_TYPE_DEFAULT: |
537 | prefix = EROFS_XATTR_INDEX_POSIX_ACL_DEFAULT; |
538 | break; |
539 | default: |
540 | return ERR_PTR(error: -EINVAL); |
541 | } |
542 | |
543 | rc = erofs_getxattr(inode, index: prefix, name: "" , NULL, buffer_size: 0); |
544 | if (rc > 0) { |
545 | value = kmalloc(size: rc, GFP_KERNEL); |
546 | if (!value) |
547 | return ERR_PTR(error: -ENOMEM); |
548 | rc = erofs_getxattr(inode, index: prefix, name: "" , buffer: value, buffer_size: rc); |
549 | } |
550 | |
551 | if (rc == -ENOATTR) |
552 | acl = NULL; |
553 | else if (rc < 0) |
554 | acl = ERR_PTR(error: rc); |
555 | else |
556 | acl = posix_acl_from_xattr(user_ns: &init_user_ns, value, size: rc); |
557 | kfree(objp: value); |
558 | return acl; |
559 | } |
560 | #endif |
561 | |