test code v1

This commit is contained in:
2026-08-13 10:48:49 +02:00
parent f3b1165f19
commit f6aeadb22d
300 changed files with 0 additions and 31306 deletions
+842
View File
@@ -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);
}