aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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;
}
}