summaryrefslogtreecommitdiff
path: root/lib/tar/read_header.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tar/read_header.c')
-rw-r--r--lib/tar/read_header.c366
1 files changed, 38 insertions, 328 deletions
diff --git a/lib/tar/read_header.c b/lib/tar/read_header.c
index de6f0cd..067b1df 100644
--- a/lib/tar/read_header.c
+++ b/lib/tar/read_header.c
@@ -1,104 +1,5 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
-#include "util.h"
-#include "tar.h"
-
-#include <sys/sysmacros.h>
-#include <string.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <stdio.h>
-
-enum {
- PAX_SIZE = 0x001,
- PAX_UID = 0x002,
- PAX_GID = 0x004,
- PAX_DEV_MAJ = 0x008,
- PAX_DEV_MIN = 0x010,
- PAX_NAME = 0x020,
- PAX_SLINK_TARGET = 0x040,
- PAX_ATIME = 0x080,
- PAX_MTIME = 0x100,
- PAX_CTIME = 0x200,
- PAX_SPARSE_SIZE = 0x400,
-};
-
-static void free_sparse_list(sparse_map_t *sparse)
-{
- sparse_map_t *old;
-
- while (sparse != NULL) {
- old = sparse;
- sparse = sparse->next;
- free(old);
- }
-}
-
-static int read_octal(const char *str, int digits, uint64_t *out)
-{
- uint64_t result = 0;
-
- while (digits > 0 && isspace(*str)) {
- ++str;
- --digits;
- }
-
- while (digits > 0 && *str >= '0' && *str <= '7') {
- if (result > 0x1FFFFFFFFFFFFFFFUL) {
- fputs("numeric overflow parsing tar header\n", stderr);
- return -1;
- }
-
- result = (result << 3) | (*(str++) - '0');
- --digits;
- }
-
- *out = result;
- return 0;
-}
-
-static int read_binary(const char *str, int digits, uint64_t *out)
-{
- uint64_t x, ov, result;
- bool first = true;
-
- while (digits > 0) {
- x = *((const unsigned char *)str++);
- --digits;
-
- if (first) {
- first = false;
- if (x == 0xFF) {
- result = 0xFFFFFFFFFFFFFFFFUL;
- } else {
- x &= 0x7F;
- result = 0;
- if (digits > 7 && x != 0)
- goto fail_ov;
- }
- }
-
- ov = (result >> 56) & 0xFF;
-
- if (ov != 0 && ov != 0xFF)
- goto fail_ov;
-
- result = (result << 8) | x;
- }
-
- *out = result;
- return 0;
-fail_ov:
- fputs("numeric overflow parsing tar header\n", stderr);
- return -1;
-}
-
-static int read_number(const char *str, int digits, uint64_t *out)
-{
- if (*((unsigned char *)str) & 0x80)
- return read_binary(str, digits, out);
-
- return read_octal(str, digits, out);
-}
+#include "internal.h"
static bool is_zero_block(const tar_header_t *hdr)
{
@@ -107,26 +8,7 @@ static bool is_zero_block(const tar_header_t *hdr)
return ptr[0] == '\0' && memcmp(ptr, ptr + 1, sizeof(*hdr) - 1) == 0;
}
-static bool is_checksum_valid(const tar_header_t *hdr)
-{
- unsigned int chksum = 0;
- tar_header_t copy;
- uint64_t ref;
- size_t i;
-
- if (read_octal(hdr->chksum, sizeof(hdr->chksum), &ref))
- return false;
-
- memcpy(&copy, hdr, sizeof(*hdr));
- memset(copy.chksum, ' ', sizeof(copy.chksum));
-
- for (i = 0; i < sizeof(copy); ++i)
- chksum += ((unsigned char *)&copy)[i];
-
- return chksum == ref;
-}
-
-static E_TAR_VERSION check_version(const tar_header_t *hdr)
+static int check_version(const tar_header_t *hdr)
{
char buffer[sizeof(hdr->magic) + sizeof(hdr->version)];
@@ -146,65 +28,35 @@ static E_TAR_VERSION check_version(const tar_header_t *hdr)
return ETV_UNKNOWN;
}
-static int pax_read_decimal(const char *str, uint64_t *out)
-{
- uint64_t result = 0;
-
- while (*str >= '0' && *str <= '9') {
- if (result > 0xFFFFFFFFFFFFFFFFUL / 10) {
- fputs("numeric overflow parsing pax header\n", stderr);
- return -1;
- }
-
- result = (result * 10) + (*(str++) - '0');
- }
-
- *out = result;
- return 0;
-}
-
-static sparse_map_t *read_sparse_map(const char *line)
+static char *record_to_memory(int fd, uint64_t size)
{
- sparse_map_t *last = NULL, *list = NULL, *ent = NULL;
-
- do {
- ent = calloc(1, sizeof(*ent));
- if (ent == NULL)
- goto fail_errno;
-
- if (pax_read_decimal(line, &ent->offset))
- goto fail_format;
-
- while (isdigit(*line))
- ++line;
-
- if (*(line++) != ',')
- goto fail_format;
+ char *buffer = malloc(size + 1);
+ ssize_t ret;
- if (pax_read_decimal(line, &ent->count))
- goto fail_format;
+ if (buffer == NULL)
+ goto fail_errno;
- while (isdigit(*line))
- ++line;
+ ret = read_retry(fd, buffer, size);
+ if (ret == 0)
+ goto fail_eof;
+ if (ret < 0)
+ goto fail_errno;
+ if ((uint64_t)ret < size)
+ goto fail_eof;
- if (last == NULL) {
- list = last = ent;
- } else {
- last->next = ent;
- last = ent;
- }
- } while (*(line++) == ',');
+ if (skip_padding(fd, size))
+ goto fail;
- return list;
+ buffer[size] = '\0';
+ return buffer;
fail_errno:
- perror("parsing GNU pax sparse file record");
+ perror("reading tar record");
goto fail;
-fail_format:
- fputs("malformed GNU pax sparse file record\n", stderr);
+fail_eof:
+ fputs("reading tar record: unexpected end of file\n", stderr);
goto fail;
fail:
- free_sparse_list(list);
- free(ent);
+ free(buffer);
return NULL;
}
@@ -214,23 +66,11 @@ static int read_pax_header(int fd, uint64_t entsize, unsigned int *set_by_pax,
sparse_map_t *sparse_last = NULL, *sparse;
uint64_t field, offset = 0, num_bytes = 0;
char *buffer, *line;
- ssize_t ret;
uint64_t i;
- buffer = malloc(entsize + 1);
+ buffer = record_to_memory(fd, entsize);
if (buffer == NULL)
- goto fail_errno;
-
- ret = read_retry(fd, buffer, entsize);
- if (ret < 0)
- goto fail_errno;
- if ((size_t)ret < entsize)
- goto fail_eof;
-
- if (skip_padding(fd, entsize))
- goto fail;
-
- buffer[entsize] = '\0';
+ return -1;
for (i = 0; i < entsize; ++i) {
while (i < entsize && isspace(buffer[i]))
@@ -251,12 +91,12 @@ static int read_pax_header(int fd, uint64_t entsize, unsigned int *set_by_pax,
if (!strncmp(line, "uid=", 4)) {
if (pax_read_decimal(line + 4, &field))
- return -1;
+ goto fail;
out->sb.st_uid = field;
*set_by_pax |= PAX_UID;
} else if (!strncmp(line, "gid=", 4)) {
if (pax_read_decimal(line + 4, &field))
- return -1;
+ goto fail;
out->sb.st_gid = field;
*set_by_pax |= PAX_GID;
} else if (!strncmp(line, "path=", 5)) {
@@ -267,7 +107,7 @@ static int read_pax_header(int fd, uint64_t entsize, unsigned int *set_by_pax,
*set_by_pax |= PAX_NAME;
} else if (!strncmp(line, "size=", 5)) {
if (pax_read_decimal(line + 5, &out->record_size))
- return -1;
+ goto fail;
*set_by_pax |= PAX_SIZE;
} else if (!strncmp(line, "linkpath=", 9)) {
free(out->link_target);
@@ -278,33 +118,33 @@ static int read_pax_header(int fd, uint64_t entsize, unsigned int *set_by_pax,
} else if (!strncmp(line, "atime=", 6)) {
if (line[6] == '-') {
if (pax_read_decimal(line + 7, &field))
- return -1;
+ goto fail;
out->sb.st_atime = -((int64_t)field);
} else {
if (pax_read_decimal(line + 6, &field))
- return -1;
+ goto fail;
out->sb.st_atime = field;
}
*set_by_pax |= PAX_ATIME;
} else if (!strncmp(line, "mtime=", 6)) {
if (line[6] == '-') {
if (pax_read_decimal(line + 7, &field))
- return -1;
+ goto fail;
out->sb.st_mtime = -((int64_t)field);
} else {
if (pax_read_decimal(line + 6, &field))
- return -1;
+ goto fail;
out->sb.st_mtime = field;
}
*set_by_pax |= PAX_MTIME;
} else if (!strncmp(line, "ctime=", 6)) {
if (line[6] == '-') {
if (pax_read_decimal(line + 7, &field))
- return -1;
+ goto fail;
out->sb.st_ctime = -((int64_t)field);
} else {
if (pax_read_decimal(line + 6, &field))
- return -1;
+ goto fail;
out->sb.st_ctime = field;
}
*set_by_pax |= PAX_CTIME;
@@ -323,14 +163,14 @@ static int read_pax_header(int fd, uint64_t entsize, unsigned int *set_by_pax,
goto fail;
} else if (!strncmp(line, "GNU.sparse.size=", 16)) {
if (pax_read_decimal(line + 16, &out->actual_size))
- return -1;
+ goto fail;
*set_by_pax |= PAX_SPARSE_SIZE;
} else if (!strncmp(line, "GNU.sparse.offset=", 18)) {
if (pax_read_decimal(line + 18, &offset))
- return -1;
+ goto fail;
} else if (!strncmp(line, "GNU.sparse.numbytes=", 20)) {
if (pax_read_decimal(line + 20, &num_bytes))
- return -1;
+ goto fail;
sparse = calloc(1, sizeof(*sparse));
if (sparse == NULL)
goto fail_errno;
@@ -351,16 +191,13 @@ static int read_pax_header(int fd, uint64_t entsize, unsigned int *set_by_pax,
fail_errno:
perror("reading pax header");
goto fail;
-fail_eof:
- fputs("reading pax header: unexpected end of file\n", stderr);
- goto fail;
fail:
free(buffer);
return -1;
}
static int decode_header(const tar_header_t *hdr, unsigned int set_by_pax,
- tar_header_decoded_t *out, E_TAR_VERSION version)
+ tar_header_decoded_t *out, int version)
{
uint64_t field;
size_t count;
@@ -520,132 +357,13 @@ static int decode_header(const tar_header_t *hdr, unsigned int set_by_pax,
return 0;
}
-static char *record_to_memory(int fd, uint64_t size)
-{
- char *buffer = malloc(size + 1);
- ssize_t ret;
-
- if (buffer == NULL)
- goto fail_errno;
-
- ret = read_retry(fd, buffer, size);
- if (ret == 0)
- goto fail_eof;
- if (ret < 0)
- goto fail_errno;
- if ((uint64_t)ret < size)
- goto fail_eof;
-
- if (skip_padding(fd, size))
- goto fail;
-
- return buffer;
-fail_errno:
- perror("reading tar record");
- goto fail;
-fail_eof:
- fputs("reading tar record: unexpected end of file\n", stderr);
- goto fail;
-fail:
- free(buffer);
- return NULL;
-}
-
-static sparse_map_t *read_gnu_old_sparse(int fd, tar_header_t *hdr)
-{
- sparse_map_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;
bool prev_was_zero = false;
- E_TAR_VERSION version;
uint64_t pax_size;
tar_header_t hdr;
- int ret;
+ int ret, version;
memset(out, 0, sizeof(*out));
@@ -744,11 +462,3 @@ fail:
clear_header(out);
return -1;
}
-
-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));
-}