summaryrefslogtreecommitdiff
path: root/lib/tar
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-06-29 02:26:11 +0200
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-06-29 02:26:57 +0200
commit8c449c503f4c37f72089a0dc50e00631a22ed12a (patch)
treee280aabfca0b5c12cafde614883aa05a17fa719e /lib/tar
parentd2f27dc653767f36885916d9ecd7b059673d26d8 (diff)
Add support for reading old style GNU sparse tar file format
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'lib/tar')
-rw-r--r--lib/tar/read_header.c116
1 files changed, 116 insertions, 0 deletions
diff --git a/lib/tar/read_header.c b/lib/tar/read_header.c
index 76634e1..44a38f9 100644
--- a/lib/tar/read_header.c
+++ b/lib/tar/read_header.c
@@ -390,6 +390,7 @@ static int decode_header(const tar_header_t *hdr, unsigned int set_by_pax,
switch (hdr->typeflag) {
case '\0':
case TAR_TYPE_FILE:
+ case TAR_TYPE_GNU_SPARSE:
out->sb.st_mode |= S_IFREG;
break;
case TAR_TYPE_LINK:
@@ -450,6 +451,104 @@ fail:
return NULL;
}
+static void free_sparse_list(tar_sparse_data_t *sparse)
+{
+ tar_sparse_data_t *old;
+
+ while (sparse != NULL) {
+ old = sparse;
+ sparse = sparse->next;
+ free(old);
+ }
+}
+
+static tar_sparse_data_t *read_gnu_old_sparse(int fd, tar_header_t *hdr)
+{
+ tar_sparse_data_t *list = NULL, *end = NULL, *node;
+ gnu_sparse_t sph;
+ uint64_t off, sz;
+ ssize_t ret;
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ if (!isdigit(hdr->tail.gnu.sparse[i].offset[0]))
+ break;
+ if (!isdigit(hdr->tail.gnu.sparse[i].numbytes[0]))
+ break;
+
+ if (read_octal(hdr->tail.gnu.sparse[i].offset,
+ sizeof(hdr->tail.gnu.sparse[i].offset), &off))
+ goto fail;
+ if (read_octal(hdr->tail.gnu.sparse[i].numbytes,
+ sizeof(hdr->tail.gnu.sparse[i].numbytes), &sz))
+ goto fail;
+
+ node = calloc(1, sizeof(*node));
+ if (node == NULL)
+ goto fail_errno;
+
+ node->offset = off;
+ node->count = sz;
+
+ if (list == NULL) {
+ list = end = node;
+ } else {
+ end->next = node;
+ end = node;
+ }
+ }
+
+ if (hdr->tail.gnu.isextended == 0)
+ return list;
+
+ do {
+ ret = read_retry(fd, &sph, sizeof(sph));
+ if (ret < 0)
+ goto fail_errno;
+ if ((size_t)ret < sizeof(sph))
+ goto fail_eof;
+
+ for (i = 0; i < 21; ++i) {
+ if (!isdigit(sph.sparse[i].offset[0]))
+ break;
+ if (!isdigit(sph.sparse[i].numbytes[0]))
+ break;
+
+ if (read_octal(sph.sparse[i].offset,
+ sizeof(sph.sparse[i].offset), &off))
+ goto fail;
+ if (read_octal(sph.sparse[i].numbytes,
+ sizeof(sph.sparse[i].numbytes), &sz))
+ goto fail;
+
+ node = calloc(1, sizeof(*node));
+ if (node == NULL)
+ goto fail_errno;
+
+ node->offset = off;
+ node->count = sz;
+
+ if (list == NULL) {
+ list = end = node;
+ } else {
+ end->next = node;
+ end = node;
+ }
+ }
+ } while (sph.isextended != 0);
+
+ return list;
+fail_eof:
+ fputs("parsing GNU sparse header: unexpected end of file", stderr);
+ goto fail;
+fail_errno:
+ perror("parsing GNU sparse header");
+ goto fail;
+fail:
+ free_sparse_list(list);
+ return NULL;
+}
+
int read_header(int fd, tar_header_decoded_t *out)
{
unsigned int set_by_pax = 0;
@@ -513,6 +612,22 @@ int read_header(int fd, tar_header_decoded_t *out)
if (read_pax_header(fd, pax_size, &set_by_pax, out))
goto fail;
continue;
+ case TAR_TYPE_GNU_SPARSE:
+ if (!(set_by_pax & PAX_SIZE)) {
+ if (read_number(hdr.tail.gnu.realsize,
+ sizeof(hdr.tail.gnu.realsize),
+ &pax_size))
+ goto fail;
+
+ out->sb.st_size = pax_size;
+ set_by_pax |= PAX_SIZE;
+ }
+
+ free_sparse_list(out->sparse);
+ out->sparse = read_gnu_old_sparse(fd, &hdr);
+ if (out->sparse == NULL)
+ goto fail;
+ break;
}
break;
}
@@ -543,6 +658,7 @@ fail:
void clear_header(tar_header_decoded_t *hdr)
{
+ free_sparse_list(hdr->sparse);
free(hdr->name);
free(hdr->link_target);
memset(hdr, 0, sizeof(*hdr));