summaryrefslogtreecommitdiff
path: root/lib/tar/pax_header.c
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2021-07-10 12:22:26 +0200
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2022-03-30 22:31:30 +0200
commit5e54880a2db2aeb7a6c301a69ee2bef9a09a237f (patch)
tree8eee963268f2db658dba93d075af3a568ade1b48 /lib/tar/pax_header.c
parent5df5c115298e8ed9ec977ded60e8019ea39f00b3 (diff)
Cleanup: table driven pax header parsing
Instead of having a long if-else-if chain, replace the PAX header field parsing with a table driven approach. Altough it is more code, it is hopefully more readable, maintainable, extensible and it dedupliates some of the value parsing code. The GNU.sparse parsing is left as is, because it requires maintaining state. 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.c265
1 files changed, 192 insertions, 73 deletions
diff --git a/lib/tar/pax_header.c b/lib/tar/pax_header.c
index e62ffc4..71b849b 100644
--- a/lib/tar/pax_header.c
+++ b/lib/tar/pax_header.c
@@ -8,6 +8,134 @@
#include "internal.h"
+static int pax_uid(tar_header_decoded_t *out, sqfs_u64 id)
+{
+ out->sb.st_uid = id;
+ return 0;
+}
+
+static int pax_gid(tar_header_decoded_t *out, sqfs_u64 id)
+{
+ out->sb.st_gid = id;
+ return 0;
+}
+
+static int pax_size(tar_header_decoded_t *out, sqfs_u64 size)
+{
+ out->record_size = size;
+ return 0;
+}
+
+static int pax_mtime(tar_header_decoded_t *out, sqfs_s64 mtime)
+{
+ out->mtime = mtime;
+ return 0;
+}
+
+static int pax_rsize(tar_header_decoded_t *out, sqfs_u64 size)
+{
+ out->actual_size = size;
+ return 0;
+}
+
+static int pax_path(tar_header_decoded_t *out, char *path)
+{
+ free(out->name);
+ out->name = path;
+ return 0;
+}
+
+static int pax_slink(tar_header_decoded_t *out, char *path)
+{
+ free(out->link_target);
+ out->link_target = path;
+ return 0;
+}
+
+static int pax_xattr_schily(tar_header_decoded_t *out,
+ tar_xattr_t *xattr)
+{
+ xattr->next = out->xattr;
+ out->xattr = xattr;
+ return 0;
+}
+
+static int pax_xattr_libarchive(tar_header_decoded_t *out,
+ tar_xattr_t *xattr)
+{
+ urldecode(xattr->key);
+ xattr->value_len = base64_decode(xattr->value,
+ (const char *)xattr->value,
+ xattr->value_len);
+ xattr->next = out->xattr;
+ out->xattr = xattr;
+ return 0;
+}
+
+enum {
+ PAX_TYPE_SINT = 0,
+ PAX_TYPE_UINT,
+ PAX_TYPE_STRING,
+ PAX_TYPE_PREFIXED_XATTR,
+ PAX_TYPE_IGNORE,
+};
+
+static const struct pax_handler_t {
+ const char *name;
+ int flag;
+ int type;
+ union {
+ 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 (*xattr)(tar_header_decoded_t *out, tar_xattr_t *xattr);
+ } cb;
+} pax_fields[] = {
+ { "uid", PAX_UID, PAX_TYPE_UINT, { .uint = pax_uid } },
+ { "gid", PAX_GID, PAX_TYPE_UINT, { .uint = pax_gid } },
+ { "path", PAX_NAME, PAX_TYPE_STRING, { .str = pax_path } },
+ { "size", PAX_SIZE, PAX_TYPE_UINT, { .uint = pax_size } },
+ { "linkpath", PAX_SLINK_TARGET, PAX_TYPE_STRING, { .str = pax_slink } },
+ { "mtime", PAX_MTIME, PAX_TYPE_SINT, { .sint = pax_mtime } },
+ { "GNU.sparse.name", PAX_NAME, PAX_TYPE_STRING, { .str = pax_path } },
+ { "GNU.sparse.size", PAX_SPARSE_SIZE, PAX_TYPE_UINT,
+ {.uint = pax_rsize} },
+ { "GNU.sparse.realsize", PAX_SPARSE_SIZE, PAX_TYPE_UINT,
+ {.uint = pax_rsize} },
+ { "GNU.sparse.major", PAX_SPARSE_GNU_1_X, PAX_TYPE_IGNORE,
+ { .str = NULL } },
+ { "GNU.sparse.minor", PAX_SPARSE_GNU_1_X, PAX_TYPE_IGNORE,
+ { .str = NULL }},
+ { "SCHILY.xattr", 0, PAX_TYPE_PREFIXED_XATTR,
+ { .xattr = pax_xattr_schily } },
+ { "LIBARCHIVE.xattr", 0, PAX_TYPE_PREFIXED_XATTR,
+ { .xattr = pax_xattr_libarchive } },
+};
+
+static const struct pax_handler_t *find_handler(const char *key)
+{
+ size_t i, fieldlen;
+
+ for (i = 0; i < sizeof(pax_fields) / sizeof(pax_fields[0]); ++i) {
+ if (pax_fields[i].type == PAX_TYPE_PREFIXED_XATTR) {
+ fieldlen = strlen(pax_fields[i].name);
+
+ if (strncmp(key, pax_fields[i].name, fieldlen))
+ continue;
+
+ if (key[fieldlen] != '.')
+ continue;
+
+ return pax_fields + i;
+ }
+
+ if (!strcmp(key, pax_fields[i].name))
+ return pax_fields + i;
+ }
+
+ return NULL;
+}
+
static tar_xattr_t *mkxattr(const char *key,
const char *value, size_t valuelen)
{
@@ -30,13 +158,68 @@ static tar_xattr_t *mkxattr(const char *key,
return xattr;
}
+static int apply_handler(tar_header_decoded_t *out,
+ const struct pax_handler_t *field, const char *key,
+ const char *value, size_t valuelen)
+{
+ tar_xattr_t *xattr;
+ sqfs_s64 s64val;
+ sqfs_u64 uval;
+ char *copy;
+
+ switch (field->type) {
+ case PAX_TYPE_SINT:
+ if (value[0] == '-') {
+ if (pax_read_decimal(value + 1, &uval))
+ return -1;
+ s64val = -((sqfs_s64)uval);
+ } else {
+ if (pax_read_decimal(value, &uval))
+ return -1;
+ s64val = (sqfs_s64)uval;
+ }
+ return field->cb.sint(out, s64val);
+ case PAX_TYPE_UINT:
+ if (pax_read_decimal(value, &uval))
+ return -1;
+ return field->cb.uint(out, uval);
+ case PAX_TYPE_STRING:
+ copy = strdup(value);
+ if (copy == NULL) {
+ perror("processing pax header");
+ return -1;
+ }
+ if (field->cb.str(out, copy)) {
+ free(copy);
+ return -1;
+ }
+ break;
+ case PAX_TYPE_PREFIXED_XATTR:
+ xattr = mkxattr(key + strlen(field->name) + 1,
+ value, valuelen);
+ if (xattr == NULL) {
+ perror("reading pax xattr field");
+ return -1;
+ }
+ if (field->cb.xattr(out, xattr)) {
+ free(xattr);
+ return -1;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
int read_pax_header(istream_t *fp, sqfs_u64 entsize, unsigned int *set_by_pax,
tar_header_decoded_t *out)
{
char *buffer, *line, *key, *ptr, *value, *end;
sparse_map_t *sparse_last = NULL, *sparse;
- sqfs_u64 field, offset = 0, num_bytes = 0;
- tar_xattr_t *xattr;
+ sqfs_u64 offset = 0, num_bytes = 0;
+ const struct pax_handler_t *field;
long len;
buffer = record_to_memory(fp, entsize);
@@ -72,49 +255,15 @@ int read_pax_header(istream_t *fp, sqfs_u64 entsize, unsigned int *set_by_pax,
*(ptr++) = '\0';
value = ptr;
- if (!strcmp(key, "uid")) {
- if (pax_read_decimal(value, &field))
- goto fail;
- out->sb.st_uid = field;
- *set_by_pax |= PAX_UID;
- } else if (!strcmp(key, "gid")) {
- if (pax_read_decimal(value, &field))
- goto fail;
- out->sb.st_gid = field;
- *set_by_pax |= PAX_GID;
- } else if (!strcmp(key, "path")) {
- free(out->name);
- out->name = strdup(value);
- if (out->name == NULL)
- goto fail_errno;
- *set_by_pax |= PAX_NAME;
- } else if (!strcmp(key, "size")) {
- if (pax_read_decimal(value, &out->record_size))
+ field = find_handler(key);
+
+ if (field != NULL) {
+ if (apply_handler(out, field, key, value,
+ len - (value - line) - 1)) {
goto fail;
- *set_by_pax |= PAX_SIZE;
- } else if (!strcmp(key, "linkpath")) {
- free(out->link_target);
- out->link_target = strdup(value);
- if (out->link_target == NULL)
- goto fail_errno;
- *set_by_pax |= PAX_SLINK_TARGET;
- } else if (!strcmp(key, "mtime")) {
- if (value[0] == '-') {
- if (pax_read_decimal(value + 1, &field))
- goto fail;
- out->mtime = -((sqfs_s64)field);
- } else {
- if (pax_read_decimal(value, &field))
- goto fail;
- out->mtime = field;
}
- *set_by_pax |= PAX_MTIME;
- } else if (!strcmp(key, "GNU.sparse.name")) {
- free(out->name);
- out->name = strdup(value);
- if (out->name == NULL)
- goto fail_errno;
- *set_by_pax |= PAX_NAME;
+
+ *set_by_pax |= field->flag;
} else if (!strcmp(key, "GNU.sparse.map")) {
free_sparse_list(out->sparse);
sparse_last = NULL;
@@ -122,17 +271,6 @@ int read_pax_header(istream_t *fp, sqfs_u64 entsize, unsigned int *set_by_pax,
out->sparse = read_sparse_map(value);
if (out->sparse == NULL)
goto fail;
- } else if (!strcmp(key, "GNU.sparse.size")) {
- if (pax_read_decimal(value, &out->actual_size))
- goto fail;
- *set_by_pax |= PAX_SPARSE_SIZE;
- } else if (!strcmp(key, "GNU.sparse.realsize")) {
- if (pax_read_decimal(value, &out->actual_size))
- goto fail;
- *set_by_pax |= PAX_SPARSE_SIZE;
- } else if (!strcmp(key, "GNU.sparse.major") ||
- !strcmp(key, "GNU.sparse.minor")) {
- *set_by_pax |= PAX_SPARSE_GNU_1_X;
} else if (!strcmp(key, "GNU.sparse.offset")) {
if (pax_read_decimal(value, &offset))
goto fail;
@@ -151,25 +289,6 @@ int read_pax_header(istream_t *fp, sqfs_u64 entsize, unsigned int *set_by_pax,
sparse_last->next = sparse;
sparse_last = sparse;
}
- } else if (!strncmp(key, "SCHILY.xattr.", 13)) {
- xattr = mkxattr(key + 13, value,
- len - (value - line) - 1);
- if (xattr == NULL)
- goto fail_errno;
-
- xattr->next = out->xattr;
- out->xattr = xattr;
- } else if (!strncmp(key, "LIBARCHIVE.xattr.", 17)) {
- xattr = mkxattr(key + 17, value, strlen(value));
- if (xattr == NULL)
- goto fail_errno;
-
- urldecode(xattr->key);
- xattr->value_len = base64_decode(xattr->value, value,
- xattr->value_len);
-
- xattr->next = out->xattr;
- out->xattr = xattr;
}
}