summaryrefslogtreecommitdiff
path: root/lib/tar/read_sparse_map_new.c
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2020-09-02 11:19:11 +0200
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2020-09-02 11:19:11 +0200
commit84ff0984bb5b471d0b4cbc0b0fca156c29273bf5 (patch)
treee6c4d1e394bfef5924c8b59fe8fad8691c9738bc /lib/tar/read_sparse_map_new.c
parentba5e71a40af4bcc0f2427dc0b4575802da09af56 (diff)
Fix nonexistant gnu tar sparse format 1.0 support
Contrary to previous claims, support for the GNU tar sparse format 1.0 was missing entirely (the newest of their 3 different sparse mapping formats). This oversight wasn't caught, because the unit test was compiling the wrong source file and tar2sqfs had no problem processing the test file because it is still a valid POSIX-ish tar archive (but the sparse part was missing and the mapping embedded in the file). Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'lib/tar/read_sparse_map_new.c')
-rw-r--r--lib/tar/read_sparse_map_new.c96
1 files changed, 96 insertions, 0 deletions
diff --git a/lib/tar/read_sparse_map_new.c b/lib/tar/read_sparse_map_new.c
new file mode 100644
index 0000000..246f8a5
--- /dev/null
+++ b/lib/tar/read_sparse_map_new.c
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * read_sparse_map_new.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "internal.h"
+
+static int decode(const char *str, size_t len, size_t *out)
+{
+ size_t count = 0;
+
+ *out = 0;
+
+ while (count < len && isdigit(*str)) {
+ if (*out > 0xFFFFFFFFFFFFFFFFUL / 10)
+ return -1;
+ *out = (*out) * 10 + (*(str++) - '0');
+ ++count;
+ }
+
+ if (count == 0 || count == len)
+ return 0;
+
+ return (*str == '\n') ? ((int)count + 1) : -1;
+}
+
+sparse_map_t *read_gnu_new_sparse(FILE *fp, tar_header_decoded_t *out)
+{
+ sparse_map_t *last = NULL, *list = NULL, *ent = NULL;
+ size_t i, count, value;
+ char buffer[1024];
+ int diff, ret;
+
+ if (read_retry("reading GNU sparse map", fp, buffer, 512))
+ return NULL;
+
+ diff = decode(buffer, 512, &count);
+ if (diff <= 0)
+ goto fail_format;
+
+ out->record_size -= 512;
+
+ for (i = 0; i < (count * 2); ++i) {
+ ret = decode(buffer + diff, 512 - diff, &value);
+ if (ret < 0)
+ goto fail_format;
+
+ if (ret > 0) {
+ diff += ret;
+ } else {
+ if (read_retry("reading GNU sparse map", fp,
+ buffer + 512, 512)) {
+ return NULL;
+ }
+
+ ret = decode(buffer + diff, 1024 - diff, &value);
+ if (ret <= 0)
+ goto fail_format;
+
+ memcpy(buffer, buffer + 512, 512);
+ diff = diff + ret - 512;
+ out->record_size -= 512;
+ }
+
+ if ((i & 0x01) == 0) {
+ ent = calloc(1, sizeof(*ent));
+ if (ent == NULL)
+ goto fail_errno;
+
+ if (list == NULL) {
+ list = last = ent;
+ } else {
+ last->next = ent;
+ last = ent;
+ }
+
+ ent->offset = value;
+ } else {
+ ent->count = value;
+ }
+ }
+
+ return list;
+fail_errno:
+ perror("parsing GNU 1.0 style sparse file record");
+ goto fail;
+fail_format:
+ fputs("Malformed GNU 1.0 style sparse file map.\n", stderr);
+ goto fail;
+fail:
+ free_sparse_list(list);
+ return NULL;
+}