As tar(5) [1] mentioned: `The key value is URL-encoded: All non-ASCII
characters and the two special characters "=" and "%" are encoded as
"%" followed by two uppercase hexadecimal digits.`

Fix it now.

[1] https://man.freebsd.org/cgi/man.cgi?tar(5)
Fixes: c0063a73b01b ("erofs-utils: lib: support importing xattrs from tarerofs")
Signed-off-by: Gao Xiang <hsiang...@linux.alibaba.com>
---
 lib/tar.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 52 insertions(+), 7 deletions(-)

diff --git a/lib/tar.c b/lib/tar.c
index 9642e2e..2ea3858 100644
--- a/lib/tar.c
+++ b/lib/tar.c
@@ -438,6 +438,47 @@ static int base64_decode(const char *src, int len, u8 *dst)
        return cp - dst;
 }
 
+static int tohex(int c)
+{
+       if (c >= '0' && c <= '9')
+               return c - '0';
+       else if (c >= 'A' && c <= 'F')
+               return c - 'A' + 10;
+       else if (c >= 'a' && c <= 'f')
+               return c - 'a' + 10;
+       return -1;
+}
+
+static unsigned int url_decode(char *str, unsigned int len)
+{
+       const char *s = str;
+       char *d = str;
+       int d1, d2;
+
+       for (; len && *s != '\0' && *s != '%'; ++d, ++s, --len);
+       if (!len || *s == '\0')
+               return d - str;
+
+       while (len && *s != '\0') {
+               if (*s == '%' && len > 2) {
+                       /* Try to convert % escape */
+                       d1 = tohex(s[1]), d2 = tohex(s[2]);
+
+                       /* Look good, consume three chars */
+                       if (d1 >= 0 && d2 >= 0) {
+                               s += 3;
+                               len -= 3;
+                               *d++ = (d1 << 4) | d2;
+                               continue;
+                       }
+                       /* Otherwise, treat '%' as normal char */
+               }
+               *d++ = *s++;
+               --len;
+       }
+       return d - str;
+}
+
 int tarerofs_parse_pax_header(struct erofs_iostream *ios,
                              struct erofs_pax_header *eh, u32 size)
 {
@@ -454,7 +495,7 @@ int tarerofs_parse_pax_header(struct erofs_iostream *ios,
                goto out;
 
        while (p < buf + size) {
-               char *kv, *value;
+               char *kv, *key, *value;
                int len, n;
                /* extended records are of the format: "LEN NAME=VALUE\n" */
                ret = sscanf(p, "%d %n", &len, &n);
@@ -537,8 +578,7 @@ int tarerofs_parse_pax_header(struct erofs_iostream *ios,
                                eh->use_gid = true;
                        } else if (!strncmp(kv, "SCHILY.xattr.",
                                   sizeof("SCHILY.xattr.") - 1)) {
-                               char *key = kv + sizeof("SCHILY.xattr.") - 1;
-
+                               key = kv + sizeof("SCHILY.xattr.") - 1;
                                --len; /* p[-1] == '\0' */
                                ret = tarerofs_insert_xattr(&eh->xattrs, key,
                                                value - key - 1,
@@ -547,9 +587,10 @@ int tarerofs_parse_pax_header(struct erofs_iostream *ios,
                                        goto out;
                        } else if (!strncmp(kv, "LIBARCHIVE.xattr.",
                                   sizeof("LIBARCHIVE.xattr.") - 1)) {
-                               char *key;
-                               key = kv + sizeof("LIBARCHIVE.xattr.") - 1;
+                               int namelen;
 
+                               key = kv + sizeof("LIBARCHIVE.xattr.") - 1;
+                               namelen = url_decode(key, value - key - 1);
                                --len; /* p[-1] == '\0' */
                                ret = base64_decode(value, len - (value - kv),
                                                    (u8 *)value);
@@ -558,9 +599,13 @@ int tarerofs_parse_pax_header(struct erofs_iostream *ios,
                                        goto out;
                                }
 
+                               if (namelen != value - key - 1) {
+                                       key[namelen] = '=';
+                                       memmove(key + namelen + 1, value, ret);
+                                       value = key + namelen + 1;
+                               }
                                ret = tarerofs_insert_xattr(&eh->xattrs, key,
-                                               value - key - 1,
-                                               value - key + ret, false);
+                                               namelen, namelen + 1 + ret, 
false);
                                if (ret)
                                        goto out;
                        } else {
-- 
2.39.3 (Apple Git-146)

Reply via email to