diff options
Diffstat (limited to 'lib/tar')
-rw-r--r-- | lib/tar/Makemodule.am | 1 | ||||
-rw-r--r-- | lib/tar/internal.h | 4 | ||||
-rw-r--r-- | lib/tar/pax_header.c | 7 | ||||
-rw-r--r-- | lib/tar/read_header.c | 7 | ||||
-rw-r--r-- | lib/tar/read_sparse_map_new.c | 96 |
5 files changed, 115 insertions, 0 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; +} |