summaryrefslogtreecommitdiff
path: root/lib/tar/pax_header.c
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2022-06-29 18:21:58 +0200
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2022-07-08 19:17:35 +0200
commite6a19ba1a05f77f051187a6b1a828ee6d39ce052 (patch)
treec8a1566b10883edbe4a8bc9a724461f4003aaa10 /lib/tar/pax_header.c
parent359d71e90050a8b83f7bc7d2ecd4ff29c477cc22 (diff)
Cleanup: split libtar header, move to sub directory
Some of the on-disk format internals are moved to a separate header and some of the stuff from internal.h is moved to that format header. C++ guards are added in addtion. Everything PAX related is moved to pax_header.c, some internal functions are marked as static. Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'lib/tar/pax_header.c')
-rw-r--r--lib/tar/pax_header.c161
1 files changed, 154 insertions, 7 deletions
diff --git a/lib/tar/pax_header.c b/lib/tar/pax_header.c
index d24def0..8a1b077 100644
--- a/lib/tar/pax_header.c
+++ b/lib/tar/pax_header.c
@@ -7,6 +7,105 @@
#include "config.h"
#include "internal.h"
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+
+static sqfs_u8 base64_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;
+}
+
+static int xdigit(int x)
+{
+ if (isupper(x))
+ return x - 'A' + 0x0A;
+ if (islower(x))
+ return x - 'a' + 0x0A;
+ return x - '0';
+}
+
+static size_t base64_decode(sqfs_u8 *out, const char *in, size_t len)
+{
+ sqfs_u8 *start = out;
+
+ while (len > 0) {
+ unsigned int diff = 0, value = 0;
+
+ while (diff < 4 && len > 0) {
+ if (*in == '=' || *in == '_' || *in == '\0') {
+ len = 0;
+ } else {
+ value = (value << 6) | base64_convert(*(in++));
+ --len;
+ ++diff;
+ }
+ }
+
+ if (diff < 2)
+ break;
+
+ value <<= 6 * (4 - diff);
+
+ switch (diff) {
+ case 4: out[2] = value & 0xff; /* fall-through */
+ case 3: out[1] = (value >> 8) & 0xff; /* fall-through */
+ default: out[0] = (value >> 16) & 0xff;
+ }
+
+ out += (diff * 3) / 4;
+ }
+
+ *out = '\0';
+ return out - start;
+}
+
+static int pax_read_decimal(const char *str, sqfs_u64 *out)
+{
+ sqfs_u64 result = 0;
+
+ while (*str >= '0' && *str <= '9') {
+ if (result > 0xFFFFFFFFFFFFFFFFUL / 10) {
+ fputs("numeric overflow parsing pax header\n", stderr);
+ return -1;
+ }
+
+ result = (result * 10) + (*(str++) - '0');
+ }
+
+ *out = result;
+ return 0;
+}
+
+static 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';
+}
static int pax_uid(tar_header_decoded_t *out, sqfs_u64 id)
{
@@ -52,6 +151,55 @@ static int pax_slink(tar_header_decoded_t *out, char *path)
return 0;
}
+static int pax_sparse_map(tar_header_decoded_t *out, const char *line)
+{
+ sparse_map_t *last = NULL, *list = NULL, *ent = NULL;
+
+ free_sparse_list(out->sparse);
+ out->sparse = NULL;
+
+ do {
+ ent = calloc(1, sizeof(*ent));
+ if (ent == NULL)
+ goto fail_errno;
+
+ if (pax_read_decimal(line, &ent->offset))
+ goto fail_format;
+
+ while (isdigit(*line))
+ ++line;
+
+ if (*(line++) != ',')
+ goto fail_format;
+
+ if (pax_read_decimal(line, &ent->count))
+ goto fail_format;
+
+ while (isdigit(*line))
+ ++line;
+
+ if (last == NULL) {
+ list = last = ent;
+ } else {
+ last->next = ent;
+ last = ent;
+ }
+ } while (*(line++) == ',');
+
+ out->sparse = list;
+ return 0;
+fail_errno:
+ perror("parsing GNU pax sparse file record");
+ goto fail;
+fail_format:
+ fputs("malformed GNU pax sparse file record\n", stderr);
+ goto fail;
+fail:
+ free_sparse_list(list);
+ free(ent);
+ return -1;
+}
+
static int pax_xattr_schily(tar_header_decoded_t *out,
tar_xattr_t *xattr)
{
@@ -76,6 +224,7 @@ enum {
PAX_TYPE_SINT = 0,
PAX_TYPE_UINT,
PAX_TYPE_STRING,
+ PAX_TYPE_CONST_STRING,
PAX_TYPE_PREFIXED_XATTR,
PAX_TYPE_IGNORE,
};
@@ -88,6 +237,7 @@ static const struct pax_handler_t {
int (*sint)(tar_header_decoded_t *out, sqfs_s64 sval);
int (*uint)(tar_header_decoded_t *out, sqfs_u64 uval);
int (*str)(tar_header_decoded_t *out, char *str);
+ int (*cstr)(tar_header_decoded_t *out, const char *str);
int (*xattr)(tar_header_decoded_t *out, tar_xattr_t *xattr);
} cb;
} pax_fields[] = {
@@ -110,6 +260,8 @@ static const struct pax_handler_t {
{ .xattr = pax_xattr_schily } },
{ "LIBARCHIVE.xattr", 0, PAX_TYPE_PREFIXED_XATTR,
{ .xattr = pax_xattr_libarchive } },
+ { "GNU.sparse.map", 0, PAX_TYPE_CONST_STRING,
+ { .cstr = pax_sparse_map } },
};
static const struct pax_handler_t *find_handler(const char *key)
@@ -183,6 +335,8 @@ static int apply_handler(tar_header_decoded_t *out,
if (pax_read_decimal(value, &uval))
return -1;
return field->cb.uint(out, uval);
+ case PAX_TYPE_CONST_STRING:
+ return field->cb.cstr(out, value);
case PAX_TYPE_STRING:
copy = strdup(value);
if (copy == NULL) {
@@ -264,13 +418,6 @@ int read_pax_header(istream_t *fp, sqfs_u64 entsize, unsigned int *set_by_pax,
}
*set_by_pax |= field->flag;
- } else if (!strcmp(key, "GNU.sparse.map")) {
- free_sparse_list(out->sparse);
- sparse_last = NULL;
-
- out->sparse = read_sparse_map(value);
- if (out->sparse == NULL)
- goto fail;
} else if (!strcmp(key, "GNU.sparse.offset")) {
if (pax_read_decimal(value, &offset))
goto fail;