summaryrefslogtreecommitdiff
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
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>
-rw-r--r--lib/tar/Makemodule.am1
-rw-r--r--lib/tar/internal.h4
-rw-r--r--lib/tar/pax_header.c7
-rw-r--r--lib/tar/read_header.c7
-rw-r--r--lib/tar/read_sparse_map_new.c96
-rw-r--r--tests/Makemodule.am2
-rw-r--r--tests/tar/sqfs.sha5122
-rw-r--r--tests/tar_sparse_gnu2.c4
8 files changed, 119 insertions, 4 deletions
diff --git a/lib/tar/Makemodule.am b/lib/tar/Makemodule.am
index a47b07a..fe18895 100644
--- a/lib/tar/Makemodule.am
+++ b/lib/tar/Makemodule.am
@@ -4,6 +4,7 @@ libtar_a_SOURCES += lib/tar/read_sparse_map.c lib/tar/read_sparse_map_old.c
libtar_a_SOURCES += lib/tar/base64.c lib/tar/urldecode.c lib/tar/internal.h
libtar_a_SOURCES += lib/tar/padd_file.c lib/tar/read_retry.c include/tar.h
libtar_a_SOURCES += lib/tar/write_retry.c lib/tar/pax_header.c
+libtar_a_SOURCES += lib/tar/read_sparse_map_new.c
libtar_a_CFLAGS = $(AM_CFLAGS)
libtar_a_CPPFLAGS = $(AM_CPPFLAGS)
diff --git a/lib/tar/internal.h b/lib/tar/internal.h
index e79596b..65e5d45 100644
--- a/lib/tar/internal.h
+++ b/lib/tar/internal.h
@@ -27,6 +27,8 @@ enum {
PAX_SLINK_TARGET = 0x040,
PAX_MTIME = 0x100,
PAX_SPARSE_SIZE = 0x400,
+
+ PAX_SPARSE_GNU_1_X = 0x800,
};
enum {
@@ -58,6 +60,8 @@ sparse_map_t *read_sparse_map(const char *line);
sparse_map_t *read_gnu_old_sparse(FILE *fp, tar_header_t *hdr);
+sparse_map_t *read_gnu_new_sparse(FILE *fp, tar_header_decoded_t *out);
+
void free_sparse_list(sparse_map_t *sparse);
size_t base64_decode(sqfs_u8 *out, const char *in, size_t len);
diff --git a/lib/tar/pax_header.c b/lib/tar/pax_header.c
index 4eeabf5..448976d 100644
--- a/lib/tar/pax_header.c
+++ b/lib/tar/pax_header.c
@@ -110,6 +110,13 @@ int read_pax_header(FILE *fp, sqfs_u64 entsize, unsigned int *set_by_pax,
if (pax_read_decimal(ptr + 16, &out->actual_size))
goto fail;
*set_by_pax |= PAX_SPARSE_SIZE;
+ } else if (!strncmp(ptr, "GNU.sparse.realsize=", 20)) {
+ if (pax_read_decimal(ptr + 20, &out->actual_size))
+ goto fail;
+ *set_by_pax |= PAX_SPARSE_SIZE;
+ } else if (!strncmp(ptr, "GNU.sparse.major=", 17) ||
+ !strncmp(ptr, "GNU.sparse.minor=", 17)) {
+ *set_by_pax |= PAX_SPARSE_GNU_1_X;
} else if (!strncmp(ptr, "GNU.sparse.offset=", 18)) {
if (pax_read_decimal(ptr + 18, &offset))
goto fail;
diff --git a/lib/tar/read_header.c b/lib/tar/read_header.c
index e8e49c2..14752ea 100644
--- a/lib/tar/read_header.c
+++ b/lib/tar/read_header.c
@@ -255,6 +255,13 @@ int read_header(FILE *fp, tar_header_decoded_t *out)
if (decode_header(&hdr, set_by_pax, out, version))
goto fail;
+ if (set_by_pax & PAX_SPARSE_GNU_1_X) {
+ free_sparse_list(out->sparse);
+ out->sparse = read_gnu_new_sparse(fp, out);
+ if (out->sparse == NULL)
+ goto fail;
+ }
+
if (out->sparse != NULL) {
out->sb.st_size = out->actual_size;
} else {
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;
+}
diff --git a/tests/Makemodule.am b/tests/Makemodule.am
index 9694619..2aea363 100644
--- a/tests/Makemodule.am
+++ b/tests/Makemodule.am
@@ -87,7 +87,7 @@ test_tar_sparse_gnu1_LDADD = libtar.a libcompat.a
test_tar_sparse_gnu1_CPPFLAGS = $(AM_CPPFLAGS)
test_tar_sparse_gnu1_CPPFLAGS += -DTESTPATH=$(top_srcdir)/tests/tar
-test_tar_sparse_gnu2_SOURCES = tests/tar_sparse_gnu1.c tests/test.h
+test_tar_sparse_gnu2_SOURCES = tests/tar_sparse_gnu2.c tests/test.h
test_tar_sparse_gnu2_LDADD = libtar.a libcompat.a
test_tar_sparse_gnu2_CPPFLAGS = $(AM_CPPFLAGS)
test_tar_sparse_gnu2_CPPFLAGS += -DTESTPATH=$(top_srcdir)/tests/tar
diff --git a/tests/tar/sqfs.sha512 b/tests/tar/sqfs.sha512
index bcdd245..90dbf7f 100644
--- a/tests/tar/sqfs.sha512
+++ b/tests/tar/sqfs.sha512
@@ -5,7 +5,7 @@
5b032c35f80b73f21aef8e9f558c16605676e3e621e927177c4ab0a60ba7441a7501dd21a1c37d43d8432bfa694b6afcbe834a277e2fd8f23315e16bc3cdd86a tests/tar/sparse-files/gnu.sqfs
5b032c35f80b73f21aef8e9f558c16605676e3e621e927177c4ab0a60ba7441a7501dd21a1c37d43d8432bfa694b6afcbe834a277e2fd8f23315e16bc3cdd86a tests/tar/sparse-files/pax-gnu0-1.sqfs
5b032c35f80b73f21aef8e9f558c16605676e3e621e927177c4ab0a60ba7441a7501dd21a1c37d43d8432bfa694b6afcbe834a277e2fd8f23315e16bc3cdd86a tests/tar/sparse-files/pax-gnu0-0.sqfs
-dbeb3e3e94a9f6e778cf626492776eb77d9482d2a27b781778987225605be8ab59aa08fb85912afc9fe7bd5ac2aed94f371f930de4685955d40e1ad70aa4380c tests/tar/sparse-files/pax-gnu1-0.sqfs
+5b032c35f80b73f21aef8e9f558c16605676e3e621e927177c4ab0a60ba7441a7501dd21a1c37d43d8432bfa694b6afcbe834a277e2fd8f23315e16bc3cdd86a tests/tar/sparse-files/pax-gnu1-0.sqfs
1b9525453fb10f266cd7f52300fa2ff586a9b5a1c141da46f72c6370485d4dc7e306f2e778108644cdea6f06ee95a2972325f950ef5fce98bf439db1869c692a tests/tar/large-mtime/12-digit.sqfs
1b9525453fb10f266cd7f52300fa2ff586a9b5a1c141da46f72c6370485d4dc7e306f2e778108644cdea6f06ee95a2972325f950ef5fce98bf439db1869c692a tests/tar/large-mtime/gnu.sqfs
1b9525453fb10f266cd7f52300fa2ff586a9b5a1c141da46f72c6370485d4dc7e306f2e778108644cdea6f06ee95a2972325f950ef5fce98bf439db1869c692a tests/tar/large-mtime/pax.sqfs
diff --git a/tests/tar_sparse_gnu2.c b/tests/tar_sparse_gnu2.c
index 84a18ec..2a54640 100644
--- a/tests/tar_sparse_gnu2.c
+++ b/tests/tar_sparse_gnu2.c
@@ -44,7 +44,7 @@ int main(void)
TEST_EQUAL_UI(sparse->count, 4096);
sparse = sparse->next;
- TSET_NOT_NULL(sparse);
+ TEST_NOT_NULL(sparse);
TEST_EQUAL_UI(sparse->offset, 524288);
TEST_EQUAL_UI(sparse->count, 4096);
@@ -74,7 +74,7 @@ int main(void)
TEST_EQUAL_UI(sparse->count, 4096);
sparse = sparse->next;
- TEST_NOT_NULL(sparse != NULL);
+ TEST_NOT_NULL(sparse);
TEST_EQUAL_UI(sparse->offset, 2097152);
TEST_EQUAL_UI(sparse->count, 0);