summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/tar.h7
-rw-r--r--lib/Makemodule.am4
-rw-r--r--lib/tar/checksum.c26
-rw-r--r--lib/tar/cleanup.c21
-rw-r--r--lib/tar/internal.h55
-rw-r--r--lib/tar/number.c94
-rw-r--r--lib/tar/read_header.c366
-rw-r--r--lib/tar/read_sparse_map.c47
-rw-r--r--lib/tar/read_sparse_map_old.c89
-rw-r--r--lib/tar/write_header.c30
10 files changed, 374 insertions, 365 deletions
diff --git a/include/tar.h b/include/tar.h
index 513e5bc..ef4fdec 100644
--- a/include/tar.h
+++ b/include/tar.h
@@ -8,13 +8,6 @@
#include "util.h"
-typedef enum {
- ETV_UNKNOWN = 0,
- ETV_V7_UNIX,
- ETV_PRE_POSIX,
- ETV_POSIX,
-} E_TAR_VERSION;
-
typedef struct {
char name[100];
char mode[8];
diff --git a/lib/Makemodule.am b/lib/Makemodule.am
index a79b9c3..6418568 100644
--- a/lib/Makemodule.am
+++ b/lib/Makemodule.am
@@ -8,7 +8,9 @@ libfstree_a_CFLAGS = $(AM_CFLAGS)
libfstree_a_CPPFLAGS = $(AM_CPPFLAGS)
libtar_a_SOURCES = lib/tar/read_header.c lib/tar/write_header.c lib/tar/skip.c
-libtar_a_SOURCES += include/tar.h
+libtar_a_SOURCES += lib/tar/number.c lib/tar/checksum.c lib/tar/cleanup.c
+libtar_a_SOURCES += lib/tar/read_sparse_map.c lib/tar/read_sparse_map_old.c
+libtar_a_SOURCES += lib/tar/internal.h include/tar.h
libtar_a_CFLAGS = $(AM_CFLAGS)
libtar_a_CPPFLAGS = $(AM_CPPFLAGS)
diff --git a/lib/tar/checksum.c b/lib/tar/checksum.c
new file mode 100644
index 0000000..925f942
--- /dev/null
+++ b/lib/tar/checksum.c
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+#include "internal.h"
+
+void update_checksum(tar_header_t *hdr)
+{
+ unsigned int chksum = 0;
+ size_t i;
+
+ memset(hdr->chksum, ' ', sizeof(hdr->chksum));
+
+ for (i = 0; i < sizeof(*hdr); ++i)
+ chksum += ((unsigned char *)hdr)[i];
+
+ write_octal(hdr->chksum, chksum, 6);
+ hdr->chksum[6] = '\0';
+ hdr->chksum[7] = ' ';
+}
+
+bool is_checksum_valid(const tar_header_t *hdr)
+{
+ tar_header_t copy;
+
+ memcpy(&copy, hdr, sizeof(*hdr));
+ update_checksum(&copy);
+ return memcmp(hdr, &copy, sizeof(*hdr)) == 0;
+}
diff --git a/lib/tar/cleanup.c b/lib/tar/cleanup.c
new file mode 100644
index 0000000..c4d1734
--- /dev/null
+++ b/lib/tar/cleanup.c
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+#include "internal.h"
+
+void free_sparse_list(sparse_map_t *sparse)
+{
+ sparse_map_t *old;
+
+ while (sparse != NULL) {
+ old = sparse;
+ sparse = sparse->next;
+ free(old);
+ }
+}
+
+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));
+}
diff --git a/lib/tar/internal.h b/lib/tar/internal.h
new file mode 100644
index 0000000..6596987
--- /dev/null
+++ b/lib/tar/internal.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+#ifndef INTERNAL_H
+#define INTERNAL_H
+
+#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,
+};
+
+enum {
+ ETV_UNKNOWN = 0,
+ ETV_V7_UNIX,
+ ETV_PRE_POSIX,
+ ETV_POSIX,
+};
+
+int read_octal(const char *str, int digits, uint64_t *out);
+
+int read_binary(const char *str, int digits, uint64_t *out);
+
+int read_number(const char *str, int digits, uint64_t *out);
+
+void write_octal(char *dst, unsigned int value, int digits);
+
+int pax_read_decimal(const char *str, uint64_t *out);
+
+void update_checksum(tar_header_t *hdr);
+
+bool is_checksum_valid(const tar_header_t *hdr);
+
+sparse_map_t *read_sparse_map(const char *line);
+
+sparse_map_t *read_gnu_old_sparse(int fd, tar_header_t *hdr);
+
+void free_sparse_list(sparse_map_t *sparse);
+
+#endif /* INTERNAL_H */
diff --git a/lib/tar/number.c b/lib/tar/number.c
new file mode 100644
index 0000000..e85866d
--- /dev/null
+++ b/lib/tar/number.c
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+#include "internal.h"
+
+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;
+}
+
+int read_binary(const char *str, int digits, uint64_t *out)
+{
+ uint64_t x, ov, result = 0;
+ 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;
+}
+
+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);
+}
+
+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;
+}
+
+void write_octal(char *dst, unsigned int value, int digits)
+{
+ char temp[64];
+
+ sprintf(temp, "%0*o ", digits, value);
+ memcpy(dst, temp, strlen(temp));
+}
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));
-}
diff --git a/lib/tar/read_sparse_map.c b/lib/tar/read_sparse_map.c
new file mode 100644
index 0000000..77876f5
--- /dev/null
+++ b/lib/tar/read_sparse_map.c
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+#include "internal.h"
+
+sparse_map_t *read_sparse_map(const char *line)
+{
+ 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;
+
+ if (pax_read_decimal(line, &ent->count))
+ goto fail_format;
+
+ while (isdigit(*line))
+ ++line;
+
+ if (last == NULL) {
+ list = last = ent;
+ } else {
+ last->next = ent;
+ last = ent;
+ }
+ } while (*(line++) == ',');
+
+ return list;
+fail_errno:
+ perror("parsing GNU pax sparse file record");
+ goto fail;
+fail_format:
+ fputs("malformed GNU pax sparse file record\n", stderr);
+ goto fail;
+fail:
+ free_sparse_list(list);
+ free(ent);
+ return NULL;
+}
diff --git a/lib/tar/read_sparse_map_old.c b/lib/tar/read_sparse_map_old.c
new file mode 100644
index 0000000..c0b2d0d
--- /dev/null
+++ b/lib/tar/read_sparse_map_old.c
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+#include "internal.h"
+
+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;
+}
diff --git a/lib/tar/write_header.c b/lib/tar/write_header.c
index 0670523..b84324b 100644
--- a/lib/tar/write_header.c
+++ b/lib/tar/write_header.c
@@ -1,22 +1,9 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
-#include "util.h"
-#include "tar.h"
-
-#include <sys/sysmacros.h>
-#include <string.h>
-#include <stdio.h>
+#include "internal.h"
static unsigned long pax_hdr_counter = 0;
static char buffer[4096];
-static void write_octal(char *dst, unsigned int value, int digits)
-{
- char temp[64];
-
- sprintf(temp, "%0*o ", digits, value);
- memcpy(dst, temp, strlen(temp));
-}
-
static int name_to_tar_header(tar_header_t *hdr, const char *path)
{
size_t len = strlen(path);
@@ -80,21 +67,6 @@ static void init_header(tar_header_t *hdr, const struct stat *sb,
sprintf(hdr->gname, "%u", sb->st_gid);
}
-static void update_checksum(tar_header_t *hdr)
-{
- unsigned int chksum = 0;
- size_t i;
-
- memset(hdr->chksum, ' ', sizeof(hdr->chksum));
-
- for (i = 0; i < sizeof(*hdr); ++i)
- chksum += ((unsigned char *)hdr)[i];
-
- write_octal(hdr->chksum, chksum, 6);
- hdr->chksum[6] = '\0';
- hdr->chksum[7] = ' ';
-}
-
static bool need_pax_header(const struct stat *sb, const char *name)
{
tar_header_t tmp;