diff options
author | htyuxe+dhbrei4sq0df8@grr.la <htyuxe+dhbrei4sq0df8@grr.la> | 2019-09-29 19:44:14 +0800 |
---|---|---|
committer | Gao Xiang <hsiangkao@aol.com> | 2019-10-15 23:59:10 +0800 |
commit | 47d6895a5ff9e7fc90d8e4143077b17dbb7a587e (patch) | |
tree | fac8cd3a5cd60e76d6cc57cf5aa18579eb593f98 /lib/xattr.c | |
parent | 0e91d131dcb21b45b21762c77c6d0377fa16f80d (diff) |
erofs-utils: introduce inline xattr support
Load xattrs from source files and pack them into target image.
Link: https://lore.kernel.org/r/20191014114206.590-1-hsiangkao@aol.com
Signed-off-by: htyuxe+dhbrei4sq0df8@grr.la <htyuxe+dhbrei4sq0df8@grr.la>
Signed-off-by: Li Guifu <blucerlee@gmail.com>
Signed-off-by: Gao Xiang <hsiangkao@aol.com>
Diffstat (limited to 'lib/xattr.c')
-rw-r--r-- | lib/xattr.c | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/lib/xattr.c b/lib/xattr.c new file mode 100644 index 0000000..d07d325 --- /dev/null +++ b/lib/xattr.c @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * erofs_utils/lib/xattr.c + * + * Originally contributed by an anonymous person, + * heavily changed by Li Guifu <blucerlee@gmail.com> + * and Gao Xiang <hsiangkao@aol.com> + */ +#include <stdlib.h> +#include <sys/xattr.h> +#ifdef HAVE_LINUX_XATTR_H +#include <linux/xattr.h> +#endif +#include "erofs/print.h" +#include "erofs/hashtable.h" +#include "erofs/xattr.h" + +#define EA_HASHTABLE_BITS 16 + +struct xattr_item { + const char *kvbuf; + unsigned int hash[2], len[2], count; + u8 prefix; + struct hlist_node node; +}; + +struct inode_xattr_node { + struct list_head list; + struct xattr_item *item; +}; + +static DECLARE_HASHTABLE(ea_hashtable, EA_HASHTABLE_BITS); + +static struct xattr_prefix { + const char *prefix; + u16 prefix_len; +} xattr_types[] = { + [EROFS_XATTR_INDEX_USER] = { + XATTR_USER_PREFIX, + XATTR_USER_PREFIX_LEN + }, [EROFS_XATTR_INDEX_POSIX_ACL_ACCESS] = { + XATTR_NAME_POSIX_ACL_ACCESS, + sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1 + }, [EROFS_XATTR_INDEX_POSIX_ACL_DEFAULT] = { + XATTR_NAME_POSIX_ACL_DEFAULT, + sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1 + }, [EROFS_XATTR_INDEX_TRUSTED] = { + XATTR_TRUSTED_PREFIX, + XATTR_TRUSTED_PREFIX_LEN + }, [EROFS_XATTR_INDEX_SECURITY] = { + XATTR_SECURITY_PREFIX, + XATTR_SECURITY_PREFIX_LEN + } +}; + +static unsigned int BKDRHash(char *str, unsigned int len) +{ + const unsigned int seed = 131313; + unsigned int hash = 0; + + while (len) { + hash = hash * seed + (*str++); + --len; + } + return hash; +} + +static unsigned int xattr_item_hash(u8 prefix, 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]; +} + +static unsigned int put_xattritem(struct xattr_item *item) +{ + if (item->count > 1) + return --item->count; + free(item); + return 0; +} + +static struct xattr_item *get_xattritem(u8 prefix, char *kvbuf, + unsigned int len[2]) +{ + struct xattr_item *item; + unsigned int hash[2], hkey; + + hkey = xattr_item_hash(prefix, 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] && + item->hash[0] == hash[0] && item->hash[1] == hash[1] && + !memcmp(kvbuf, item->kvbuf, len[0] + len[1])) { + free(kvbuf); + ++item->count; + return item; + } + } + + item = malloc(sizeof(*item)); + if (!item) { + free(kvbuf); + return ERR_PTR(-ENOMEM); + } + INIT_HLIST_NODE(&item->node); + item->count = 1; + item->kvbuf = kvbuf; + item->len[0] = len[0]; + item->len[1] = len[1]; + item->hash[0] = hash[0]; + item->hash[1] = hash[1]; + item->prefix = prefix; + hash_add(ea_hashtable, &item->node, hkey); + return item; +} + +static bool match_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 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); + + /* determine length of the value */ + ret = lgetxattr(path, key, NULL, 0); + if (ret < 0) + return ERR_PTR(-errno); + len[1] = ret; + + /* allocate key-value buffer */ + len[0] = keylen - prefixlen; + + kvbuf = malloc(len[0] + len[1]); + if (!kvbuf) + return ERR_PTR(-ENOMEM); + + memcpy(kvbuf, key + prefixlen, len[0]); + if (len[1]) { + /* copy value to buffer */ + ret = lgetxattr(path, key, kvbuf + len[0], len[1]); + if (ret < 0) { + free(kvbuf); + return ERR_PTR(-errno); + } + if (len[1] != ret) { + erofs_err("size of xattr value got changed just now (%u-> %ld)", + len[1], (long)ret); + len[1] = ret; + } + } + return get_xattritem(prefix, kvbuf, len); +} + +static int inode_xattr_add(struct list_head *hlist, struct xattr_item *item) +{ + struct inode_xattr_node *node = malloc(sizeof(*node)); + + if (!node) + return -ENOMEM; + init_list_head(&node->list); + node->item = item; + list_add(&node->list, hlist); + return 0; +} + +static int read_xattrs_from_file(const char *path, struct list_head *ixattrs) +{ + int ret = 0; + char *keylst, *key; + ssize_t kllen = llistxattr(path, NULL, 0); + + if (kllen < 0 && errno != ENODATA) { + erofs_err("llistxattr to get the size of names for %s failed", + path); + return -errno; + } + if (kllen <= 1) + return 0; + + keylst = malloc(kllen); + if (!keylst) + return -ENOMEM; + + /* copy the list of attribute keys to the buffer.*/ + kllen = llistxattr(path, keylst, kllen); + if (kllen < 0) { + erofs_err("llistxattr to get names for %s failed", path); + ret = -errno; + goto err; + } + + /* + * loop over the list of zero terminated strings with the + * attribute keys. Use the remaining buffer length to determine + * the end of the list. + */ + key = keylst; + while (kllen > 0) { + unsigned int keylen = strlen(key); + struct xattr_item *item = parse_one_xattr(path, key, keylen); + + if (IS_ERR(item)) { + ret = PTR_ERR(item); + goto err; + } + + if (ixattrs) { + ret = inode_xattr_add(ixattrs, item); + if (ret < 0) + goto err; + } + kllen -= keylen + 1; + key += keylen + 1; + } +err: + free(keylst); + return ret; + +} + +int erofs_prepare_xattr_ibody(const char *path, struct list_head *ixattrs) +{ + int ret; + struct inode_xattr_node *node; + + /* check if xattr is disabled */ + if (cfg.c_inline_xattr_tolerance < 0) + return 0; + + ret = read_xattrs_from_file(path, ixattrs); + if (ret < 0) + return ret; + + if (list_empty(ixattrs)) + return 0; + + /* 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; + + ret += sizeof(struct erofs_xattr_entry); + ret = EROFS_XATTR_ALIGN(ret + item->len[0] + item->len[1]); + } + return ret; +} + +char *erofs_export_xattr_ibody(struct list_head *ixattrs, unsigned int size) +{ + struct inode_xattr_node *node, *n; + struct erofs_xattr_ibody_header *header; + unsigned int p; + char *buf = calloc(1, size); + + if (!buf) + return ERR_PTR(-ENOMEM); + + header = (struct erofs_xattr_ibody_header *)buf; + header->h_shared_count = 0; + + p = sizeof(struct erofs_xattr_ibody_header); + list_for_each_entry_safe(node, n, ixattrs, 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]); + + list_del(&node->list); + free(node); + put_xattritem(item); + } + DBG_BUGON(p > size); + return buf; +} + |