diff options
author | Jingbo Xu <jefflexu@linux.alibaba.com> | 2023-09-13 11:18:04 +0800 |
---|---|---|
committer | Gao Xiang <hsiangkao@linux.alibaba.com> | 2023-09-14 10:56:07 +0800 |
commit | 5df285cf405d7a0c9774fd47af104c81921c7bd8 (patch) | |
tree | 3593d408fed697fd803380cc60573e53266705e9 | |
parent | fdded5b7a810f53b2ea43f0cf3de54802184f0d9 (diff) | |
download | erofs-utils-5df285cf405d7a0c9774fd47af104c81921c7bd8.tar.gz |
erofs-utils: lib: refactor extended attribute name prefixes
Previously, the extended attribute name in `struct xattr_item` was
actually the part with the matched prefix stripped, which makes it
clumsy to strip or delete a specific attribute from one file.
To fix this, make the complete attribute name stored in xattr_item.
One thing worth noting is that the attribute name in xattr_item has a
trailing '\0' for ease of name comparison, while the trailing '\0' will
get stripped when writing to on-disk erofs_xattr_entry.
Signed-off-by: Jingbo Xu <jefflexu@linux.alibaba.com>
Link: https://lore.kernel.org/r/20230913031804.84819-1-jefflexu@linux.alibaba.com
-rw-r--r-- | lib/xattr.c | 377 |
1 files changed, 158 insertions, 219 deletions
diff --git a/lib/xattr.c b/lib/xattr.c index 54a6ae2..bf63a81 100644 --- a/lib/xattr.c +++ b/lib/xattr.c @@ -51,6 +51,12 @@ #ifndef XATTR_NAME_POSIX_ACL_DEFAULT #define XATTR_NAME_POSIX_ACL_DEFAULT "system.posix_acl_default" #endif +#ifndef XATTR_NAME_SECURITY_SELINUX +#define XATTR_NAME_SECURITY_SELINUX "security.selinux" +#endif +#ifndef XATTR_NAME_SECURITY_CAPABILITY +#define XATTR_NAME_SECURITY_CAPABILITY "security.capability" +#endif #ifndef OVL_XATTR_NAMESPACE #define OVL_XATTR_NAMESPACE "overlay." #endif @@ -72,12 +78,23 @@ #define EA_HASHTABLE_BITS 16 +/* one extra byte for the trailing `\0` of attribute name */ +#define EROFS_XATTR_KSIZE(kvlen) (kvlen[0] + 1) +#define EROFS_XATTR_KVSIZE(kvlen) (EROFS_XATTR_KSIZE(kvlen) + kvlen[1]) + +/* + * @base_index: the index of the matched predefined short prefix + * @prefix: the index of the matched long prefix, if any; + * same as base_index otherwise + * @prefix_len: the length of the matched long prefix if any; + * the length of the matched predefined short prefix otherwise + */ struct xattr_item { struct xattr_item *next_shared_xattr; const char *kvbuf; unsigned int hash[2], len[2], count; int shared_xattr_id; - u8 prefix; + unsigned int prefix, base_index, prefix_len; struct hlist_node node; }; @@ -93,7 +110,7 @@ static unsigned int shared_xattrs_count; static struct xattr_prefix { const char *prefix; - u8 prefix_len; + unsigned int prefix_len; } xattr_types[] = { [EROFS_XATTR_INDEX_USER] = { XATTR_USER_PREFIX, @@ -116,11 +133,27 @@ static struct xattr_prefix { struct ea_type_node { struct list_head list; struct xattr_prefix type; - u8 index; + unsigned int index, base_index, base_len; }; + static LIST_HEAD(ea_name_prefixes); static unsigned int ea_prefix_count; +static bool match_prefix(const char *key, unsigned int *index, + unsigned int *len) +{ + struct xattr_prefix *p; + + for (p = xattr_types; p < xattr_types + ARRAY_SIZE(xattr_types); ++p) { + if (p->prefix && !strncmp(p->prefix, key, p->prefix_len)) { + *len = p->prefix_len; + *index = p - xattr_types; + return true; + } + } + return false; +} + static unsigned int BKDRHash(char *str, unsigned int len) { const unsigned int seed = 131313; @@ -133,13 +166,12 @@ static unsigned int BKDRHash(char *str, unsigned int len) return hash; } -static unsigned int xattr_item_hash(u8 prefix, char *buf, - unsigned int len[2], unsigned int hash[2]) +static unsigned int xattr_item_hash(char *buf, unsigned int len[2], + unsigned int hash[2]) { hash[0] = BKDRHash(buf, len[0]); /* key */ hash[1] = BKDRHash(buf + len[0], len[1]); /* value */ - - return prefix ^ hash[0] ^ hash[1]; + return hash[0] ^ hash[1]; } static unsigned int put_xattritem(struct xattr_item *item) @@ -150,17 +182,15 @@ static unsigned int put_xattritem(struct xattr_item *item) return 0; } -static struct xattr_item *get_xattritem(u8 prefix, char *kvbuf, - unsigned int len[2]) +static struct xattr_item *get_xattritem(char *kvbuf, unsigned int len[2]) { struct xattr_item *item; + struct ea_type_node *tnode; unsigned int hash[2], hkey; - hkey = xattr_item_hash(prefix, kvbuf, len, hash); - + hkey = xattr_item_hash(kvbuf, len, hash); hash_for_each_possible(ea_hashtable, item, node, hkey) { - if (prefix == item->prefix && - item->len[0] == len[0] && item->len[1] == len[1] && + if (item->len[0] == len[0] && item->len[1] == len[1] && item->hash[0] == hash[0] && item->hash[1] == hash[1] && !memcmp(kvbuf, item->kvbuf, len[0] + len[1])) { free(kvbuf); @@ -174,6 +204,14 @@ static struct xattr_item *get_xattritem(u8 prefix, char *kvbuf, free(kvbuf); return ERR_PTR(-ENOMEM); } + + if (!match_prefix(kvbuf, &item->base_index, &item->prefix_len)) { + free(item); + free(kvbuf); + return ERR_PTR(-ENODATA); + } + DBG_BUGON(len[0] < item->prefix_len); + INIT_HLIST_NODE(&item->node); item->count = 1; item->kvbuf = kvbuf; @@ -182,56 +220,32 @@ static struct xattr_item *get_xattritem(u8 prefix, char *kvbuf, item->hash[0] = hash[0]; item->hash[1] = hash[1]; item->shared_xattr_id = -1; - item->prefix = prefix; - hash_add(ea_hashtable, &item->node, hkey); - return item; -} - -static bool match_base_prefix(const char *key, u8 *index, u16 *len) -{ - struct xattr_prefix *p; - - for (p = xattr_types; p < xattr_types + ARRAY_SIZE(xattr_types); ++p) { - if (p->prefix && !strncmp(p->prefix, key, p->prefix_len)) { - *len = p->prefix_len; - *index = p - xattr_types; - return true; - } - } - return false; -} - -static bool match_prefix(const char *key, u8 *index, u16 *len) -{ - struct xattr_prefix *p; - struct ea_type_node *tnode; + item->prefix = item->base_index; list_for_each_entry(tnode, &ea_name_prefixes, list) { - p = &tnode->type; - if (p->prefix && !strncmp(p->prefix, key, p->prefix_len)) { - *len = p->prefix_len; - *index = tnode->index; - return true; + if (item->base_index == tnode->base_index && + !strncmp(tnode->type.prefix, kvbuf, + tnode->type.prefix_len)) { + item->prefix = tnode->index; + item->prefix_len = tnode->type.prefix_len; + break; } } - return match_base_prefix(key, index, len); + hash_add(ea_hashtable, &item->node, hkey); + return item; } static struct xattr_item *parse_one_xattr(const char *path, const char *key, unsigned int keylen) { ssize_t ret; - u8 prefix; - u16 prefixlen; unsigned int len[2]; char *kvbuf; erofs_dbg("parse xattr [%s] of %s", path, key); - if (!match_prefix(key, &prefix, &prefixlen)) - return ERR_PTR(-ENODATA); - - DBG_BUGON(keylen < prefixlen); + /* length of the key */ + len[0] = keylen; /* determine length of the value */ #ifdef HAVE_LGETXATTR @@ -246,19 +260,18 @@ static struct xattr_item *parse_one_xattr(const char *path, const char *key, len[1] = ret; /* allocate key-value buffer */ - len[0] = keylen - prefixlen; - - kvbuf = malloc(len[0] + len[1]); + kvbuf = malloc(EROFS_XATTR_KVSIZE(len)); if (!kvbuf) return ERR_PTR(-ENOMEM); - memcpy(kvbuf, key + prefixlen, len[0]); + memcpy(kvbuf, key, EROFS_XATTR_KSIZE(len)); if (len[1]) { /* copy value to buffer */ #ifdef HAVE_LGETXATTR - ret = lgetxattr(path, key, kvbuf + len[0], len[1]); + ret = lgetxattr(path, key, kvbuf + EROFS_XATTR_KSIZE(len), + len[1]); #elif defined(__APPLE__) - ret = getxattr(path, key, kvbuf + len[0], len[1], 0, - XATTR_NOFOLLOW); + ret = getxattr(path, key, kvbuf + EROFS_XATTR_KSIZE(len), + len[1], 0, XATTR_NOFOLLOW); #else free(kvbuf); return ERR_PTR(-EOPNOTSUPP); @@ -273,7 +286,7 @@ static struct xattr_item *parse_one_xattr(const char *path, const char *key, len[1] = ret; } } - return get_xattritem(prefix, kvbuf, len); + return get_xattritem(kvbuf, len); } static struct xattr_item *erofs_get_selabel_xattr(const char *srcpath, @@ -308,16 +321,17 @@ static struct xattr_item *erofs_get_selabel_xattr(const char *srcpath, return NULL; } - len[0] = sizeof("selinux") - 1; + len[0] = sizeof(XATTR_NAME_SECURITY_SELINUX) - 1; len[1] = strlen(secontext); - kvbuf = malloc(len[0] + len[1] + 1); + kvbuf = malloc(EROFS_XATTR_KVSIZE(len)); if (!kvbuf) { freecon(secontext); return ERR_PTR(-ENOMEM); } - sprintf(kvbuf, "selinux%s", secontext); + sprintf(kvbuf, "%s", XATTR_NAME_SECURITY_SELINUX); + memcpy(kvbuf + EROFS_XATTR_KSIZE(len), secontext, len[1]); freecon(secontext); - return get_xattritem(EROFS_XATTR_INDEX_SECURITY, kvbuf, len); + return get_xattritem(kvbuf, len); } #endif return NULL; @@ -466,24 +480,18 @@ int erofs_setxattr(struct erofs_inode *inode, char *key, char *kvbuf; unsigned int len[2]; struct xattr_item *item; - u8 prefix; - u16 prefixlen; - - if (!match_prefix(key, &prefix, &prefixlen)) - return -ENODATA; + len[0] = strlen(key); len[1] = size; - /* allocate key-value buffer */ - len[0] = strlen(key) - prefixlen; - kvbuf = malloc(len[0] + len[1]); + kvbuf = malloc(EROFS_XATTR_KVSIZE(len)); if (!kvbuf) return -ENOMEM; - memcpy(kvbuf, key + prefixlen, len[0]); - memcpy(kvbuf + len[0], value, size); + memcpy(kvbuf, key, EROFS_XATTR_KSIZE(len)); + memcpy(kvbuf + EROFS_XATTR_KSIZE(len), value, size); - item = get_xattritem(prefix, kvbuf, len); + item = get_xattritem(kvbuf, len); if (IS_ERR(item)) return PTR_ERR(item); DBG_BUGON(!item); @@ -513,22 +521,22 @@ static int erofs_droid_xattr_set_caps(struct erofs_inode *inode) if (!capabilities) return 0; - len[0] = sizeof("capability") - 1; + len[0] = sizeof(XATTR_NAME_SECURITY_CAPABILITY) - 1; len[1] = sizeof(caps); - kvbuf = malloc(len[0] + len[1]); + kvbuf = malloc(EROFS_XATTR_KVSIZE(len)); if (!kvbuf) return -ENOMEM; - memcpy(kvbuf, "capability", len[0]); + sprintf(kvbuf, "%s", XATTR_NAME_SECURITY_CAPABILITY); caps.magic_etc = VFS_CAP_REVISION_2 | VFS_CAP_FLAGS_EFFECTIVE; caps.data[0].permitted = (u32) capabilities; caps.data[0].inheritable = 0; caps.data[1].permitted = (u32) (capabilities >> 32); caps.data[1].inheritable = 0; - memcpy(kvbuf + len[0], &caps, len[1]); + memcpy(kvbuf + EROFS_XATTR_KSIZE(len), &caps, len[1]); - item = get_xattritem(EROFS_XATTR_INDEX_SECURITY, kvbuf, len); + item = get_xattritem(kvbuf, len); if (IS_ERR(item)) return PTR_ERR(item); DBG_BUGON(!item); @@ -558,6 +566,13 @@ int erofs_scan_file_xattrs(struct erofs_inode *inode) return erofs_droid_xattr_set_caps(inode); } +static inline unsigned int erofs_next_xattr_align(unsigned int pos, + struct xattr_item *item) +{ + return EROFS_XATTR_ALIGN(pos + sizeof(struct erofs_xattr_entry) + + item->len[0] + item->len[1] - item->prefix_len); +} + int erofs_prepare_xattr_ibody(struct erofs_inode *inode) { int ret; @@ -572,14 +587,13 @@ int erofs_prepare_xattr_ibody(struct erofs_inode *inode) /* get xattr ibody size */ ret = sizeof(struct erofs_xattr_ibody_header); list_for_each_entry(node, ixattrs, list) { - const struct xattr_item *item = node->item; + struct xattr_item *item = node->item; if (item->shared_xattr_id >= 0) { ret += sizeof(__le32); continue; } - ret += sizeof(struct erofs_xattr_entry); - ret = EROFS_XATTR_ALIGN(ret + item->len[0] + item->len[1]); + ret = erofs_next_xattr_align(ret, item); } inode->xattr_isize = ret; return ret; @@ -688,25 +702,9 @@ static int comp_shared_xattr_item(const void *a, const void *b) return la > lb; } -static inline int erofs_xattr_index_by_prefix(const char *prefix, int *len) -{ - if (!strncmp(prefix, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)){ - *len = XATTR_USER_PREFIX_LEN; - return EROFS_XATTR_INDEX_USER; - } else if (!strncmp(prefix, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN)) { - *len = XATTR_TRUSTED_PREFIX_LEN; - return EROFS_XATTR_INDEX_TRUSTED; - } else if (!strncmp(prefix, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN)) { - *len = XATTR_SECURITY_PREFIX_LEN; - return EROFS_XATTR_INDEX_SECURITY; - } - return -ENODATA; -} - int erofs_xattr_write_name_prefixes(struct erofs_sb_info *sbi, FILE *f) { struct ea_type_node *tnode; - struct xattr_prefix *p; off_t offset; if (!ea_prefix_count) @@ -732,16 +730,13 @@ int erofs_xattr_write_name_prefixes(struct erofs_sb_info *sbi, FILE *f) u8 data[EROFS_NAME_LEN + 2 + sizeof(struct erofs_xattr_long_prefix)]; } u; - int ret, len; + int len, infix_len; - p = &tnode->type; - ret = erofs_xattr_index_by_prefix(p->prefix, &len); - if (ret < 0) - return ret; - u.s.prefix.base_index = ret; - memcpy(u.s.prefix.infix, p->prefix + len, p->prefix_len - len); - len = sizeof(struct erofs_xattr_long_prefix) + - p->prefix_len - len; + u.s.prefix.base_index = tnode->base_index; + infix_len = tnode->type.prefix_len - tnode->base_len; + memcpy(u.s.prefix.infix, tnode->type.prefix + tnode->base_len, + infix_len); + len = sizeof(struct erofs_xattr_long_prefix) + infix_len; u.s.size = cpu_to_le16(len); if (fwrite(&u.s, sizeof(__le16) + len, 1, f) != 1) return -EIO; @@ -754,11 +749,30 @@ int erofs_xattr_write_name_prefixes(struct erofs_sb_info *sbi, FILE *f) return 0; } +static void erofs_write_xattr_entry(char *buf, struct xattr_item *item) +{ + struct erofs_xattr_entry entry = { + .e_name_index = item->prefix, + .e_name_len = item->len[0] - item->prefix_len, + .e_value_size = cpu_to_le16(item->len[1]), + }; + + memcpy(buf, &entry, sizeof(entry)); + buf += sizeof(struct erofs_xattr_entry); + memcpy(buf, item->kvbuf + item->prefix_len, + item->len[0] - item->prefix_len); + buf += item->len[0] - item->prefix_len; + memcpy(buf, item->kvbuf + item->len[0] + 1, item->len[1]); + + erofs_dbg("writing xattr %d %s (%d %s)", item->base_index, item->kvbuf, + item->prefix, item->kvbuf + item->prefix_len); +} + int erofs_build_shared_xattrs_from_path(struct erofs_sb_info *sbi, const char *path) { int ret; struct erofs_buffer_head *bh; - struct xattr_item *n, **sorted_n; + struct xattr_item *item, *n, **sorted_n; char *buf; unsigned int p, i; erofs_off_t off; @@ -787,13 +801,11 @@ int erofs_build_shared_xattrs_from_path(struct erofs_sb_info *sbi, const char *p i = 0; while (shared_xattrs_list) { - struct xattr_item *item = shared_xattrs_list; - + item = shared_xattrs_list; sorted_n[i++] = item; shared_xattrs_list = item->next_shared_xattr; - shared_xattrs_size += sizeof(struct erofs_xattr_entry); - shared_xattrs_size = EROFS_XATTR_ALIGN(shared_xattrs_size + - item->len[0] + item->len[1]); + shared_xattrs_size = erofs_next_xattr_align(shared_xattrs_size, + item); } DBG_BUGON(i != shared_xattrs_count); sorted_n[i] = NULL; @@ -820,20 +832,11 @@ int erofs_build_shared_xattrs_from_path(struct erofs_sb_info *sbi, const char *p off %= erofs_blksiz(sbi); p = 0; for (i = 0; i < shared_xattrs_count; i++) { - struct xattr_item *item = sorted_n[i]; - const struct erofs_xattr_entry entry = { - .e_name_index = item->prefix, - .e_name_len = item->len[0], - .e_value_size = cpu_to_le16(item->len[1]) - }; - + item = sorted_n[i]; + erofs_write_xattr_entry(buf + p, item); item->next_shared_xattr = sorted_n[i + 1]; item->shared_xattr_id = (off + p) / sizeof(__le32); - - memcpy(buf + p, &entry, sizeof(entry)); - p += sizeof(struct erofs_xattr_entry); - memcpy(buf + p, item->kvbuf, item->len[0] + item->len[1]); - p = EROFS_XATTR_ALIGN(p + item->len[0] + item->len[1]); + p = erofs_next_xattr_align(p, item); } shared_xattrs_list = sorted_n[0]; free(sorted_n); @@ -846,70 +849,12 @@ out: return ret; } -static int erofs_xattr_filter_hashbit(struct xattr_item *item) -{ - u8 prefix = item->prefix; - const char *key = item->kvbuf; - unsigned int len = item->len[0]; - char *name = NULL; - uint32_t hashbit; - - if (prefix & EROFS_XATTR_LONG_PREFIX) { - struct ea_type_node *tnode; - u16 prefix_len; - int ret; - - list_for_each_entry(tnode, &ea_name_prefixes, list) { - if (tnode->index == item->prefix) { - ret = asprintf(&name, "%s%.*s", - tnode->type.prefix, len, key); - if (ret < 0) - return -ENOMEM; - break; - } - } - if (!name) - return -ENOENT; - - if (!match_base_prefix(name, &prefix, &prefix_len)) { - free(name); - return -ENOENT; - } - key = name + prefix_len; - len = strlen(key); - } - - hashbit = xxh32(key, len, EROFS_XATTR_FILTER_SEED + prefix) & - (EROFS_XATTR_FILTER_BITS - 1); - if (name) - free(name); - return hashbit; -} - -static u32 erofs_xattr_filter_map(struct list_head *ixattrs) -{ - struct inode_xattr_node *node, *n; - u32 name_filter; - int hashbit; - - name_filter = 0; - list_for_each_entry_safe(node, n, ixattrs, list) { - hashbit = erofs_xattr_filter_hashbit(node->item); - if (hashbit < 0) { - erofs_warn("failed to generate xattr name filter: %s", - strerror(-hashbit)); - return 0; - } - name_filter |= (1UL << hashbit); - } - return EROFS_XATTR_FILTER_DEFAULT & ~name_filter; -} - char *erofs_export_xattr_ibody(struct erofs_inode *inode) { struct list_head *ixattrs = &inode->i_xattrs; unsigned int size = inode->xattr_isize; struct inode_xattr_node *node, *n; + struct xattr_item *item; struct erofs_xattr_ibody_header *header; LIST_HEAD(ilst); unsigned int p; @@ -922,16 +867,29 @@ char *erofs_export_xattr_ibody(struct erofs_inode *inode) header->h_shared_count = 0; if (cfg.c_xattr_name_filter) { - header->h_name_filter = - cpu_to_le32(erofs_xattr_filter_map(ixattrs)); + u32 name_filter = 0; + int hashbit; + unsigned int base_len; + + list_for_each_entry(node, ixattrs, list) { + item = node->item; + base_len = xattr_types[item->base_index].prefix_len; + hashbit = xxh32(item->kvbuf + base_len, + item->len[0] - base_len, + EROFS_XATTR_FILTER_SEED + item->base_index) & + (EROFS_XATTR_FILTER_BITS - 1); + name_filter |= (1UL << hashbit); + } + name_filter = EROFS_XATTR_FILTER_DEFAULT & ~name_filter; + + header->h_name_filter = cpu_to_le32(name_filter); if (header->h_name_filter) erofs_sb_set_xattr_filter(inode->sbi); } p = sizeof(struct erofs_xattr_ibody_header); list_for_each_entry_safe(node, n, ixattrs, list) { - struct xattr_item *const item = node->item; - + item = node->item; list_del(&node->list); /* move inline xattrs to the onstack list */ @@ -948,18 +906,9 @@ char *erofs_export_xattr_ibody(struct erofs_inode *inode) } list_for_each_entry_safe(node, n, &ilst, list) { - struct xattr_item *const item = node->item; - const struct erofs_xattr_entry entry = { - .e_name_index = item->prefix, - .e_name_len = item->len[0], - .e_value_size = cpu_to_le16(item->len[1]) - }; - - memcpy(buf + p, &entry, sizeof(entry)); - p += sizeof(struct erofs_xattr_entry); - memcpy(buf + p, item->kvbuf, item->len[0] + item->len[1]); - p = EROFS_XATTR_ALIGN(p + item->len[0] + item->len[1]); - + item = node->item; + erofs_write_xattr_entry(buf + p, item); + p = erofs_next_xattr_align(p, item); list_del(&node->list); free(node); put_xattritem(item); @@ -1354,8 +1303,7 @@ int erofs_getxattr(struct erofs_inode *vi, const char *name, char *buffer, size_t buffer_size) { int ret; - u8 prefix; - u16 prefixlen; + unsigned int prefix, prefixlen; struct getxattr_iter it; if (!name) @@ -1365,7 +1313,7 @@ int erofs_getxattr(struct erofs_inode *vi, const char *name, char *buffer, if (ret) return ret; - if (!match_base_prefix(name, &prefix, &prefixlen)) + if (!match_prefix(name, &prefix, &prefixlen)) return -ENODATA; it.it.sbi = vi->sbi; @@ -1532,34 +1480,25 @@ int erofs_listxattr(struct erofs_inode *vi, char *buffer, size_t buffer_size) int erofs_xattr_insert_name_prefix(const char *prefix) { struct ea_type_node *tnode; - struct xattr_prefix *p; - bool matched = false; - char *s; if (ea_prefix_count >= 0x80 || strlen(prefix) > UINT8_MAX) return -EOVERFLOW; - for (p = xattr_types; p < xattr_types + ARRAY_SIZE(xattr_types); ++p) { - if (!strncmp(p->prefix, prefix, p->prefix_len)) { - matched = true; - break; - } - } - if (!matched) - return -ENODATA; - - s = strdup(prefix); - if (!s) + tnode = calloc(1, sizeof(*tnode)); + if (!tnode) return -ENOMEM; - tnode = malloc(sizeof(*tnode)); - if (!tnode) { - free(s); - return -ENOMEM; + if (!match_prefix(prefix, &tnode->base_index, &tnode->base_len)) { + free(tnode); + return -ENODATA; } - tnode->type.prefix = s; tnode->type.prefix_len = strlen(prefix); + tnode->type.prefix = strdup(prefix); + if (!tnode->type.prefix) { + free(tnode); + return -ENOMEM; + } tnode->index = EROFS_XATTR_LONG_PREFIX | ea_prefix_count; ea_prefix_count++; |