test code v1
This commit is contained in:
@@ -0,0 +1,842 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) 2017-2018 HUAWEI, Inc.
|
||||
* https://www.huawei.com/
|
||||
* Copyright (C) 2021-2022, Alibaba Cloud
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/dirent.h>
|
||||
#include <sys/extattr.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/libkern.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/acl.h>
|
||||
|
||||
#include "internal.h"
|
||||
#include "xattr.h"
|
||||
|
||||
struct posix_acl_xattr_entry {
|
||||
uint16_t e_tag;
|
||||
uint16_t e_perm;
|
||||
uint32_t e_id;
|
||||
};
|
||||
|
||||
struct posix_acl_xattr_header {
|
||||
uint32_t a_version;
|
||||
};
|
||||
|
||||
#define POSIX_ACL_XATTR_VERSION 0x0002
|
||||
#define EROFS_XATTR_FILTER_POSIX_ACL \
|
||||
((1U << 21) | (1U << 30))
|
||||
|
||||
static int
|
||||
erofs_xattr_backing_size(struct erofs_mount *em, struct erofs_node *backing_en,
|
||||
uint64_t *sizep)
|
||||
{
|
||||
if (backing_en != NULL) {
|
||||
*sizep = backing_en->size;
|
||||
return (0);
|
||||
}
|
||||
if (em->blocks > (UINT64_MAX >> em->block_bits))
|
||||
return (EOVERFLOW);
|
||||
*sizep = em->blocks << em->block_bits;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
erofs_xattr_read_backing(struct erofs_mount *em,
|
||||
struct erofs_node *backing_en, uint64_t off, size_t len, void **bufp)
|
||||
{
|
||||
uint64_t backing_size;
|
||||
int error;
|
||||
|
||||
error = erofs_xattr_backing_size(em, backing_en, &backing_size);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
if (off > backing_size || (uint64_t)len > backing_size - off)
|
||||
return (EINTEGRITY);
|
||||
if (backing_en != NULL)
|
||||
return (erofs_read_data(em, backing_en, off, len, bufp));
|
||||
if (off > INT64_MAX)
|
||||
return (EOVERFLOW);
|
||||
return (erofs_bread(em, (off_t)off, len, bufp));
|
||||
}
|
||||
|
||||
/*
|
||||
* Read one prefix table metadata record.
|
||||
*
|
||||
* When backing_en == NULL the record is in the physical metadata area;
|
||||
* otherwise it lives in the selected metadata carrier's logical data stream.
|
||||
*/
|
||||
static int
|
||||
erofs_xattr_read_metadata(struct erofs_mount *em, struct erofs_node *backing_en,
|
||||
uint64_t *offp, void **bufp, size_t *lenp)
|
||||
{
|
||||
uint16_t raw_len;
|
||||
void *buf, *hdrbuf;
|
||||
uint64_t off;
|
||||
size_t len;
|
||||
int error;
|
||||
|
||||
if (*offp > UINT64_MAX - (sizeof(struct erofs_xattr_entry) - 1))
|
||||
return (EOVERFLOW);
|
||||
off = roundup2(*offp, sizeof(struct erofs_xattr_entry));
|
||||
error = erofs_xattr_read_backing(em, backing_en, off, sizeof(raw_len),
|
||||
&hdrbuf);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
raw_len = le16toh(*(uint16_t *)hdrbuf);
|
||||
erofs_brelse(hdrbuf);
|
||||
len = (raw_len == 0) ? (size_t)UINT16_MAX + 1 : raw_len;
|
||||
if (len < sizeof(struct erofs_xattr_long_prefix) ||
|
||||
len > EROFS_NAME_LEN + sizeof(struct erofs_xattr_long_prefix))
|
||||
return (EINTEGRITY);
|
||||
if (off > UINT64_MAX - sizeof(raw_len))
|
||||
return (EOVERFLOW);
|
||||
error = erofs_xattr_read_backing(em, backing_en,
|
||||
off + sizeof(raw_len), len, &buf);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
*offp = off + sizeof(raw_len) + len;
|
||||
*bufp = buf;
|
||||
*lenp = len;
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
erofs_xattr_prefixes_cleanup(struct erofs_mount *em)
|
||||
{
|
||||
if (em->xattr_prefixes == NULL)
|
||||
return;
|
||||
for (uint8_t i = 0; i < em->xattr_prefix_count; i++)
|
||||
free(em->xattr_prefixes[i].infix, M_EROFS);
|
||||
free(em->xattr_prefixes, M_EROFS);
|
||||
em->xattr_prefixes = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
erofs_xattr_prefixes_init(struct erofs_mount *em)
|
||||
{
|
||||
struct erofs_xattr_long_prefix *prefix = NULL;
|
||||
struct erofs_node packed_en, *prefix_en;
|
||||
uint64_t off;
|
||||
size_t infix_len, len;
|
||||
int error;
|
||||
|
||||
if ((em->feature_incompat & EROFS_FEATURE_INCOMPAT_XATTR_PREFIXES) ==
|
||||
0 ||
|
||||
em->xattr_prefix_count == 0)
|
||||
return (0);
|
||||
prefix_en = NULL;
|
||||
if ((em->feature_compat & EROFS_FEATURE_COMPAT_PLAIN_XATTR_PFX) == 0) {
|
||||
if (erofs_sb_has_metabox(em)) {
|
||||
if (em->metabox_en == NULL)
|
||||
return (EINTEGRITY);
|
||||
prefix_en = em->metabox_en;
|
||||
} else if (em->packed_inode != NULL) {
|
||||
prefix_en = em->packed_inode;
|
||||
} else if (em->packed_nid != 0) {
|
||||
error = erofs_read_inode(em, em->packed_nid, &packed_en);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
if (packed_en.vtype != VREG)
|
||||
return (EINTEGRITY);
|
||||
prefix_en = &packed_en;
|
||||
}
|
||||
}
|
||||
em->xattr_prefixes = malloc(sizeof(*em->xattr_prefixes) *
|
||||
em->xattr_prefix_count,
|
||||
M_EROFS, M_WAITOK | M_ZERO);
|
||||
off = (uint64_t)em->xattr_prefix_start << 2;
|
||||
for (uint8_t i = 0; i < em->xattr_prefix_count; i++) {
|
||||
error = erofs_xattr_read_metadata(em, prefix_en, &off,
|
||||
(void **)&prefix, &len);
|
||||
if (error != 0)
|
||||
goto fail;
|
||||
infix_len = len - sizeof(*prefix);
|
||||
em->xattr_prefixes[i].base_index = prefix->base_index;
|
||||
em->xattr_prefixes[i].infix_len = infix_len;
|
||||
em->xattr_prefixes[i].infix = malloc(infix_len + 1, M_EROFS,
|
||||
M_WAITOK);
|
||||
memcpy(em->xattr_prefixes[i].infix, prefix->infix, infix_len);
|
||||
em->xattr_prefixes[i].infix[infix_len] = '\0';
|
||||
erofs_brelse(prefix);
|
||||
prefix = NULL;
|
||||
}
|
||||
return (0);
|
||||
fail:
|
||||
if (prefix != NULL)
|
||||
erofs_brelse(prefix);
|
||||
erofs_xattr_prefixes_cleanup(em);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
erofs_xattr_move(void *value, size_t value_size, struct uio *uio, size_t *sizep)
|
||||
{
|
||||
if (sizep != NULL)
|
||||
*sizep = value_size;
|
||||
if (uio == NULL || value_size == 0)
|
||||
return (0);
|
||||
return (uiomove(value, value_size, uio));
|
||||
}
|
||||
|
||||
static int
|
||||
erofs_xattr_load_body(struct erofs_mount *em, struct erofs_node *en,
|
||||
char **bodyp, struct erofs_xattr_ibody_header **ihp, size_t *header_sizep)
|
||||
{
|
||||
struct erofs_xattr_ibody_header *ih;
|
||||
char *body;
|
||||
uint64_t body_off;
|
||||
size_t header_size;
|
||||
int error;
|
||||
|
||||
if (en->xattr_isize < sizeof(*ih))
|
||||
return (EINTEGRITY);
|
||||
if (en->inode_off > UINT64_MAX - en->inode_isize)
|
||||
return (EINTEGRITY);
|
||||
body_off = en->inode_off + en->inode_isize;
|
||||
error = erofs_xattr_read_backing(em,
|
||||
erofs_nid_in_metabox(en->nid) ? em->metabox_en : NULL, body_off,
|
||||
en->xattr_isize, (void **)&body);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
ih = (struct erofs_xattr_ibody_header *)body;
|
||||
if (en->xattr_isize == sizeof(*ih)) {
|
||||
error = EOPNOTSUPP;
|
||||
goto fail;
|
||||
}
|
||||
header_size = sizeof(*ih) + sizeof(uint32_t) * ih->h_shared_count;
|
||||
if (header_size > en->xattr_isize) {
|
||||
error = EINTEGRITY;
|
||||
goto fail;
|
||||
}
|
||||
*bodyp = body;
|
||||
*ihp = ih;
|
||||
*header_sizep = header_size;
|
||||
return (0);
|
||||
fail:
|
||||
erofs_brelse(body);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
erofs_xattr_validate_entry(struct erofs_xattr_entry *entry, size_t remaining,
|
||||
size_t *entry_sizep, size_t *value_sizep)
|
||||
{
|
||||
size_t entry_size, min_size, value_size;
|
||||
|
||||
if (remaining < sizeof(*entry))
|
||||
return (EINTEGRITY);
|
||||
value_size = le16toh(entry->e_value_size);
|
||||
min_size = sizeof(*entry) + entry->e_name_len + value_size;
|
||||
if (min_size > remaining)
|
||||
return (EINTEGRITY);
|
||||
entry_size = erofs_xattr_entry_size(entry);
|
||||
if (entry_size > remaining)
|
||||
return (EINTEGRITY);
|
||||
if (entry_sizep != NULL)
|
||||
*entry_sizep = entry_size;
|
||||
if (value_sizep != NULL)
|
||||
*value_sizep = value_size;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static bool
|
||||
erofs_xattr_prefix(uint8_t base_index, int *namespacep,
|
||||
const char **prefixp, size_t *prefix_lenp)
|
||||
{
|
||||
switch (base_index) {
|
||||
case EROFS_XATTR_INDEX_USER:
|
||||
*namespacep = EXTATTR_NAMESPACE_USER;
|
||||
*prefixp = NULL;
|
||||
*prefix_lenp = 0;
|
||||
return (true);
|
||||
case EROFS_XATTR_INDEX_POSIX_ACL_ACCESS:
|
||||
*namespacep = EXTATTR_NAMESPACE_SYSTEM;
|
||||
*prefixp = "posix_acl_access";
|
||||
*prefix_lenp = sizeof("posix_acl_access") - 1;
|
||||
return (true);
|
||||
case EROFS_XATTR_INDEX_POSIX_ACL_DEFAULT:
|
||||
*namespacep = EXTATTR_NAMESPACE_SYSTEM;
|
||||
*prefixp = "posix_acl_default";
|
||||
*prefix_lenp = sizeof("posix_acl_default") - 1;
|
||||
return (true);
|
||||
case EROFS_XATTR_INDEX_TRUSTED:
|
||||
*namespacep = EXTATTR_NAMESPACE_SYSTEM;
|
||||
*prefixp = "trusted.";
|
||||
*prefix_lenp = sizeof("trusted.") - 1;
|
||||
return (true);
|
||||
case EROFS_XATTR_INDEX_SECURITY:
|
||||
*namespacep = EXTATTR_NAMESPACE_SYSTEM;
|
||||
*prefixp = "security.";
|
||||
*prefix_lenp = sizeof("security.") - 1;
|
||||
return (true);
|
||||
case EROFS_XATTR_INDEX_LUSTRE:
|
||||
default:
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
erofs_xattr_namespace_prefix(int attrnamespace, uint8_t base_index,
|
||||
const char **prefixp, size_t *prefix_lenp)
|
||||
{
|
||||
int mapped_namespace;
|
||||
|
||||
if (attrnamespace != EXTATTR_NAMESPACE_USER &&
|
||||
attrnamespace != EXTATTR_NAMESPACE_SYSTEM)
|
||||
return (EOPNOTSUPP);
|
||||
if (!erofs_xattr_prefix(base_index, &mapped_namespace, prefixp,
|
||||
prefix_lenp))
|
||||
return (ENOATTR);
|
||||
if (mapped_namespace != attrnamespace)
|
||||
return (ENOATTR);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
erofs_xattr_list_move(const char *namespace_prefix, size_t namespace_prefix_len,
|
||||
const char *infix, size_t infix_len, const char *name, uint8_t name_len,
|
||||
struct uio *uio, size_t *sizep)
|
||||
{
|
||||
uint8_t total_name_len;
|
||||
int error;
|
||||
|
||||
if (namespace_prefix_len + infix_len + name_len > EROFS_NAME_LEN)
|
||||
return (EINTEGRITY);
|
||||
total_name_len = namespace_prefix_len + infix_len + name_len;
|
||||
if (sizep != NULL) {
|
||||
*sizep += total_name_len + 1;
|
||||
return (0);
|
||||
}
|
||||
if (uio == NULL)
|
||||
return (0);
|
||||
error = uiomove(__DECONST(void *, &total_name_len), 1, uio);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
if (namespace_prefix_len != 0) {
|
||||
error = uiomove(__DECONST(void *, namespace_prefix),
|
||||
namespace_prefix_len, uio);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
}
|
||||
if (infix_len != 0) {
|
||||
error = uiomove(__DECONST(void *, infix), infix_len, uio);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
}
|
||||
return (uiomove(__DECONST(void *, name), name_len, uio));
|
||||
}
|
||||
|
||||
static int
|
||||
erofs_xattr_resolve_name(struct erofs_mount *em,
|
||||
const struct erofs_xattr_entry *entry, uint8_t *base_indexp,
|
||||
const char **infixp, size_t *infix_lenp)
|
||||
{
|
||||
struct erofs_xattr_prefix_item *prefix;
|
||||
uint8_t prefix_id;
|
||||
|
||||
if ((entry->e_name_index & EROFS_XATTR_LONG_PREFIX) == 0) {
|
||||
*base_indexp = entry->e_name_index;
|
||||
*infixp = NULL;
|
||||
*infix_lenp = 0;
|
||||
return (0);
|
||||
}
|
||||
if (em->xattr_prefixes == NULL)
|
||||
return (ENOATTR);
|
||||
prefix_id = entry->e_name_index & EROFS_XATTR_LONG_PREFIX_MASK;
|
||||
if (prefix_id >= em->xattr_prefix_count)
|
||||
return (ENOATTR);
|
||||
prefix = &em->xattr_prefixes[prefix_id];
|
||||
*base_indexp = prefix->base_index;
|
||||
*infixp = prefix->infix;
|
||||
*infix_lenp = prefix->infix_len;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static bool
|
||||
erofs_xattr_name_match(const char *namespace_prefix,
|
||||
size_t namespace_prefix_len, const char *infix, size_t infix_len,
|
||||
const struct erofs_xattr_entry *entry, const char *name, size_t name_len)
|
||||
{
|
||||
if (name_len != namespace_prefix_len + infix_len + entry->e_name_len)
|
||||
return (false);
|
||||
if (namespace_prefix_len != 0 &&
|
||||
memcmp(name, namespace_prefix, namespace_prefix_len) != 0)
|
||||
return (false);
|
||||
if (infix_len != 0 &&
|
||||
memcmp(name + namespace_prefix_len, infix, infix_len) != 0)
|
||||
return (false);
|
||||
return (memcmp(name + namespace_prefix_len + infix_len, entry->e_name,
|
||||
entry->e_name_len) == 0);
|
||||
}
|
||||
|
||||
static int
|
||||
erofs_xattr_shared_entry_offset(struct erofs_mount *em, uint32_t shared_id,
|
||||
uint64_t *phys_offp)
|
||||
{
|
||||
uint64_t base, relative;
|
||||
|
||||
if (em->xattr_blkaddr > (UINT64_MAX >> em->block_bits))
|
||||
return (EOVERFLOW);
|
||||
base = (uint64_t)em->xattr_blkaddr << em->block_bits;
|
||||
relative = (uint64_t)shared_id * sizeof(uint32_t);
|
||||
if (relative > UINT64_MAX - base)
|
||||
return (EOVERFLOW);
|
||||
*phys_offp = base + relative;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
erofs_xattr_load_shared_entry(struct erofs_mount *em, uint32_t shared_id,
|
||||
struct erofs_xattr_entry **entryp, size_t *entry_sizep, size_t *value_sizep)
|
||||
{
|
||||
struct erofs_xattr_entry *entry;
|
||||
struct erofs_node *backing_en;
|
||||
void *hdrbuf;
|
||||
uint64_t off;
|
||||
size_t entry_size, value_size;
|
||||
int error;
|
||||
|
||||
backing_en = erofs_sb_has_shared_ea_in_metabox(em) ? em->metabox_en : NULL;
|
||||
if (erofs_sb_has_shared_ea_in_metabox(em) && backing_en == NULL)
|
||||
return (EINTEGRITY);
|
||||
|
||||
error = erofs_xattr_shared_entry_offset(em, shared_id, &off);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
error = erofs_xattr_read_backing(em, backing_en, off, sizeof(*entry),
|
||||
&hdrbuf);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
entry = hdrbuf;
|
||||
value_size = le16toh(entry->e_value_size);
|
||||
entry_size = erofs_xattr_entry_size(entry);
|
||||
|
||||
erofs_brelse(hdrbuf);
|
||||
|
||||
error = erofs_xattr_read_backing(em, backing_en, off, entry_size,
|
||||
(void **)entryp);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
if (entry_sizep != NULL)
|
||||
*entry_sizep = entry_size;
|
||||
if (value_sizep != NULL)
|
||||
*value_sizep = value_size;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
erofs_inode_has_noacl(struct erofs_mount *em, struct erofs_node *en,
|
||||
bool *noaclp)
|
||||
{
|
||||
struct erofs_xattr_ibody_header *ih;
|
||||
struct erofs_node *backing_en;
|
||||
uint64_t body_off;
|
||||
uint32_t name_filter;
|
||||
int error;
|
||||
|
||||
*noaclp = false;
|
||||
if (en->xattr_isize < sizeof(*ih)) {
|
||||
*noaclp = true;
|
||||
return (0);
|
||||
}
|
||||
if (!erofs_sb_has_xattr_filter(em))
|
||||
return (0);
|
||||
if (en->inode_off > UINT64_MAX - en->inode_isize)
|
||||
return (EINTEGRITY);
|
||||
body_off = en->inode_off + en->inode_isize;
|
||||
backing_en = erofs_nid_in_metabox(en->nid) ? em->metabox_en : NULL;
|
||||
error = erofs_xattr_read_backing(em, backing_en, body_off, sizeof(*ih),
|
||||
(void **)&ih);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
name_filter = le32toh(ih->h_name_filter);
|
||||
erofs_brelse(ih);
|
||||
*noaclp = (name_filter & EROFS_XATTR_FILTER_POSIX_ACL) ==
|
||||
EROFS_XATTR_FILTER_POSIX_ACL;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
erofs_acl_from_mode(struct erofs_node *en, acl_type_t type, struct acl *aclp)
|
||||
{
|
||||
if (type == ACL_TYPE_DEFAULT) {
|
||||
aclp->acl_cnt = 0;
|
||||
return;
|
||||
}
|
||||
aclp->acl_cnt = 3;
|
||||
aclp->acl_entry[0].ae_tag = ACL_USER_OBJ;
|
||||
aclp->acl_entry[0].ae_id = ACL_UNDEFINED_ID;
|
||||
aclp->acl_entry[0].ae_perm = (en->mode >> 6) & ACL_PERM_BITS;
|
||||
aclp->acl_entry[1].ae_tag = ACL_GROUP_OBJ;
|
||||
aclp->acl_entry[1].ae_id = ACL_UNDEFINED_ID;
|
||||
aclp->acl_entry[1].ae_perm = (en->mode >> 3) & ACL_PERM_BITS;
|
||||
aclp->acl_entry[2].ae_tag = ACL_OTHER;
|
||||
aclp->acl_entry[2].ae_id = ACL_UNDEFINED_ID;
|
||||
aclp->acl_entry[2].ae_perm = en->mode & ACL_PERM_BITS;
|
||||
}
|
||||
|
||||
struct erofs_xattr_iter {
|
||||
struct erofs_mount *em;
|
||||
struct erofs_node *en;
|
||||
int attrnamespace;
|
||||
const char *name;
|
||||
size_t name_len;
|
||||
struct uio *uio;
|
||||
size_t *sizep;
|
||||
};
|
||||
|
||||
static int
|
||||
erofs_getxattr_foreach(struct erofs_xattr_iter *it,
|
||||
struct erofs_xattr_entry *entry, size_t value_size)
|
||||
{
|
||||
const char *infix, *namespace_prefix;
|
||||
size_t infix_len, namespace_prefix_len;
|
||||
uint8_t base_index;
|
||||
int error;
|
||||
|
||||
error = erofs_xattr_resolve_name(it->em, entry, &base_index, &infix,
|
||||
&infix_len);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
error = erofs_xattr_namespace_prefix(it->attrnamespace, base_index,
|
||||
&namespace_prefix, &namespace_prefix_len);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
if (!erofs_xattr_name_match(namespace_prefix, namespace_prefix_len,
|
||||
infix, infix_len, entry, it->name, it->name_len))
|
||||
return (ENOATTR);
|
||||
return (erofs_xattr_move(entry->e_name + entry->e_name_len, value_size,
|
||||
it->uio, it->sizep));
|
||||
}
|
||||
|
||||
static int
|
||||
erofs_listxattr_foreach(struct erofs_xattr_iter *it,
|
||||
struct erofs_xattr_entry *entry)
|
||||
{
|
||||
const char *infix, *namespace_prefix;
|
||||
size_t infix_len, namespace_prefix_len;
|
||||
uint8_t base_index;
|
||||
int error;
|
||||
|
||||
error = erofs_xattr_resolve_name(it->em, entry, &base_index, &infix,
|
||||
&infix_len);
|
||||
if (error == ENOATTR)
|
||||
return (0);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
error = erofs_xattr_namespace_prefix(it->attrnamespace, base_index,
|
||||
&namespace_prefix, &namespace_prefix_len);
|
||||
if (error == ENOATTR)
|
||||
return (0);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
return (erofs_xattr_list_move(namespace_prefix, namespace_prefix_len,
|
||||
infix, infix_len, entry->e_name, entry->e_name_len, it->uio,
|
||||
it->sizep));
|
||||
}
|
||||
|
||||
static int
|
||||
erofs_xattr_iter_inline(struct erofs_xattr_iter *it, char *body,
|
||||
size_t header_size, bool get)
|
||||
{
|
||||
struct erofs_xattr_entry *entry;
|
||||
char *cursor;
|
||||
size_t entry_size, remaining, value_size;
|
||||
int error;
|
||||
|
||||
remaining = it->en->xattr_isize - header_size;
|
||||
cursor = body + header_size;
|
||||
while (remaining != 0) {
|
||||
entry = (struct erofs_xattr_entry *)cursor;
|
||||
error = erofs_xattr_validate_entry(entry, remaining,
|
||||
&entry_size, get ? &value_size : NULL);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
if (get)
|
||||
error = erofs_getxattr_foreach(it, entry, value_size);
|
||||
else
|
||||
error = erofs_listxattr_foreach(it, entry);
|
||||
if (get) {
|
||||
if (error != ENOATTR)
|
||||
return (error);
|
||||
} else if (error != 0) {
|
||||
return (error);
|
||||
}
|
||||
cursor += entry_size;
|
||||
remaining -= entry_size;
|
||||
}
|
||||
return (get ? ENOATTR : 0);
|
||||
}
|
||||
|
||||
static int
|
||||
erofs_xattr_iter_shared(struct erofs_xattr_iter *it,
|
||||
struct erofs_xattr_ibody_header *ih, bool get)
|
||||
{
|
||||
struct erofs_xattr_entry *entry;
|
||||
uint32_t shared_id;
|
||||
size_t value_size;
|
||||
int error;
|
||||
|
||||
for (uint8_t i = 0; i < ih->h_shared_count; i++) {
|
||||
shared_id = le32toh(ih->h_shared_xattrs[i]);
|
||||
error = erofs_xattr_load_shared_entry(it->em, shared_id, &entry,
|
||||
NULL, get ? &value_size : NULL);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
if (get)
|
||||
error = erofs_getxattr_foreach(it, entry, value_size);
|
||||
else
|
||||
error = erofs_listxattr_foreach(it, entry);
|
||||
erofs_brelse(entry);
|
||||
if (get) {
|
||||
if (error != ENOATTR)
|
||||
return (error);
|
||||
} else if (error != 0) {
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
return (get ? ENOATTR : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Look up one inline/shared xattr by name.
|
||||
*
|
||||
* Name exposure rules:
|
||||
* - user namespace: bare name, no "user." prefix;
|
||||
* - system namespace: exposes full "trusted.*" / "security.*" names.
|
||||
*/
|
||||
int
|
||||
erofs_getxattr(struct vnode *vp, int attrnamespace, const char *name,
|
||||
struct uio *uio, size_t *sizep)
|
||||
{
|
||||
struct erofs_mount *em;
|
||||
struct erofs_node *en;
|
||||
struct erofs_xattr_ibody_header *ih;
|
||||
struct erofs_xattr_iter it;
|
||||
char *body;
|
||||
size_t header_size, name_len;
|
||||
int error;
|
||||
|
||||
em = MTOE(vp->v_mount);
|
||||
en = VTOE(vp);
|
||||
if (name == NULL || name[0] == '\0')
|
||||
return (EINVAL);
|
||||
name_len = strlen(name);
|
||||
if (name_len > EROFS_NAME_LEN)
|
||||
return (EINVAL);
|
||||
if (en->xattr_isize == 0)
|
||||
return (ENOATTR);
|
||||
error = erofs_xattr_load_body(em, en, &body, &ih, &header_size);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
it.em = em;
|
||||
it.en = en;
|
||||
it.attrnamespace = attrnamespace;
|
||||
it.name = name;
|
||||
it.name_len = name_len;
|
||||
it.uio = uio;
|
||||
it.sizep = sizep;
|
||||
error = erofs_xattr_iter_inline(&it, body, header_size, true);
|
||||
if (error == ENOATTR)
|
||||
error = erofs_xattr_iter_shared(&it, ih, true);
|
||||
erofs_brelse(body);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enumerate inline/shared xattr names for a given namespace.
|
||||
*
|
||||
* Return format: 1-byte name length followed by non-NUL-terminated name bytes.
|
||||
*/
|
||||
int
|
||||
erofs_listxattr(struct vnode *vp, int attrnamespace, struct uio *uio,
|
||||
size_t *sizep)
|
||||
{
|
||||
struct erofs_mount *em;
|
||||
struct erofs_node *en;
|
||||
struct erofs_xattr_ibody_header *ih;
|
||||
struct erofs_xattr_iter it;
|
||||
char *body;
|
||||
size_t header_size;
|
||||
int error;
|
||||
|
||||
em = MTOE(vp->v_mount);
|
||||
en = VTOE(vp);
|
||||
if (sizep != NULL)
|
||||
*sizep = 0;
|
||||
if (en->xattr_isize == 0)
|
||||
return (0);
|
||||
error = erofs_xattr_load_body(em, en, &body, &ih, &header_size);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
it.em = em;
|
||||
it.en = en;
|
||||
it.attrnamespace = attrnamespace;
|
||||
it.name = NULL;
|
||||
it.name_len = 0;
|
||||
it.uio = uio;
|
||||
it.sizep = sizep;
|
||||
error = erofs_xattr_iter_inline(&it, body, header_size, false);
|
||||
if (error == 0)
|
||||
error = erofs_xattr_iter_shared(&it, ih, false);
|
||||
erofs_brelse(body);
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
erofs_get_acl(struct vnode *vp, acl_type_t type, struct acl *aclp)
|
||||
{
|
||||
struct erofs_mount *em;
|
||||
struct erofs_node *en;
|
||||
const char *xattr_name;
|
||||
struct uio auio;
|
||||
struct iovec aiov;
|
||||
struct posix_acl_xattr_header hdr;
|
||||
struct posix_acl_xattr_entry entry;
|
||||
uint8_t buf[sizeof(hdr) + sizeof(entry) * ACL_MAX_ENTRIES];
|
||||
size_t size;
|
||||
uint32_t id;
|
||||
bool noacl;
|
||||
int error, count, i, j, phase;
|
||||
|
||||
em = MTOE(vp->v_mount);
|
||||
if (!test_opt(&em->opt, POSIX_ACL))
|
||||
return (EOPNOTSUPP);
|
||||
|
||||
en = VTOE(vp);
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
xattr_name = "posix_acl_access";
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
if (vp->v_type != VDIR)
|
||||
return (EINVAL);
|
||||
xattr_name = "posix_acl_default";
|
||||
break;
|
||||
default:
|
||||
return (EINVAL);
|
||||
}
|
||||
error = erofs_inode_has_noacl(em, en, &noacl);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
if (noacl) {
|
||||
erofs_acl_from_mode(en, type, aclp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
error = erofs_getxattr(vp, EXTATTR_NAMESPACE_SYSTEM, xattr_name, NULL,
|
||||
&size);
|
||||
if (error == ENOATTR) {
|
||||
erofs_acl_from_mode(en, type, aclp);
|
||||
return (0);
|
||||
}
|
||||
if (error != 0)
|
||||
return (error);
|
||||
if (size > sizeof(buf))
|
||||
return (EINTEGRITY);
|
||||
|
||||
aiov.iov_base = buf;
|
||||
aiov.iov_len = size;
|
||||
auio.uio_iov = &aiov;
|
||||
auio.uio_iovcnt = 1;
|
||||
auio.uio_offset = 0;
|
||||
auio.uio_resid = size;
|
||||
auio.uio_segflg = UIO_SYSSPACE;
|
||||
auio.uio_rw = UIO_READ;
|
||||
auio.uio_td = curthread;
|
||||
error = erofs_getxattr(vp, EXTATTR_NAMESPACE_SYSTEM, xattr_name, &auio,
|
||||
NULL);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
if (auio.uio_resid != 0)
|
||||
return (EINTEGRITY);
|
||||
|
||||
if (size < sizeof(hdr) || (size - sizeof(hdr)) % sizeof(entry) != 0)
|
||||
return (EINTEGRITY);
|
||||
|
||||
memcpy(&hdr, buf, sizeof(hdr));
|
||||
if (le32toh(hdr.a_version) != POSIX_ACL_XATTR_VERSION)
|
||||
return (EINTEGRITY);
|
||||
|
||||
count = (size - sizeof(hdr)) / sizeof(entry);
|
||||
if (count > ACL_MAX_ENTRIES)
|
||||
return (EINTEGRITY);
|
||||
if (count == 0) {
|
||||
erofs_acl_from_mode(en, type, aclp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
aclp->acl_cnt = count;
|
||||
phase = 0;
|
||||
for (i = 0; i < count; i++) {
|
||||
uint16_t tag, perm;
|
||||
|
||||
memcpy(&entry, buf + sizeof(hdr) + i * sizeof(entry),
|
||||
sizeof(entry));
|
||||
tag = le16toh(entry.e_tag);
|
||||
perm = le16toh(entry.e_perm);
|
||||
|
||||
id = le32toh(entry.e_id);
|
||||
if ((perm & ~ACL_PERM_BITS) != 0)
|
||||
return (EINTEGRITY);
|
||||
switch (tag) {
|
||||
case ACL_USER_OBJ:
|
||||
if (phase != 0 || id != UINT32_MAX)
|
||||
return (EINTEGRITY);
|
||||
phase = 1;
|
||||
break;
|
||||
case ACL_USER:
|
||||
if ((phase != 1 && phase != 2) || id == UINT32_MAX)
|
||||
return (EINTEGRITY);
|
||||
phase = 2;
|
||||
break;
|
||||
case ACL_GROUP_OBJ:
|
||||
if ((phase != 1 && phase != 2) || id != UINT32_MAX)
|
||||
return (EINTEGRITY);
|
||||
phase = 3;
|
||||
break;
|
||||
case ACL_GROUP:
|
||||
if ((phase != 3 && phase != 4) || id == UINT32_MAX)
|
||||
return (EINTEGRITY);
|
||||
phase = 4;
|
||||
break;
|
||||
case ACL_MASK:
|
||||
if ((phase != 3 && phase != 4) || id != UINT32_MAX)
|
||||
return (EINTEGRITY);
|
||||
phase = 5;
|
||||
break;
|
||||
case ACL_OTHER:
|
||||
if ((phase != 3 && phase != 4 && phase != 5) ||
|
||||
id != UINT32_MAX)
|
||||
return (EINTEGRITY);
|
||||
phase = 6;
|
||||
break;
|
||||
default:
|
||||
return (EINTEGRITY);
|
||||
}
|
||||
if (tag == ACL_USER || tag == ACL_GROUP) {
|
||||
for (j = 0; j < i; j++) {
|
||||
if (aclp->acl_entry[j].ae_tag == tag &&
|
||||
aclp->acl_entry[j].ae_id == id)
|
||||
return (EINTEGRITY);
|
||||
}
|
||||
}
|
||||
|
||||
aclp->acl_entry[i].ae_tag = tag;
|
||||
aclp->acl_entry[i].ae_perm = perm;
|
||||
aclp->acl_entry[i].ae_id = (id == UINT32_MAX) ? ACL_UNDEFINED_ID : id;
|
||||
}
|
||||
if (phase != 6 || acl_posix1e_check(aclp) != 0)
|
||||
return (EINTEGRITY);
|
||||
|
||||
return (0);
|
||||
}
|
||||
Reference in New Issue
Block a user