aboutsummaryrefslogtreecommitdiff
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>2021-07-21 10:19:51 +0200
commit5781bc71b5fb5410d8fa5017c7832a34a00f1cc1 (patch)
tree4786968ef4f29bf51c3ffdc61c0b5c8be99092d5
parent25d1702d01c38481a09b33ccb3f843a69d2727c6 (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>
-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 73c853b..2ef0b09 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)
{
@@ -26,13 +154,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);
@@ -68,49 +251,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;
@@ -118,17 +267,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;
@@ -147,25 +285,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;
}
}