[PATCH v2] erofs-utils: lib: support importing xattrs from tarerofs
Gao Xiang
hsiangkao at linux.alibaba.com
Fri Sep 1 17:26:42 AEST 2023
`SCHILY.xattr.attr` indicates a POSIX.1-2001 coded version of the
Linux extended attributes. Let's dump such xattrs to erofs images.
In addition, `LIBARCHIVE.xattr` is also supported, which uses
URL-Encoding instead of the binary form.
Signed-off-by: Gao Xiang <hsiangkao at linux.alibaba.com>
---
changes since v1:
- fix `SCHILY.xattr.attr` xattr value incorrect sizes reported by Jingbo.
include/erofs/tar.h | 1 +
lib/tar.c | 171 ++++++++++++++++++++++++++++++++++++++++++--
mkfs/main.c | 4 +-
3 files changed, 171 insertions(+), 5 deletions(-)
diff --git a/include/erofs/tar.h b/include/erofs/tar.h
index d5648f6..b50db1d 100644
--- a/include/erofs/tar.h
+++ b/include/erofs/tar.h
@@ -13,6 +13,7 @@ extern "C"
struct erofs_pax_header {
struct stat st;
+ struct list_head xattrs;
bool use_mtime;
bool use_size;
bool use_uid;
diff --git a/lib/tar.c b/lib/tar.c
index 328ab98..53196c1 100644
--- a/lib/tar.c
+++ b/lib/tar.c
@@ -235,6 +235,131 @@ static struct erofs_dentry *tarerofs_get_dentry(struct erofs_inode *pwd, char *p
return d;
}
+struct tarerofs_xattr_item {
+ struct list_head list;
+ char *kv;
+ unsigned int len, namelen;
+};
+
+int tarerofs_insert_xattr(struct list_head *xattrs,
+ char *kv, int namelen, int len, bool skip)
+{
+ struct tarerofs_xattr_item *item;
+ char *nv;
+
+ DBG_BUGON(namelen >= len);
+ list_for_each_entry(item, xattrs, list) {
+ if (!strncmp(item->kv, kv, namelen + 1)) {
+ if (skip)
+ return 0;
+ goto found;
+ }
+ }
+
+ item = malloc(sizeof(*item));
+ if (!item)
+ return -ENOMEM;
+ item->kv = NULL;
+ item->namelen = namelen;
+ namelen = 0;
+ list_add_tail(&item->list, xattrs);
+found:
+ nv = realloc(item->kv, len);
+ if (!nv)
+ return -ENOMEM;
+ item->kv = nv;
+ item->len = len;
+ memcpy(nv + namelen, kv + namelen, len - namelen);
+ return 0;
+}
+
+int tarerofs_merge_xattrs(struct list_head *dst, struct list_head *src)
+{
+ struct tarerofs_xattr_item *item;
+
+ list_for_each_entry(item, src, list) {
+ int ret;
+
+ ret = tarerofs_insert_xattr(dst, item->kv, item->namelen,
+ item->len, true);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+void tarerofs_remove_xattrs(struct list_head *xattrs)
+{
+ struct tarerofs_xattr_item *item, *n;
+
+ list_for_each_entry_safe(item, n, xattrs, list) {
+ DBG_BUGON(!item->kv);
+ free(item->kv);
+ list_del(&item->list);
+ free(item);
+ }
+}
+
+int tarerofs_apply_xattrs(struct erofs_inode *inode, struct list_head *xattrs)
+{
+ struct tarerofs_xattr_item *item;
+ int ret;
+
+ list_for_each_entry(item, xattrs, list) {
+ const char *v = item->kv + item->namelen + 1;
+ unsigned int vsz = item->len - item->namelen - 1;
+
+ if (item->len <= item->namelen - 1) {
+ DBG_BUGON(item->len < item->namelen - 1);
+ continue;
+ }
+ item->kv[item->namelen] = '\0';
+ erofs_dbg("Recording xattr(%s)=\"%s\" (of %u bytes) to file %s",
+ item->kv, v, vsz, inode->i_srcpath);
+ ret = erofs_setxattr(inode, item->kv, v, vsz);
+ if (ret == -ENODATA)
+ erofs_err("Failed to set xattr(%s)=%s to file %s",
+ item->kv, v, inode->i_srcpath);
+ else if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static const char lookup_table[65] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
+
+static int base64_decode(const char *src, int len, u8 *dst)
+{
+ int i, bits = 0, ac = 0;
+ const char *p;
+ u8 *cp = dst;
+
+ if(!(len % 4)) {
+ /* Check for and ignore any end padding */
+ if (src[len - 2] == '=' && src[len - 1] == '=')
+ len -= 2;
+ else if (src[len - 1] == '=')
+ --len;
+ }
+
+ for (i = 0; i < len; i++) {
+ p = strchr(lookup_table, src[i]);
+ if (p == NULL || src[i] == 0)
+ return -2;
+ ac += (p - lookup_table) << bits;
+ bits += 6;
+ if (bits >= 8) {
+ *cp++ = ac & 0xff;
+ ac >>= 8;
+ bits -= 8;
+ }
+ }
+ if (ac)
+ return -1;
+ return cp - dst;
+}
+
int tarerofs_parse_pax_header(int fd, struct erofs_pax_header *eh, u32 size)
{
char *buf, *p;
@@ -260,6 +385,7 @@ int tarerofs_parse_pax_header(int fd, struct erofs_pax_header *eh, u32 size)
}
kv = p + n;
p += len;
+ len -= n;
if (p[-1] != '\n') {
ret = -EIO;
@@ -330,6 +456,34 @@ int tarerofs_parse_pax_header(int fd, struct erofs_pax_header *eh, u32 size)
}
eh->st.st_gid = lln;
eh->use_gid = true;
+ } else if (!strncmp(kv, "SCHILY.xattr.",
+ sizeof("SCHILY.xattr.") - 1)) {
+ char *key = kv + sizeof("SCHILY.xattr.") - 1;
+
+ --len; /* p[-1] == '\0' */
+ ret = tarerofs_insert_xattr(&eh->xattrs, key,
+ value - key - 1,
+ len - (key - kv), false);
+ if (ret)
+ goto out;
+ } else if (!strncmp(kv, "LIBARCHIVE.xattr.",
+ sizeof("LIBARCHIVE.xattr.") - 1)) {
+ char *key;
+ key = kv + sizeof("LIBARCHIVE.xattr.") - 1;
+
+ --len; /* p[-1] == '\0' */
+ ret = base64_decode(value, len - (value - kv),
+ (u8 *)value);
+ if (ret < 0) {
+ ret = -EFSCORRUPTED;
+ goto out;
+ }
+
+ ret = tarerofs_insert_xattr(&eh->xattrs, key,
+ value - key - 1,
+ value - key + ret, false);
+ if (ret)
+ goto out;
} else {
erofs_info("unrecognized pax keyword \"%s\", ignoring", kv);
}
@@ -416,6 +570,7 @@ int tarerofs_parse_tar(struct erofs_inode *root, struct erofs_tarfile *tar)
eh.path = strdup(eh.path);
if (eh.link)
eh.link = strdup(eh.link);
+ init_list_head(&eh.xattrs);
restart:
rem = tar->offset & 511;
@@ -723,15 +878,23 @@ new_inode:
}
}
inode->i_nlink++;
- ret = 0;
- } else if (!inode->i_nlink)
+ } else if (!inode->i_nlink) {
ret = erofs_init_empty_dir(inode);
- else
- ret = 0;
+ if (ret)
+ goto out;
+ }
+
+ ret = tarerofs_merge_xattrs(&eh.xattrs, &tar->global.xattrs);
+ if (ret)
+ goto out;
+
+ ret = tarerofs_apply_xattrs(inode, &eh.xattrs);
+
out:
if (eh.path != path)
free(eh.path);
free(eh.link);
+ tarerofs_remove_xattrs(&eh.xattrs);
return ret;
invalid_tar:
diff --git a/mkfs/main.c b/mkfs/main.c
index 16da9c0..4352e62 100644
--- a/mkfs/main.c
+++ b/mkfs/main.c
@@ -132,7 +132,9 @@ static void usage(void)
}
static unsigned int pclustersize_packed, pclustersize_max;
-static struct erofs_tarfile erofstar;
+static struct erofs_tarfile erofstar = {
+ .global.xattrs = LIST_HEAD_INIT(erofstar.global.xattrs)
+};
static bool tar_mode;
static int parse_extended_opts(const char *opts)
--
2.24.4
More information about the Linux-erofs
mailing list