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