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; +}  | 
