mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:24:47 +01:00
Similar to commit 91ef18b567 ("ext4: mark inodes without acls in
__ext4_iget()"), the ACL state won't be read when the file owner
performs a lookup, and the RCU fast path for lookups won't work
because the ACL state remains unknown.
If there are no extended attributes, or if the xattr filter
indicates that no ACL xattr is present, call cache_no_acl() directly.
Reviewed-by: Hongbo Li <lihongbo22@huawei.com>
Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
644 lines
17 KiB
C
644 lines
17 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (C) 2017-2018 HUAWEI, Inc.
|
|
* https://www.huawei.com/
|
|
* Copyright (C) 2021-2022, Alibaba Cloud
|
|
*/
|
|
#include <linux/security.h>
|
|
#include <linux/xxhash.h>
|
|
#include "xattr.h"
|
|
|
|
struct erofs_xattr_iter {
|
|
struct super_block *sb;
|
|
struct erofs_buf buf;
|
|
erofs_off_t pos;
|
|
void *kaddr;
|
|
|
|
char *buffer;
|
|
int buffer_size, buffer_ofs;
|
|
|
|
/* getxattr */
|
|
int index, infix_len;
|
|
struct qstr name;
|
|
|
|
/* listxattr */
|
|
struct dentry *dentry;
|
|
};
|
|
|
|
static const char *erofs_xattr_prefix(unsigned int idx, struct dentry *dentry);
|
|
|
|
static int erofs_init_inode_xattrs(struct inode *inode)
|
|
{
|
|
struct erofs_buf buf = __EROFS_BUF_INITIALIZER;
|
|
struct erofs_inode *vi = EROFS_I(inode);
|
|
struct super_block *sb = inode->i_sb;
|
|
const struct erofs_xattr_ibody_header *ih;
|
|
__le32 *xattr_id;
|
|
erofs_off_t pos;
|
|
unsigned int i;
|
|
int ret = 0;
|
|
|
|
if (!vi->xattr_isize)
|
|
return -ENODATA;
|
|
|
|
/* the most case is that xattrs of this inode are initialized. */
|
|
if (test_bit(EROFS_I_EA_INITED_BIT, &vi->flags)) {
|
|
/*
|
|
* paired with smp_mb() at the end of the function to ensure
|
|
* fields will only be observed after the bit is set.
|
|
*/
|
|
smp_mb();
|
|
return 0;
|
|
}
|
|
if (wait_on_bit_lock(&vi->flags, EROFS_I_BL_XATTR_BIT, TASK_KILLABLE))
|
|
return -ERESTARTSYS;
|
|
|
|
/* someone has initialized xattrs for us? */
|
|
if (test_bit(EROFS_I_EA_INITED_BIT, &vi->flags))
|
|
goto out_unlock;
|
|
|
|
/*
|
|
* bypass all xattr operations if ->xattr_isize is not greater than
|
|
* sizeof(struct erofs_xattr_ibody_header), in detail:
|
|
* 1) it is not enough to contain erofs_xattr_ibody_header then
|
|
* ->xattr_isize should be 0 (it means no xattr);
|
|
* 2) it is just to contain erofs_xattr_ibody_header, which is on-disk
|
|
* undefined right now (maybe use later with some new sb feature).
|
|
*/
|
|
if (vi->xattr_isize == sizeof(struct erofs_xattr_ibody_header)) {
|
|
erofs_err(sb, "xattr_isize %d of nid %llu is not supported yet",
|
|
vi->xattr_isize, vi->nid);
|
|
ret = -EOPNOTSUPP;
|
|
goto out_unlock;
|
|
} else if (vi->xattr_isize < sizeof(struct erofs_xattr_ibody_header)) {
|
|
erofs_err(sb, "bogus xattr ibody @ nid %llu", vi->nid);
|
|
DBG_BUGON(1);
|
|
ret = -EFSCORRUPTED;
|
|
goto out_unlock;
|
|
}
|
|
|
|
pos = erofs_iloc(inode) + vi->inode_isize;
|
|
ih = erofs_read_metabuf(&buf, sb, pos, erofs_inode_in_metabox(inode));
|
|
if (IS_ERR(ih)) {
|
|
ret = PTR_ERR(ih);
|
|
goto out_unlock;
|
|
}
|
|
vi->xattr_name_filter = le32_to_cpu(ih->h_name_filter);
|
|
vi->xattr_shared_count = ih->h_shared_count;
|
|
vi->xattr_shared_xattrs = kmalloc_array(vi->xattr_shared_count,
|
|
sizeof(uint), GFP_KERNEL);
|
|
if (!vi->xattr_shared_xattrs) {
|
|
erofs_put_metabuf(&buf);
|
|
ret = -ENOMEM;
|
|
goto out_unlock;
|
|
}
|
|
|
|
/* skip the ibody header and read the shared xattr array */
|
|
pos += sizeof(struct erofs_xattr_ibody_header);
|
|
for (i = 0; i < vi->xattr_shared_count; ++i) {
|
|
xattr_id = erofs_bread(&buf, pos + i * sizeof(__le32), true);
|
|
if (IS_ERR(xattr_id)) {
|
|
kfree(vi->xattr_shared_xattrs);
|
|
vi->xattr_shared_xattrs = NULL;
|
|
ret = PTR_ERR(xattr_id);
|
|
goto out_unlock;
|
|
}
|
|
vi->xattr_shared_xattrs[i] = le32_to_cpu(*xattr_id);
|
|
}
|
|
erofs_put_metabuf(&buf);
|
|
|
|
/* paired with smp_mb() at the beginning of the function. */
|
|
smp_mb();
|
|
set_bit(EROFS_I_EA_INITED_BIT, &vi->flags);
|
|
out_unlock:
|
|
clear_and_wake_up_bit(EROFS_I_BL_XATTR_BIT, &vi->flags);
|
|
return ret;
|
|
}
|
|
|
|
static int erofs_xattr_copy_to_buffer(struct erofs_xattr_iter *it,
|
|
unsigned int len)
|
|
{
|
|
unsigned int slice, processed;
|
|
struct super_block *sb = it->sb;
|
|
|
|
for (processed = 0; processed < len; processed += slice) {
|
|
it->kaddr = erofs_bread(&it->buf, it->pos, true);
|
|
if (IS_ERR(it->kaddr))
|
|
return PTR_ERR(it->kaddr);
|
|
|
|
slice = min_t(unsigned int, sb->s_blocksize -
|
|
erofs_blkoff(sb, it->pos), len - processed);
|
|
memcpy(it->buffer + it->buffer_ofs, it->kaddr, slice);
|
|
it->buffer_ofs += slice;
|
|
it->pos += slice;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int erofs_listxattr_foreach(struct erofs_xattr_iter *it)
|
|
{
|
|
struct erofs_xattr_entry entry;
|
|
unsigned int base_index, name_total, prefix_len, infix_len = 0;
|
|
const char *prefix, *infix = NULL;
|
|
int err;
|
|
|
|
/* 1. handle xattr entry */
|
|
entry = *(struct erofs_xattr_entry *)it->kaddr;
|
|
it->pos += sizeof(struct erofs_xattr_entry);
|
|
|
|
base_index = entry.e_name_index;
|
|
if (entry.e_name_index & EROFS_XATTR_LONG_PREFIX) {
|
|
struct erofs_sb_info *sbi = EROFS_SB(it->sb);
|
|
struct erofs_xattr_prefix_item *pf = sbi->xattr_prefixes +
|
|
(entry.e_name_index & EROFS_XATTR_LONG_PREFIX_MASK);
|
|
|
|
if (pf >= sbi->xattr_prefixes + sbi->xattr_prefix_count)
|
|
return 0;
|
|
infix = pf->prefix->infix;
|
|
infix_len = pf->infix_len;
|
|
base_index = pf->prefix->base_index;
|
|
}
|
|
|
|
prefix = erofs_xattr_prefix(base_index, it->dentry);
|
|
if (!prefix)
|
|
return 0;
|
|
prefix_len = strlen(prefix);
|
|
name_total = prefix_len + infix_len + entry.e_name_len + 1;
|
|
|
|
if (!it->buffer) {
|
|
it->buffer_ofs += name_total;
|
|
return 0;
|
|
}
|
|
|
|
if (it->buffer_ofs + name_total > it->buffer_size)
|
|
return -ERANGE;
|
|
|
|
memcpy(it->buffer + it->buffer_ofs, prefix, prefix_len);
|
|
memcpy(it->buffer + it->buffer_ofs + prefix_len, infix, infix_len);
|
|
it->buffer_ofs += prefix_len + infix_len;
|
|
|
|
/* 2. handle xattr name */
|
|
err = erofs_xattr_copy_to_buffer(it, entry.e_name_len);
|
|
if (err)
|
|
return err;
|
|
|
|
it->buffer[it->buffer_ofs++] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
static int erofs_getxattr_foreach(struct erofs_xattr_iter *it)
|
|
{
|
|
struct super_block *sb = it->sb;
|
|
struct erofs_xattr_entry entry;
|
|
unsigned int slice, processed, value_sz;
|
|
|
|
/* 1. handle xattr entry */
|
|
entry = *(struct erofs_xattr_entry *)it->kaddr;
|
|
it->pos += sizeof(struct erofs_xattr_entry);
|
|
value_sz = le16_to_cpu(entry.e_value_size);
|
|
|
|
/* should also match the infix for long name prefixes */
|
|
if (entry.e_name_index & EROFS_XATTR_LONG_PREFIX) {
|
|
struct erofs_sb_info *sbi = EROFS_SB(sb);
|
|
struct erofs_xattr_prefix_item *pf = sbi->xattr_prefixes +
|
|
(entry.e_name_index & EROFS_XATTR_LONG_PREFIX_MASK);
|
|
|
|
if (pf >= sbi->xattr_prefixes + sbi->xattr_prefix_count)
|
|
return -ENODATA;
|
|
|
|
if (it->index != pf->prefix->base_index ||
|
|
it->name.len != entry.e_name_len + pf->infix_len)
|
|
return -ENODATA;
|
|
|
|
if (memcmp(it->name.name, pf->prefix->infix, pf->infix_len))
|
|
return -ENODATA;
|
|
|
|
it->infix_len = pf->infix_len;
|
|
} else {
|
|
if (it->index != entry.e_name_index ||
|
|
it->name.len != entry.e_name_len)
|
|
return -ENODATA;
|
|
|
|
it->infix_len = 0;
|
|
}
|
|
|
|
/* 2. handle xattr name */
|
|
for (processed = 0; processed < entry.e_name_len; processed += slice) {
|
|
it->kaddr = erofs_bread(&it->buf, it->pos, true);
|
|
if (IS_ERR(it->kaddr))
|
|
return PTR_ERR(it->kaddr);
|
|
|
|
slice = min_t(unsigned int,
|
|
sb->s_blocksize - erofs_blkoff(sb, it->pos),
|
|
entry.e_name_len - processed);
|
|
if (memcmp(it->name.name + it->infix_len + processed,
|
|
it->kaddr, slice))
|
|
return -ENODATA;
|
|
it->pos += slice;
|
|
}
|
|
|
|
/* 3. handle xattr value */
|
|
if (!it->buffer) {
|
|
it->buffer_ofs = value_sz;
|
|
return 0;
|
|
}
|
|
|
|
if (it->buffer_size < value_sz)
|
|
return -ERANGE;
|
|
|
|
return erofs_xattr_copy_to_buffer(it, value_sz);
|
|
}
|
|
|
|
static int erofs_xattr_iter_inline(struct erofs_xattr_iter *it,
|
|
struct inode *inode, bool getxattr)
|
|
{
|
|
struct erofs_inode *const vi = EROFS_I(inode);
|
|
unsigned int xattr_header_sz, remaining, entry_sz;
|
|
erofs_off_t next_pos;
|
|
int ret;
|
|
|
|
xattr_header_sz = sizeof(struct erofs_xattr_ibody_header) +
|
|
sizeof(u32) * vi->xattr_shared_count;
|
|
if (xattr_header_sz >= vi->xattr_isize) {
|
|
DBG_BUGON(xattr_header_sz > vi->xattr_isize);
|
|
return -ENODATA;
|
|
}
|
|
|
|
ret = erofs_init_metabuf(&it->buf, it->sb, erofs_inode_in_metabox(inode));
|
|
if (ret)
|
|
return ret;
|
|
remaining = vi->xattr_isize - xattr_header_sz;
|
|
it->pos = erofs_iloc(inode) + vi->inode_isize + xattr_header_sz;
|
|
|
|
while (remaining) {
|
|
it->kaddr = erofs_bread(&it->buf, it->pos, true);
|
|
if (IS_ERR(it->kaddr))
|
|
return PTR_ERR(it->kaddr);
|
|
|
|
entry_sz = erofs_xattr_entry_size(it->kaddr);
|
|
/* xattr on-disk corruption: xattr entry beyond xattr_isize */
|
|
if (remaining < entry_sz) {
|
|
DBG_BUGON(1);
|
|
return -EFSCORRUPTED;
|
|
}
|
|
remaining -= entry_sz;
|
|
next_pos = it->pos + entry_sz;
|
|
|
|
if (getxattr)
|
|
ret = erofs_getxattr_foreach(it);
|
|
else
|
|
ret = erofs_listxattr_foreach(it);
|
|
if ((getxattr && ret != -ENODATA) || (!getxattr && ret))
|
|
break;
|
|
|
|
it->pos = next_pos;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int erofs_xattr_iter_shared(struct erofs_xattr_iter *it,
|
|
struct inode *inode, bool getxattr)
|
|
{
|
|
struct erofs_inode *const vi = EROFS_I(inode);
|
|
struct super_block *const sb = it->sb;
|
|
struct erofs_sb_info *sbi = EROFS_SB(sb);
|
|
unsigned int i = 0;
|
|
int ret;
|
|
|
|
ret = erofs_init_metabuf(&it->buf, sb,
|
|
erofs_sb_has_shared_ea_in_metabox(sbi));
|
|
if (ret)
|
|
return ret;
|
|
|
|
while (i < vi->xattr_shared_count) {
|
|
it->pos = erofs_pos(sb, sbi->xattr_blkaddr) +
|
|
vi->xattr_shared_xattrs[i++] * sizeof(__le32);
|
|
it->kaddr = erofs_bread(&it->buf, it->pos, true);
|
|
if (IS_ERR(it->kaddr))
|
|
return PTR_ERR(it->kaddr);
|
|
|
|
if (getxattr)
|
|
ret = erofs_getxattr_foreach(it);
|
|
else
|
|
ret = erofs_listxattr_foreach(it);
|
|
if ((getxattr && ret != -ENODATA) || (!getxattr && ret))
|
|
break;
|
|
}
|
|
return i ? ret : -ENODATA;
|
|
}
|
|
|
|
static int erofs_getxattr(struct inode *inode, int index, const char *name,
|
|
void *buffer, size_t buffer_size)
|
|
{
|
|
int ret;
|
|
unsigned int hashbit;
|
|
struct erofs_xattr_iter it;
|
|
struct erofs_inode *vi = EROFS_I(inode);
|
|
struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb);
|
|
|
|
if (!name)
|
|
return -EINVAL;
|
|
|
|
ret = erofs_init_inode_xattrs(inode);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* reserved flag is non-zero if there's any change of on-disk format */
|
|
if (erofs_sb_has_xattr_filter(sbi) && !sbi->xattr_filter_reserved) {
|
|
hashbit = xxh32(name, strlen(name),
|
|
EROFS_XATTR_FILTER_SEED + index);
|
|
hashbit &= EROFS_XATTR_FILTER_BITS - 1;
|
|
if (vi->xattr_name_filter & (1U << hashbit))
|
|
return -ENODATA;
|
|
}
|
|
|
|
it.index = index;
|
|
it.name = QSTR(name);
|
|
if (it.name.len > EROFS_NAME_LEN)
|
|
return -ERANGE;
|
|
|
|
it.sb = inode->i_sb;
|
|
it.buf = __EROFS_BUF_INITIALIZER;
|
|
it.buffer = buffer;
|
|
it.buffer_size = buffer_size;
|
|
it.buffer_ofs = 0;
|
|
|
|
ret = erofs_xattr_iter_inline(&it, inode, true);
|
|
if (ret == -ENODATA)
|
|
ret = erofs_xattr_iter_shared(&it, inode, true);
|
|
erofs_put_metabuf(&it.buf);
|
|
return ret ? ret : it.buffer_ofs;
|
|
}
|
|
|
|
ssize_t erofs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
|
|
{
|
|
int ret;
|
|
struct erofs_xattr_iter it;
|
|
struct inode *inode = d_inode(dentry);
|
|
|
|
ret = erofs_init_inode_xattrs(inode);
|
|
if (ret == -ENODATA)
|
|
return 0;
|
|
if (ret)
|
|
return ret;
|
|
|
|
it.sb = dentry->d_sb;
|
|
it.buf = __EROFS_BUF_INITIALIZER;
|
|
it.dentry = dentry;
|
|
it.buffer = buffer;
|
|
it.buffer_size = buffer_size;
|
|
it.buffer_ofs = 0;
|
|
|
|
ret = erofs_xattr_iter_inline(&it, inode, false);
|
|
if (!ret || ret == -ENODATA)
|
|
ret = erofs_xattr_iter_shared(&it, inode, false);
|
|
if (ret == -ENODATA)
|
|
ret = 0;
|
|
erofs_put_metabuf(&it.buf);
|
|
return ret ? ret : it.buffer_ofs;
|
|
}
|
|
|
|
static bool erofs_xattr_user_list(struct dentry *dentry)
|
|
{
|
|
return test_opt(&EROFS_SB(dentry->d_sb)->opt, XATTR_USER);
|
|
}
|
|
|
|
static bool erofs_xattr_trusted_list(struct dentry *dentry)
|
|
{
|
|
return capable(CAP_SYS_ADMIN);
|
|
}
|
|
|
|
static int erofs_xattr_generic_get(const struct xattr_handler *handler,
|
|
struct dentry *unused, struct inode *inode,
|
|
const char *name, void *buffer, size_t size)
|
|
{
|
|
if (handler->flags == EROFS_XATTR_INDEX_USER &&
|
|
!test_opt(&EROFS_I_SB(inode)->opt, XATTR_USER))
|
|
return -EOPNOTSUPP;
|
|
|
|
return erofs_getxattr(inode, handler->flags, name, buffer, size);
|
|
}
|
|
|
|
static const struct xattr_handler erofs_xattr_user_handler = {
|
|
.prefix = XATTR_USER_PREFIX,
|
|
.flags = EROFS_XATTR_INDEX_USER,
|
|
.list = erofs_xattr_user_list,
|
|
.get = erofs_xattr_generic_get,
|
|
};
|
|
|
|
static const struct xattr_handler erofs_xattr_trusted_handler = {
|
|
.prefix = XATTR_TRUSTED_PREFIX,
|
|
.flags = EROFS_XATTR_INDEX_TRUSTED,
|
|
.list = erofs_xattr_trusted_list,
|
|
.get = erofs_xattr_generic_get,
|
|
};
|
|
|
|
#ifdef CONFIG_EROFS_FS_SECURITY
|
|
static const struct xattr_handler erofs_xattr_security_handler = {
|
|
.prefix = XATTR_SECURITY_PREFIX,
|
|
.flags = EROFS_XATTR_INDEX_SECURITY,
|
|
.get = erofs_xattr_generic_get,
|
|
};
|
|
#endif
|
|
|
|
const struct xattr_handler * const erofs_xattr_handlers[] = {
|
|
&erofs_xattr_user_handler,
|
|
&erofs_xattr_trusted_handler,
|
|
#ifdef CONFIG_EROFS_FS_SECURITY
|
|
&erofs_xattr_security_handler,
|
|
#endif
|
|
NULL,
|
|
};
|
|
|
|
static const char *erofs_xattr_prefix(unsigned int idx, struct dentry *dentry)
|
|
{
|
|
static const struct xattr_handler * const xattr_handler_map[] = {
|
|
[EROFS_XATTR_INDEX_USER] = &erofs_xattr_user_handler,
|
|
#ifdef CONFIG_EROFS_FS_POSIX_ACL
|
|
[EROFS_XATTR_INDEX_POSIX_ACL_ACCESS] = &nop_posix_acl_access,
|
|
[EROFS_XATTR_INDEX_POSIX_ACL_DEFAULT] = &nop_posix_acl_default,
|
|
#endif
|
|
[EROFS_XATTR_INDEX_TRUSTED] = &erofs_xattr_trusted_handler,
|
|
#ifdef CONFIG_EROFS_FS_SECURITY
|
|
[EROFS_XATTR_INDEX_SECURITY] = &erofs_xattr_security_handler,
|
|
#endif
|
|
};
|
|
const struct xattr_handler *handler = NULL;
|
|
|
|
if (idx && idx < ARRAY_SIZE(xattr_handler_map)) {
|
|
handler = xattr_handler_map[idx];
|
|
if (xattr_handler_can_list(handler, dentry))
|
|
return xattr_prefix(handler);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void erofs_xattr_prefixes_cleanup(struct super_block *sb)
|
|
{
|
|
struct erofs_sb_info *sbi = EROFS_SB(sb);
|
|
int i;
|
|
|
|
if (sbi->xattr_prefixes) {
|
|
for (i = 0; i < sbi->xattr_prefix_count; i++)
|
|
kfree(sbi->xattr_prefixes[i].prefix);
|
|
kfree(sbi->xattr_prefixes);
|
|
sbi->xattr_prefixes = NULL;
|
|
}
|
|
}
|
|
|
|
int erofs_xattr_prefixes_init(struct super_block *sb)
|
|
{
|
|
struct erofs_sb_info *sbi = EROFS_SB(sb);
|
|
struct erofs_buf buf = __EROFS_BUF_INITIALIZER;
|
|
erofs_off_t pos = (erofs_off_t)sbi->xattr_prefix_start << 2;
|
|
struct erofs_xattr_prefix_item *pfs;
|
|
int ret = 0, i, len;
|
|
bool plain = erofs_sb_has_plain_xattr_pfx(sbi);
|
|
|
|
if (!sbi->xattr_prefix_count)
|
|
return 0;
|
|
|
|
pfs = kcalloc(sbi->xattr_prefix_count, sizeof(*pfs), GFP_KERNEL);
|
|
if (!pfs)
|
|
return -ENOMEM;
|
|
|
|
if (!plain) {
|
|
if (erofs_sb_has_metabox(sbi))
|
|
(void)erofs_init_metabuf(&buf, sb, true);
|
|
else if (sbi->packed_inode)
|
|
buf.mapping = sbi->packed_inode->i_mapping;
|
|
else
|
|
plain = true;
|
|
}
|
|
if (plain)
|
|
(void)erofs_init_metabuf(&buf, sb, false);
|
|
|
|
for (i = 0; i < sbi->xattr_prefix_count; i++) {
|
|
void *ptr = erofs_read_metadata(sb, &buf, &pos, &len);
|
|
|
|
if (IS_ERR(ptr)) {
|
|
ret = PTR_ERR(ptr);
|
|
break;
|
|
} else if (len < sizeof(*pfs->prefix) ||
|
|
len > EROFS_NAME_LEN + sizeof(*pfs->prefix)) {
|
|
kfree(ptr);
|
|
ret = -EFSCORRUPTED;
|
|
break;
|
|
}
|
|
pfs[i].prefix = ptr;
|
|
pfs[i].infix_len = len - sizeof(struct erofs_xattr_long_prefix);
|
|
}
|
|
|
|
erofs_put_metabuf(&buf);
|
|
if (!ret && erofs_sb_has_ishare_xattrs(sbi)) {
|
|
struct erofs_xattr_prefix_item *pf = pfs + sbi->ishare_xattr_prefix_id;
|
|
struct erofs_xattr_long_prefix *newpfx;
|
|
|
|
newpfx = krealloc(pf->prefix,
|
|
sizeof(*newpfx) + pf->infix_len + 1, GFP_KERNEL);
|
|
if (newpfx) {
|
|
newpfx->infix[pf->infix_len] = '\0';
|
|
pf->prefix = newpfx;
|
|
} else {
|
|
ret = -ENOMEM;
|
|
}
|
|
}
|
|
sbi->xattr_prefixes = pfs;
|
|
if (ret)
|
|
erofs_xattr_prefixes_cleanup(sb);
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_EROFS_FS_POSIX_ACL
|
|
struct posix_acl *erofs_get_acl(struct inode *inode, int type, bool rcu)
|
|
{
|
|
struct posix_acl *acl;
|
|
int prefix, rc;
|
|
char *value = NULL;
|
|
|
|
if (rcu)
|
|
return ERR_PTR(-ECHILD);
|
|
|
|
switch (type) {
|
|
case ACL_TYPE_ACCESS:
|
|
prefix = EROFS_XATTR_INDEX_POSIX_ACL_ACCESS;
|
|
break;
|
|
case ACL_TYPE_DEFAULT:
|
|
prefix = EROFS_XATTR_INDEX_POSIX_ACL_DEFAULT;
|
|
break;
|
|
default:
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
rc = erofs_getxattr(inode, prefix, "", NULL, 0);
|
|
if (rc > 0) {
|
|
value = kmalloc(rc, GFP_KERNEL);
|
|
if (!value)
|
|
return ERR_PTR(-ENOMEM);
|
|
rc = erofs_getxattr(inode, prefix, "", value, rc);
|
|
}
|
|
|
|
if (rc == -ENODATA)
|
|
acl = NULL;
|
|
else if (rc < 0)
|
|
acl = ERR_PTR(rc);
|
|
else
|
|
acl = posix_acl_from_xattr(&init_user_ns, value, rc);
|
|
kfree(value);
|
|
return acl;
|
|
}
|
|
|
|
bool erofs_inode_has_noacl(struct inode *inode, void *kaddr, unsigned int ofs)
|
|
{
|
|
static const unsigned int bitmask =
|
|
BIT(21) | /* system.posix_acl_default */
|
|
BIT(30); /* system.posix_acl_access */
|
|
struct erofs_sb_info *sbi = EROFS_I_SB(inode);
|
|
const struct erofs_xattr_ibody_header *ih = kaddr + ofs;
|
|
|
|
if (EROFS_I(inode)->xattr_isize < sizeof(*ih))
|
|
return true;
|
|
|
|
if (erofs_sb_has_xattr_filter(sbi) && !sbi->xattr_filter_reserved &&
|
|
!check_add_overflow(ofs, sizeof(*ih), &ofs) &&
|
|
ofs <= i_blocksize(inode)) {
|
|
if ((le32_to_cpu(ih->h_name_filter) & bitmask) == bitmask)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_EROFS_FS_PAGE_CACHE_SHARE
|
|
int erofs_xattr_fill_inode_fingerprint(struct erofs_inode_fingerprint *fp,
|
|
struct inode *inode, const char *domain_id)
|
|
{
|
|
struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb);
|
|
struct erofs_xattr_prefix_item *prefix;
|
|
const char *infix;
|
|
int valuelen, base_index;
|
|
|
|
if (!test_opt(&sbi->opt, INODE_SHARE))
|
|
return -EOPNOTSUPP;
|
|
if (!sbi->xattr_prefixes)
|
|
return -EINVAL;
|
|
prefix = sbi->xattr_prefixes + sbi->ishare_xattr_prefix_id;
|
|
infix = prefix->prefix->infix;
|
|
base_index = prefix->prefix->base_index;
|
|
valuelen = erofs_getxattr(inode, base_index, infix, NULL, 0);
|
|
if (valuelen <= 0 || valuelen > (1 << sbi->blkszbits))
|
|
return -EFSCORRUPTED;
|
|
fp->size = valuelen + (domain_id ? strlen(domain_id) : 0);
|
|
fp->opaque = kmalloc(fp->size, GFP_KERNEL);
|
|
if (!fp->opaque)
|
|
return -ENOMEM;
|
|
if (valuelen != erofs_getxattr(inode, base_index, infix,
|
|
fp->opaque, valuelen)) {
|
|
kfree(fp->opaque);
|
|
fp->opaque = NULL;
|
|
return -EFSCORRUPTED;
|
|
}
|
|
memcpy(fp->opaque + valuelen, domain_id, fp->size - valuelen);
|
|
return 0;
|
|
}
|
|
#endif
|