From 692cbd0079658a8eb048c9b00dadec2d69d28b14 Mon Sep 17 00:00:00 2001
From: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Date: Mon, 18 May 2020 14:00:31 +0200
Subject: libtar: fix size computation of PAX line length

This commit attempts to fix the following two problems:

 - The number of digits computation returning an off-by-one result
   if the number is 10, or the resulting digit string starts
   with "10". This results in one-too-many padding bytes, corrupting
   the rest of the archive since the headers now don't start at
   multiples of 512 anymore.

 - Adding the line length prefix affects the line length (duh). If it
   grows far enough to require more digits, the result is a similar
   problem. This is a converging series that we need to compute the
   limit of.

Unit tests for this still need to be added. Or maybe I can convince a
bored undergrad student to provide an induction proof.

Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
---
 lib/tar/write_header.c | 22 +++++++++++++++++-----
 1 file changed, 17 insertions(+), 5 deletions(-)

(limited to 'lib/tar')

diff --git a/lib/tar/write_header.c b/lib/tar/write_header.c
index cd65f59..aaf9f08 100644
--- a/lib/tar/write_header.c
+++ b/lib/tar/write_header.c
@@ -116,7 +116,7 @@ static size_t num_digits(size_t num)
 {
 	size_t i = 1;
 
-	while (num > 10) {
+	while (num >= 10) {
 		num /= 10;
 		++i;
 	}
@@ -124,6 +124,18 @@ static size_t num_digits(size_t num)
 	return i;
 }
 
+static size_t prefix_digit_len(size_t len)
+{
+	size_t old_ndigit, ndigit = 0;
+
+	do {
+		old_ndigit = ndigit;
+		ndigit = num_digits(len + ndigit);
+	} while (old_ndigit != ndigit);
+
+	return ndigit;
+}
+
 static int write_schily_xattr(FILE *fp, const struct stat *orig,
 			      const char *name, const tar_xattr_t *xattr)
 {
@@ -133,9 +145,9 @@ static int write_schily_xattr(FILE *fp, const struct stat *orig,
 	struct stat sb;
 
 	for (it = xattr; it != NULL; it = it->next) {
-		len = strlen(prefix) + strlen(it->key) + it->value_len + 2;
+		len = strlen(prefix) + strlen(it->key) + it->value_len + 3;
 
-		total_size += num_digits(len) + 1 + len;
+		total_size += len + prefix_digit_len(len);
 	}
 
 	sb = *orig;
@@ -146,8 +158,8 @@ static int write_schily_xattr(FILE *fp, const struct stat *orig,
 		return -1;
 
 	for (it = xattr; it != NULL; it = it->next) {
-		len = strlen(prefix) + strlen(it->key) + it->value_len + 2;
-		len += num_digits(len) + 1;
+		len = strlen(prefix) + strlen(it->key) + it->value_len + 3;
+		len += prefix_digit_len(len);
 
 		fprintf(fp, PRI_SZ " %s%s=", len, prefix, it->key);
 		fwrite(it->value, 1, it->value_len, fp);
-- 
cgit v1.2.3