From 6623b1fe4df1e2fceb27eff286a86cf36809b2bc Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Thu, 4 Jul 2019 12:43:57 +0200 Subject: libtar: add support for xattr extensions Signed-off-by: David Oberhollenzer --- lib/tar/base64.c | 33 ++++++++++++++++++++++++++++++++ lib/tar/cleanup.c | 12 ++++++++++++ lib/tar/internal.h | 6 ++++++ lib/tar/read_header.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++- lib/tar/urldecode.c | 31 ++++++++++++++++++++++++++++++ 5 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 lib/tar/base64.c create mode 100644 lib/tar/urldecode.c (limited to 'lib/tar') diff --git a/lib/tar/base64.c b/lib/tar/base64.c new file mode 100644 index 0000000..313e9f4 --- /dev/null +++ b/lib/tar/base64.c @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "internal.h" + +static uint8_t convert(char in) +{ + if (isupper(in)) + return in - 'A'; + if (islower(in)) + return in - 'a' + 26; + if (isdigit(in)) + return in - '0' + 52; + if (in == '+') + return 62; + if (in == '/' || in == '-') + return 63; + return 0; +} + +void base64_decode(uint8_t *out, const char *in) +{ + char temp[4]; + + while (*in != '\0' && *in != '=') { + temp[0] = *in == '\0' ? 0 : convert(*(in++)); + temp[1] = *in == '\0' ? 0 : convert(*(in++)); + temp[2] = *in == '\0' ? 0 : convert(*(in++)); + temp[3] = *in == '\0' ? 0 : convert(*(in++)); + + *(out++) = ((temp[0] << 2) & 0xFC) | ((temp[1] >> 4) & 0x03); + *(out++) = ((temp[1] << 4) & 0xF0) | ((temp[2] >> 2) & 0x0F); + *(out++) = ((temp[2] << 6) & 0xC0) | ( temp[3] & 0x3F); + } +} diff --git a/lib/tar/cleanup.c b/lib/tar/cleanup.c index c4d1734..a34c28b 100644 --- a/lib/tar/cleanup.c +++ b/lib/tar/cleanup.c @@ -12,8 +12,20 @@ void free_sparse_list(sparse_map_t *sparse) } } +void free_xattr_list(tar_xattr_t *list) +{ + tar_xattr_t *old; + + while (list != NULL) { + old = list; + list = list->next; + free(old); + } +} + void clear_header(tar_header_decoded_t *hdr) { + free_xattr_list(hdr->xattr); free_sparse_list(hdr->sparse); free(hdr->name); free(hdr->link_target); diff --git a/lib/tar/internal.h b/lib/tar/internal.h index 3c0e27f..04a97d3 100644 --- a/lib/tar/internal.h +++ b/lib/tar/internal.h @@ -50,4 +50,10 @@ sparse_map_t *read_gnu_old_sparse(int fd, tar_header_t *hdr); void free_sparse_list(sparse_map_t *sparse); +void free_xattr_list(tar_xattr_t *list); + +void base64_decode(uint8_t *out, const char *in); + +void urldecode(char *str); + #endif /* INTERNAL_H */ diff --git a/lib/tar/read_header.c b/lib/tar/read_header.c index db40d7d..94c4a69 100644 --- a/lib/tar/read_header.c +++ b/lib/tar/read_header.c @@ -60,12 +60,29 @@ fail: return NULL; } +static tar_xattr_t *mkxattr(const char *key, size_t keylen, + const char *value, size_t valuelen) +{ + tar_xattr_t *xattr; + + xattr = calloc(1, sizeof(*xattr) + keylen + 1 + valuelen + 1); + if (xattr == NULL) + return NULL; + + xattr->key = xattr->data; + xattr->value = xattr->data + keylen + 1; + memcpy(xattr->key, key, keylen); + memcpy(xattr->value, value, valuelen); + return xattr; +} + static int read_pax_header(int fd, uint64_t entsize, unsigned int *set_by_pax, tar_header_decoded_t *out) { sparse_map_t *sparse_last = NULL, *sparse; uint64_t field, offset = 0, num_bytes = 0; - char *buffer, *line; + char *buffer, *line, *key, *ptr, *value; + tar_xattr_t *xattr; uint64_t i; buffer = record_to_memory(fd, entsize); @@ -183,6 +200,39 @@ static int read_pax_header(int fd, uint64_t entsize, unsigned int *set_by_pax, sparse_last->next = sparse; sparse_last = sparse; } + } else if (!strncmp(line, "SCHILY.xattr.", 13)) { + key = line + 13; + + ptr = strrchr(key, '='); + if (ptr == NULL || ptr == key) + continue; + + value = ptr + 1; + + xattr = mkxattr(key, ptr - key, value, strlen(value)); + if (xattr == NULL) + goto fail_errno; + + xattr->next = out->xattr; + out->xattr = xattr; + } else if (!strncmp(line, "LIBARCHIVE.xattr.", 17)) { + key = line + 17; + + ptr = strrchr(key, '='); + if (ptr == NULL || ptr == key) + continue; + + value = ptr + 1; + + xattr = mkxattr(key, ptr - key, value, strlen(value)); + if (xattr == NULL) + goto fail_errno; + + urldecode(xattr->key); + base64_decode((uint8_t *)xattr->value, value); + + xattr->next = out->xattr; + out->xattr = xattr; } } diff --git a/lib/tar/urldecode.c b/lib/tar/urldecode.c new file mode 100644 index 0000000..ac03f10 --- /dev/null +++ b/lib/tar/urldecode.c @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "internal.h" + +static int xdigit(int x) +{ + if (isupper(x)) + return x - 'A' + 0x0A; + if (islower(x)) + return x - 'a' + 0x0A; + return x - '0'; +} + +void urldecode(char *str) +{ + unsigned char *out = (unsigned char *)str; + char *in = str; + int x; + + while (*in != '\0') { + x = *(in++); + + if (x == '%' && isxdigit(in[0]) && isxdigit(in[1])) { + x = xdigit(*(in++)) << 4; + x |= xdigit(*(in++)); + } + + *(out++) = x; + } + + *out = '\0'; +} -- cgit v1.2.3