aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-07-24 00:55:03 +0200
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-07-24 01:03:42 +0200
commitd6f15cb9b054ed76b5bee2e6924d4b0b2a5e52ae (patch)
tree04bd67a5893cb4c2790a01fe14f93ca69dc97e1c
parente6c869ab1753986b032463f2e0bd5fad7bc70e0f (diff)
libtar: more lenient tar checksum verification
Until now, the tar checksum verification simply copied the header, recomputed the checksum and compared it byte-for-byte with the original. However, not all implementations store the checksum the same way. For instance, git-archive generates tar balls that use the same format as for other octal numbers. This patch makes the checksum verification more lenient by parsing the checksum from the header and comparing it with the computed value instead of copying the entire block and insisting on byte-for-byte equivalence. The result is better interoperabillity with existing tools and perhaps slightly faster processing since the block doesn't have to be copied. Reported-by: Matt Turner <mattst88@gmail.com> Suggested-by: René Scharfe <l.s.r@web.de> Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
-rw-r--r--lib/tar/checksum.c31
1 files changed, 22 insertions, 9 deletions
diff --git a/lib/tar/checksum.c b/lib/tar/checksum.c
index a2a101a..af94ab4 100644
--- a/lib/tar/checksum.c
+++ b/lib/tar/checksum.c
@@ -1,15 +1,27 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
#include "internal.h"
-void update_checksum(tar_header_t *hdr)
+static unsigned int get_checksum(const tar_header_t *hdr)
{
+ const unsigned char *header_start = (const unsigned char *)hdr;
+ const unsigned char *chksum_start = (const unsigned char *)hdr->chksum;
+ const unsigned char *header_end = header_start + sizeof(*hdr);
+ const unsigned char *chksum_end = chksum_start + sizeof(hdr->chksum);
+ const unsigned char *p;
unsigned int chksum = 0;
- size_t i;
- memset(hdr->chksum, ' ', sizeof(hdr->chksum));
+ for (p = header_start; p < chksum_start; p++)
+ chksum += *p;
+ for (; p < chksum_end; p++)
+ chksum += ' ';
+ for (; p < header_end; p++)
+ chksum += *p;
+ return chksum;
+}
- for (i = 0; i < sizeof(*hdr); ++i)
- chksum += ((unsigned char *)hdr)[i];
+void update_checksum(tar_header_t *hdr)
+{
+ unsigned int chksum = get_checksum(hdr);
sprintf(hdr->chksum, "%06o", chksum);
hdr->chksum[6] = '\0';
@@ -18,9 +30,10 @@ void update_checksum(tar_header_t *hdr)
bool is_checksum_valid(const tar_header_t *hdr)
{
- tar_header_t copy;
+ unsigned int calculated_chksum = get_checksum(hdr);
+ uint64_t read_chksum;
- memcpy(&copy, hdr, sizeof(*hdr));
- update_checksum(&copy);
- return memcmp(hdr, &copy, sizeof(*hdr)) == 0;
+ if (read_octal(hdr->chksum, sizeof(hdr->chksum), &read_chksum))
+ return 0;
+ return read_chksum == calculated_chksum;
}